@reventlessdev/reventless-aws 3.0.0-alpha.259 → 3.0.0-alpha.261
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 +19 -0
- package/package.json +9 -9
- package/src/Platform.res +79 -4
- package/src/Platform.res.mjs +24 -8
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Backend.res +77 -0
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Backend.res.mjs +87 -0
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Ops.res +42 -36
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Ops.res.mjs +24 -6
- package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +13 -1
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +87 -8
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +36 -5
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +3 -16
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +4 -19
- package/src/components/AutomationSlice_Builder.res +13 -0
- package/src/components/AutomationSlice_Builder.res.mjs +7 -2
- package/src/components/OutboundTranslationSlice_Builder.res +18 -2
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +9 -3
- package/src/plugin/runtime/PluginRuntime_Builder.res +31 -0
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +13 -0
- package/src/util/Util_DcbMetrics.res +48 -0
- package/src/util/Util_DcbMetrics.res.mjs +37 -0
- package/src/util/Util_ShellConfig.res +72 -0
- package/src/util/Util_ShellConfig.res.mjs +60 -0
- package/tests/Geocoder_AwsLocation_OpsTest.res +78 -0
- package/tests/Geocoder_AwsLocation_OpsTest.res.mjs +71 -0
- package/tests/Util_DcbMetricsTest.res +35 -0
- package/tests/Util_DcbMetricsTest.res.mjs +29 -0
- package/tests/Util_ShellConfigTest.res +78 -0
- package/tests/Util_ShellConfigTest.res.mjs +113 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
4
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as Location$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/Location.res.mjs";
|
|
5
6
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
6
7
|
import * as ClientLocation from "@aws-sdk/client-location";
|
|
7
8
|
|
|
@@ -42,7 +43,18 @@ async function handler(event) {
|
|
|
42
43
|
try {
|
|
43
44
|
let indexName = Stdlib_Option.getOr(getEnv("PLACE_INDEX_NAME"), "");
|
|
44
45
|
let q = Stdlib_Option.getOr(readQueryParam(event), "");
|
|
45
|
-
if (indexName === ""
|
|
46
|
+
if (indexName === "") {
|
|
47
|
+
console.error("Geocoder: PLACE_INDEX_NAME is unset");
|
|
48
|
+
return {
|
|
49
|
+
statusCode: 502,
|
|
50
|
+
headers: Object.fromEntries([[
|
|
51
|
+
"content-type",
|
|
52
|
+
"application/json"
|
|
53
|
+
]]),
|
|
54
|
+
body: "[]"
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (q === "") {
|
|
46
58
|
return {
|
|
47
59
|
statusCode: 200,
|
|
48
60
|
headers: Object.fromEntries([[
|
|
@@ -52,8 +64,7 @@ async function handler(event) {
|
|
|
52
64
|
body: "[]"
|
|
53
65
|
};
|
|
54
66
|
}
|
|
55
|
-
let
|
|
56
|
-
let resp = await client.send(new ClientLocation.SearchPlaceIndexForTextCommand({
|
|
67
|
+
let resp = await Location$AwsSdk.SearchPlaceIndexForTextCommand.send(new ClientLocation.SearchPlaceIndexForTextCommand({
|
|
57
68
|
IndexName: indexName,
|
|
58
69
|
Text: q,
|
|
59
70
|
MaxResults: 5
|
|
@@ -73,6 +84,7 @@ async function handler(event) {
|
|
|
73
84
|
}
|
|
74
85
|
let lng = pt[0];
|
|
75
86
|
let lat = pt[1];
|
|
87
|
+
let rel = r.Relevance;
|
|
76
88
|
return Object.fromEntries([
|
|
77
89
|
[
|
|
78
90
|
"label",
|
|
@@ -86,7 +98,10 @@ async function handler(event) {
|
|
|
86
98
|
"lng",
|
|
87
99
|
lng
|
|
88
100
|
]
|
|
89
|
-
]
|
|
101
|
+
].concat(rel !== undefined ? [[
|
|
102
|
+
"relevance",
|
|
103
|
+
rel
|
|
104
|
+
]] : []));
|
|
90
105
|
});
|
|
91
106
|
return {
|
|
92
107
|
statusCode: 200,
|
|
@@ -100,7 +115,7 @@ async function handler(event) {
|
|
|
100
115
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
101
116
|
console.error("Geocoder: search failed", exn);
|
|
102
117
|
return {
|
|
103
|
-
statusCode:
|
|
118
|
+
statusCode: 502,
|
|
104
119
|
headers: Object.fromEntries([[
|
|
105
120
|
"content-type",
|
|
106
121
|
"application/json"
|
|
@@ -110,10 +125,13 @@ async function handler(event) {
|
|
|
110
125
|
}
|
|
111
126
|
}
|
|
112
127
|
|
|
128
|
+
let Search;
|
|
129
|
+
|
|
113
130
|
export {
|
|
131
|
+
Search,
|
|
114
132
|
getEnv,
|
|
115
133
|
jsonHeaders,
|
|
116
134
|
readQueryParam,
|
|
117
135
|
handler,
|
|
118
136
|
}
|
|
119
|
-
/*
|
|
137
|
+
/* Location-AwsSdk Not a pure module */
|
|
@@ -45,7 +45,19 @@ async function buildAllHandlers() {
|
|
|
45
45
|
specModule.name,
|
|
46
46
|
automationSliceCallbackMake(specModule)(bodyModule)
|
|
47
47
|
);
|
|
48
|
-
|
|
48
|
+
// A slice can listen on several streams — its plugin's DCB log and any
|
|
49
|
+
// Aggregate EventTopic it named. Register the one handler under each, since
|
|
50
|
+
// the router dispatches by the record's own eventSourceARN. `sourceUrn` is
|
|
51
|
+
// the pre-multi-source spelling, kept as a fallback so an older handler
|
|
52
|
+
// config still routes.
|
|
53
|
+
const sourceUrns = entry.sourceUrns?.length
|
|
54
|
+
? entry.sourceUrns
|
|
55
|
+
: entry.sourceUrn
|
|
56
|
+
? [entry.sourceUrn]
|
|
57
|
+
: [];
|
|
58
|
+
for (const sourceUrn of sourceUrns) {
|
|
59
|
+
StreamOps.addToRegistry(handlers, sourceUrn, registered);
|
|
60
|
+
}
|
|
49
61
|
}));
|
|
50
62
|
|
|
51
63
|
return handlers;
|
|
@@ -24,6 +24,20 @@ type sliceInfo = {
|
|
|
24
24
|
// Threaded to phase 1 collect/resolve on the automation path (mirrors the
|
|
25
25
|
// in-process builder's `~context`); outbound slices take no context.
|
|
26
26
|
context: option<Reventless.AutomationSlice.context>,
|
|
27
|
+
// The non-DCB topics this slice subscribes to — an Aggregate's EventTopic when
|
|
28
|
+
// it names one by `Spec.name`. Empty for the common DCB-only slice.
|
|
29
|
+
//
|
|
30
|
+
// Captured *synchronously* here for the same reason the whole `bundledInfos`
|
|
31
|
+
// path exists: `forEventCollector` runs inside a `Pulumi.Output.apply`, so
|
|
32
|
+
// `storedSpecs` — which carries the resolved topics on the dead `finish` path
|
|
33
|
+
// — is still empty when `finishWithDcbEventLog` runs. The core builders
|
|
34
|
+
// compute this dict before that apply, so the value is available in time; it
|
|
35
|
+
// just had nowhere to go until now.
|
|
36
|
+
sourceTopics: ReventlessCore.EventTopic.allOutputs,
|
|
37
|
+
// Whether this slice also reads the plugin's own DCB event log. True for every
|
|
38
|
+
// slice that declares no sources (the default) and for any that names the log
|
|
39
|
+
// explicitly, which is why it is not derivable from `sourceTopics` being empty.
|
|
40
|
+
consumesDcbLog: bool,
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
let bundledInfos: dict<sliceInfo> = Dict.make()
|
|
@@ -35,6 +49,9 @@ let setDcbQueueUrl = url => dcbQueueUrlRef := Some(url)
|
|
|
35
49
|
// ApiFragmentRegistry push (2e) to dispatch RecordApiFragmentPush.
|
|
36
50
|
let getDcbQueueUrl = () => dcbQueueUrlRef.contents
|
|
37
51
|
|
|
52
|
+
// `~sourceTopics` / `~consumesDcbLog` default to the DCB-only shape every slice
|
|
53
|
+
// had before aggregate sources existed, so a caller that has not been updated
|
|
54
|
+
// keeps exactly its previous wiring.
|
|
38
55
|
let registerAutomationSlice = (
|
|
39
56
|
~name,
|
|
40
57
|
~specModulePath,
|
|
@@ -43,10 +60,21 @@ let registerAutomationSlice = (
|
|
|
43
60
|
~queryDbTableName,
|
|
44
61
|
~queryDbResources=[],
|
|
45
62
|
~context=?,
|
|
63
|
+
~sourceTopics=Dict.make(),
|
|
64
|
+
~consumesDcbLog=true,
|
|
46
65
|
) =>
|
|
47
66
|
bundledInfos->Dict.set(
|
|
48
67
|
name,
|
|
49
|
-
{
|
|
68
|
+
{
|
|
69
|
+
specModulePath,
|
|
70
|
+
bodyModulePath,
|
|
71
|
+
callbackType,
|
|
72
|
+
queryDbTableName,
|
|
73
|
+
queryDbResources,
|
|
74
|
+
context,
|
|
75
|
+
sourceTopics,
|
|
76
|
+
consumesDcbLog,
|
|
77
|
+
},
|
|
50
78
|
)
|
|
51
79
|
|
|
52
80
|
type storedSpec = {
|
|
@@ -198,6 +226,16 @@ let finish = () =>
|
|
|
198
226
|
|
|
199
227
|
let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
|
|
200
228
|
envVars->Dict.set("HANDLER_CONFIG", handlerConfigOutput->Pulumi.Output.asInput)
|
|
229
|
+
// Deploy-derived capability endpoints — a geocoder URL today. Plugin-wide
|
|
230
|
+
// rather than per-handler, which is why they sit here and not inside
|
|
231
|
+
// HANDLER_CONFIG: this is one shared Lambda and every slice on it reaches
|
|
232
|
+
// the same capability. A capability the platform did not provision arrives
|
|
233
|
+
// as "", which its client reads as "not configured" and reports as a
|
|
234
|
+
// retryable `Unavailable` — a modelled outcome rather than a missing
|
|
235
|
+
// variable that fails as a crash.
|
|
236
|
+
PluginRuntime_Builder.capabilityEnv()->Array.forEach(((name, value)) =>
|
|
237
|
+
envVars->Dict.set(name, value->Pulumi.Output.asInput)
|
|
238
|
+
)
|
|
201
239
|
|
|
202
240
|
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
203
241
|
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs",
|
|
@@ -249,9 +287,18 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
|
|
|
249
287
|
let opts = {Pulumi.ComponentResource.parent: parent}
|
|
250
288
|
let dcbOutputs: ReventlessCore.DcbEventLog.outputs =
|
|
251
289
|
dcbEventLog->ReventlessCore.Component.outputs
|
|
290
|
+
// The plugin's DCB log, plus every Aggregate EventTopic any slice on this
|
|
291
|
+
// Lambda named. One channel spans them all: `connect` turns each topic
|
|
292
|
+
// into its own event-source mapping, and the entry point routes an
|
|
293
|
+
// incoming record to the handlers registered for its stream, so a slice
|
|
294
|
+
// only ever sees the sources it asked for.
|
|
252
295
|
let eventTopics: ReventlessCore.EventTopic.allOutputs = Dict.fromArray([
|
|
253
296
|
("DcbEventLog", dcbOutputs.eventTopic),
|
|
254
297
|
])
|
|
298
|
+
bundledInfos->Dict.forEach(info =>
|
|
299
|
+
info.sourceTopics->Dict.forEachWithKey((topic, key) => eventTopics->Dict.set(key, topic))
|
|
300
|
+
)
|
|
301
|
+
|
|
255
302
|
let channel = EventCollectorChannel.make(
|
|
256
303
|
~name="AllAutomationSlices",
|
|
257
304
|
~eventTopics,
|
|
@@ -259,13 +306,29 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
|
|
|
259
306
|
~opts,
|
|
260
307
|
)
|
|
261
308
|
|
|
262
|
-
|
|
263
|
-
// dispatch key the entry point maps event-source records back to.
|
|
264
|
-
let sourceUrn = switch dcbOutputs.eventTopic.resources->Array.get(0) {
|
|
309
|
+
let dcbSourceUrn = switch dcbOutputs.eventTopic.resources->Array.get(0) {
|
|
265
310
|
| Some(resource) => resource.urn
|
|
266
311
|
| None => Pulumi.Output.make("")
|
|
267
312
|
}
|
|
268
313
|
|
|
314
|
+
// The stream URNs this one slice listens on — the dispatch keys the entry
|
|
315
|
+
// point maps event-source records back to. Per slice rather than one
|
|
316
|
+
// shared DCB URN, which is what lets an Aggregate-sourced slice exist at
|
|
317
|
+
// all: its events arrive on the Aggregate's stream and would otherwise
|
|
318
|
+
// reach a registry that has never heard of them.
|
|
319
|
+
let sourceUrnsFor = (info: sliceInfo): Pulumi.Output.t<array<string>> => {
|
|
320
|
+
let urns = []
|
|
321
|
+
if info.consumesDcbLog {
|
|
322
|
+
urns->Array.push(dcbSourceUrn)->ignore
|
|
323
|
+
}
|
|
324
|
+
info.sourceTopics
|
|
325
|
+
->Dict.valuesToArray
|
|
326
|
+
->Array.forEach(topic =>
|
|
327
|
+
topic.resources->Array.forEach(resource => urns->Array.push(resource.urn)->ignore)
|
|
328
|
+
)
|
|
329
|
+
urns->Pulumi.Output.all
|
|
330
|
+
}
|
|
331
|
+
|
|
269
332
|
let dcbQueueUrl = switch dcbQueueUrlRef.contents {
|
|
270
333
|
| Some(url) => url
|
|
271
334
|
| None => Pulumi.Output.make("NOT_AVAILABLE")
|
|
@@ -297,10 +360,16 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
|
|
|
297
360
|
}
|
|
298
361
|
|
|
299
362
|
let handlerJson =
|
|
300
|
-
Pulumi.Output.all3((info.queryDbTableName, dcbQueueUrl,
|
|
301
|
-
->Pulumi.Output.apply(((tableName, queueUrl,
|
|
302
|
-
|
|
303
|
-
|
|
363
|
+
Pulumi.Output.all3((info.queryDbTableName, dcbQueueUrl, sourceUrnsFor(info)))
|
|
364
|
+
->Pulumi.Output.apply(((tableName, queueUrl, urns)) => {
|
|
365
|
+
let urnsJson =
|
|
366
|
+
urns->Array.map(JSON.Encode.string)->JSON.Encode.array->JSON.stringify
|
|
367
|
+
// `sourceUrn` stays beside `sourceUrns` so a handler config read by
|
|
368
|
+
// an entry point that predates multi-source still routes its first
|
|
369
|
+
// stream rather than none.
|
|
370
|
+
let firstUrn = urns->Array.get(0)->Option.getOr("")
|
|
371
|
+
`{"specModule":${specModule},"bodyModule":${bodyModule},"callbackType":${callbackType},"queryDbTableName":"${tableName}","dcbQueueUrl":"${queueUrl}","sourceUrn":"${firstUrn}","sourceUrns":${urnsJson}${contextFragment}}`
|
|
372
|
+
})
|
|
304
373
|
let _ = handlerOutputs->Array.push(handlerJson)
|
|
305
374
|
})
|
|
306
375
|
|
|
@@ -310,6 +379,16 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
|
|
|
310
379
|
|
|
311
380
|
let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
|
|
312
381
|
envVars->Dict.set("HANDLER_CONFIG", handlerConfigOutput->Pulumi.Output.asInput)
|
|
382
|
+
// Deploy-derived capability endpoints — a geocoder URL today. Plugin-wide
|
|
383
|
+
// rather than per-handler, which is why they sit here and not inside
|
|
384
|
+
// HANDLER_CONFIG: this is one shared Lambda and every slice on it reaches
|
|
385
|
+
// the same capability. A capability the platform did not provision arrives
|
|
386
|
+
// as "", which its client reads as "not configured" and reports as a
|
|
387
|
+
// retryable `Unavailable` — a modelled outcome rather than a missing
|
|
388
|
+
// variable that fails as a crash.
|
|
389
|
+
PluginRuntime_Builder.capabilityEnv()->Array.forEach(((name, value)) =>
|
|
390
|
+
envVars->Dict.set(name, value->Pulumi.Output.asInput)
|
|
391
|
+
)
|
|
313
392
|
|
|
314
393
|
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
315
394
|
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs",
|
|
@@ -8,6 +8,7 @@ import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
|
8
8
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
9
9
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
10
10
|
import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
11
|
+
import * as PluginRuntime_Builder$ReventlessAws from "../../plugin/runtime/PluginRuntime_Builder.res.mjs";
|
|
11
12
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
|
|
12
13
|
import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
|
|
13
14
|
|
|
@@ -27,16 +28,20 @@ function getDcbQueueUrl() {
|
|
|
27
28
|
return dcbQueueUrlRef.contents;
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
function registerAutomationSlice(name, specModulePath, bodyModulePath, callbackTypeOpt, queryDbTableName, queryDbResourcesOpt, context) {
|
|
31
|
+
function registerAutomationSlice(name, specModulePath, bodyModulePath, callbackTypeOpt, queryDbTableName, queryDbResourcesOpt, context, sourceTopicsOpt, consumesDcbLogOpt) {
|
|
31
32
|
let callbackType = callbackTypeOpt !== undefined ? callbackTypeOpt : "automation";
|
|
32
33
|
let queryDbResources = queryDbResourcesOpt !== undefined ? queryDbResourcesOpt : [];
|
|
34
|
+
let sourceTopics = sourceTopicsOpt !== undefined ? sourceTopicsOpt : ({});
|
|
35
|
+
let consumesDcbLog = consumesDcbLogOpt !== undefined ? consumesDcbLogOpt : true;
|
|
33
36
|
bundledInfos[name] = {
|
|
34
37
|
specModulePath: specModulePath,
|
|
35
38
|
bodyModulePath: bodyModulePath,
|
|
36
39
|
callbackType: callbackType,
|
|
37
40
|
queryDbTableName: queryDbTableName,
|
|
38
41
|
queryDbResources: queryDbResources,
|
|
39
|
-
context: context
|
|
42
|
+
context: context,
|
|
43
|
+
sourceTopics: sourceTopics,
|
|
44
|
+
consumesDcbLog: consumesDcbLog
|
|
40
45
|
};
|
|
41
46
|
}
|
|
42
47
|
|
|
@@ -129,6 +134,9 @@ function finish() {
|
|
|
129
134
|
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => `{"handlers":[` + handlers.join(",") + `]}`);
|
|
130
135
|
let envVars = {};
|
|
131
136
|
envVars["HANDLER_CONFIG"] = handlerConfigOutput;
|
|
137
|
+
PluginRuntime_Builder$ReventlessAws.capabilityEnv().forEach(param => {
|
|
138
|
+
envVars[param[0]] = param[1];
|
|
139
|
+
});
|
|
132
140
|
let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs", packageDirs, undefined);
|
|
133
141
|
let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAutomationSlices", "Reactor", "AutomationSlice", match$1.code, match$1.sourceCodeHash, envVars, match[0], match[1], undefined, undefined, undefined, undefined, undefined, opts);
|
|
134
142
|
let channelSpecs = storedSpecs.map(param => param.channelSpec);
|
|
@@ -157,9 +165,24 @@ function finishWithDcbEventLog(dcbEventLog) {
|
|
|
157
165
|
"DcbEventLog",
|
|
158
166
|
dcbOutputs.eventTopic
|
|
159
167
|
]]);
|
|
168
|
+
Stdlib_Dict.forEach(bundledInfos, info => Stdlib_Dict.forEachWithKey(info.sourceTopics, (topic, key) => {
|
|
169
|
+
eventTopics[key] = topic;
|
|
170
|
+
}));
|
|
160
171
|
let channel = EventCollectorChannel_DynamoDbStream$ReventlessAws.make("AllAutomationSlices", eventTopics, undefined, opts);
|
|
161
172
|
let resource = dcbOutputs.eventTopic.resources[0];
|
|
162
|
-
let
|
|
173
|
+
let dcbSourceUrn = resource !== undefined ? resource.urn : Pulumi.output("");
|
|
174
|
+
let sourceUrnsFor = info => {
|
|
175
|
+
let urns = [];
|
|
176
|
+
if (info.consumesDcbLog) {
|
|
177
|
+
urns.push(dcbSourceUrn);
|
|
178
|
+
}
|
|
179
|
+
Object.values(info.sourceTopics).forEach(topic => {
|
|
180
|
+
topic.resources.forEach(resource => {
|
|
181
|
+
urns.push(resource.urn);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
return Pulumi.all(urns);
|
|
185
|
+
};
|
|
163
186
|
let url = dcbQueueUrlRef.contents;
|
|
164
187
|
let dcbQueueUrl = url !== undefined ? url : Pulumi.output("NOT_AVAILABLE");
|
|
165
188
|
let handlerOutputs = [];
|
|
@@ -181,13 +204,21 @@ function finishWithDcbEventLog(dcbEventLog) {
|
|
|
181
204
|
let handlerJson = Pulumi.all([
|
|
182
205
|
info.queryDbTableName,
|
|
183
206
|
dcbQueueUrl,
|
|
184
|
-
|
|
185
|
-
]).apply(param =>
|
|
207
|
+
sourceUrnsFor(info)
|
|
208
|
+
]).apply(param => {
|
|
209
|
+
let urns = param[2];
|
|
210
|
+
let urnsJson = JSON.stringify(urns.map(prim => prim));
|
|
211
|
+
let firstUrn = Stdlib_Option.getOr(urns[0], "");
|
|
212
|
+
return `{"specModule":` + specModule + `,"bodyModule":` + bodyModule + `,"callbackType":` + callbackType + `,"queryDbTableName":"` + param[0] + `","dcbQueueUrl":"` + param[1] + `","sourceUrn":"` + firstUrn + `","sourceUrns":` + urnsJson + contextFragment + `}`;
|
|
213
|
+
});
|
|
186
214
|
handlerOutputs.push(handlerJson);
|
|
187
215
|
});
|
|
188
216
|
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => `{"handlers":[` + handlers.join(",") + `]}`);
|
|
189
217
|
let envVars = {};
|
|
190
218
|
envVars["HANDLER_CONFIG"] = handlerConfigOutput;
|
|
219
|
+
PluginRuntime_Builder$ReventlessAws.capabilityEnv().forEach(param => {
|
|
220
|
+
envVars[param[0]] = param[1];
|
|
221
|
+
});
|
|
191
222
|
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AutomationSliceEntryPoint.mjs", packageDirs, undefined);
|
|
192
223
|
let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAutomationSlices", "Reactor", "AutomationSlice", match.code, match.sourceCodeHash, envVars, 1024, 30, undefined, undefined, undefined, undefined, undefined, opts);
|
|
193
224
|
EventCollectorChannel_DynamoDbStream$ReventlessAws.connect("AllAutomationSlices", [{
|
|
@@ -331,26 +331,13 @@ let makeFromCodeAsset: (
|
|
|
331
331
|
// is no race against Lambda's lazy auto-created group. Namespace/dimension
|
|
332
332
|
// are CloudWatch-specific and live here, not in core (provider-neutral).
|
|
333
333
|
if dcbMetrics {
|
|
334
|
-
|
|
335
|
-
"AppendRetry",
|
|
336
|
-
"AppendConflict",
|
|
337
|
-
"DcbDecisionModelCacheHit",
|
|
338
|
-
"DcbDecisionModelCacheMiss",
|
|
339
|
-
"DcbDecisionModelDeltaEventCount",
|
|
340
|
-
]->Array.forEach(metricName => {
|
|
334
|
+
Util_DcbMetrics.metricNames->Array.forEach(metricName => {
|
|
341
335
|
let _ = Cloudwatch.LogMetricFilter.make(
|
|
342
336
|
~name=`${name}${metricName}Filter`,
|
|
343
337
|
~args={
|
|
344
|
-
pattern:
|
|
338
|
+
pattern: Util_DcbMetrics.patternFor(metricName)->Pulumi.Input.make,
|
|
345
339
|
logGroupName: logGroup.name->Pulumi.Output.asInput,
|
|
346
|
-
metricTransformation: (
|
|
347
|
-
name: metricName->Pulumi.Input.make,
|
|
348
|
-
namespace: "Reventless/DCB"->Pulumi.Input.make,
|
|
349
|
-
value: "$.value"->Pulumi.Input.make,
|
|
350
|
-
defaultValue: "0"->Pulumi.Input.make,
|
|
351
|
-
unit: "Count"->Pulumi.Input.make,
|
|
352
|
-
dimensions: Dict.fromArray([("slice", "$.slice")])->Pulumi.Input.make,
|
|
353
|
-
}: Cloudwatch.LogMetricFilter.metricTransformation)->Pulumi.Input.make,
|
|
340
|
+
metricTransformation: Util_DcbMetrics.transformationFor(metricName)->Pulumi.Input.make,
|
|
354
341
|
},
|
|
355
342
|
~opts?,
|
|
356
343
|
)
|
|
@@ -20,6 +20,7 @@ import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
|
20
20
|
import * as Util_Lambda$ReventlessAws from "../../util/Util_Lambda.res.mjs";
|
|
21
21
|
import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
|
|
22
22
|
import * as Util_IAM_Role$ReventlessAws from "../../util/Util_IAM_Role.res.mjs";
|
|
23
|
+
import * as Util_DcbMetrics$ReventlessAws from "../../util/Util_DcbMetrics.res.mjs";
|
|
23
24
|
import * as Util_LocalConfig$ReventlessAws from "../../util/Util_LocalConfig.res.mjs";
|
|
24
25
|
import * as Util_HostUiDomain$ReventlessAws from "../../util/Util_HostUiDomain.res.mjs";
|
|
25
26
|
import * as Util_LogRetention$ReventlessAws from "../../util/Util_LogRetention.res.mjs";
|
|
@@ -136,27 +137,11 @@ function makeFromCodeAsset(name, unitKind, componentKind, code, sourceCodeHash,
|
|
|
136
137
|
tags: tagsFor(name + `LogGroup`, "Logs")
|
|
137
138
|
}, opts$1 !== undefined ? Primitive_option.valFromOption(opts$1) : undefined);
|
|
138
139
|
if (dcbMetrics) {
|
|
139
|
-
|
|
140
|
-
"AppendRetry",
|
|
141
|
-
"AppendConflict",
|
|
142
|
-
"DcbDecisionModelCacheHit",
|
|
143
|
-
"DcbDecisionModelCacheMiss",
|
|
144
|
-
"DcbDecisionModelDeltaEventCount"
|
|
145
|
-
].forEach(metricName => {
|
|
140
|
+
Util_DcbMetrics$ReventlessAws.metricNames.forEach(metricName => {
|
|
146
141
|
new (Aws.cloudwatch.LogMetricFilter)(name + metricName + `Filter`, {
|
|
147
|
-
pattern:
|
|
142
|
+
pattern: Util_DcbMetrics$ReventlessAws.patternFor(metricName),
|
|
148
143
|
logGroupName: logGroup.name,
|
|
149
|
-
metricTransformation:
|
|
150
|
-
name: metricName,
|
|
151
|
-
namespace: "Reventless/DCB",
|
|
152
|
-
value: "$.value",
|
|
153
|
-
defaultValue: "0",
|
|
154
|
-
unit: "Count",
|
|
155
|
-
dimensions: Object.fromEntries([[
|
|
156
|
-
"slice",
|
|
157
|
-
"$.slice"
|
|
158
|
-
]])
|
|
159
|
-
}
|
|
144
|
+
metricTransformation: Util_DcbMetrics$ReventlessAws.transformationFor(metricName)
|
|
160
145
|
}, opts$1 !== undefined ? Primitive_option.valFromOption(opts$1) : undefined);
|
|
161
146
|
});
|
|
162
147
|
return;
|
|
@@ -37,6 +37,17 @@ module Make = (Api: {
|
|
|
37
37
|
let tableResource = queryDbOutputs.resources->Array.getUnsafe(0)
|
|
38
38
|
let queryDbTableName = tableResource.name
|
|
39
39
|
|
|
40
|
+
// An automation names its sources through its Mappings rather than a flat
|
|
41
|
+
// list, but the runtime question is the same one the outbound builder
|
|
42
|
+
// answers: which streams must this Lambda listen on. Unlike outbound, an
|
|
43
|
+
// automation's DCB log is already a key in `allEventTopics`, so the filter
|
|
44
|
+
// picks it up and nothing extra has to be assumed.
|
|
45
|
+
let sourceTopics =
|
|
46
|
+
allEventTopics->ReventlessCore.EventTopic.filter(sourceNames->Belt.Set.String.fromArray)
|
|
47
|
+
let consumesDcbLog =
|
|
48
|
+
sourceNames->Array.length == 0 ||
|
|
49
|
+
sourceNames->Array.some(name => !(allEventTopics->Dict.has(name)))
|
|
50
|
+
|
|
40
51
|
EventCollectorRuntimeBuilder.registerAutomationSlice(
|
|
41
52
|
~name=Spec.name,
|
|
42
53
|
~specModulePath=Util_Bundle.getModuleSpecifier(Spec.moduleUrl),
|
|
@@ -45,6 +56,8 @@ module Make = (Api: {
|
|
|
45
56
|
~queryDbTableName,
|
|
46
57
|
~queryDbResources=queryDbOutputs.resources,
|
|
47
58
|
~context,
|
|
59
|
+
~sourceTopics,
|
|
60
|
+
~consumesDcbLog,
|
|
48
61
|
)
|
|
49
62
|
|
|
50
63
|
as_
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Belt_SetString from "@rescript/runtime/lib/es6/Belt_SetString.js";
|
|
3
4
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
5
|
+
import * as EventTopic$ReventlessCore from "@reventlessdev/reventless-core/src/components/EventTopic/EventTopic.res.mjs";
|
|
4
6
|
import * as Util_Bundle$ReventlessAws from "../util/Util_Bundle.res.mjs";
|
|
5
7
|
import * as QueryDbStorage_DynamoDb$ReventlessAws from "../adapter/QueryDb/QueryDbStorage_DynamoDb.res.mjs";
|
|
6
8
|
import * as AutomationSlice_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/AutomationSlice/AutomationSlice_Builder.res.mjs";
|
|
@@ -36,19 +38,22 @@ function Make(Api) {
|
|
|
36
38
|
})(Api);
|
|
37
39
|
let Make$1 = Spec => (Automation => {
|
|
38
40
|
let InnerMake = Inner.Make(Spec)(Automation);
|
|
41
|
+
let sourceNames = InnerMake.sourceNames;
|
|
39
42
|
let make = (allEventTopics, publishJsons, context, runtime, opts) => {
|
|
40
43
|
let as_ = InnerMake.make(allEventTopics, publishJsons, context, runtime, opts);
|
|
41
44
|
let queryDbOutputs = Component$ReventlessCore.outputs(as_).queryDb;
|
|
42
45
|
let tableResource = queryDbOutputs.resources[0];
|
|
43
46
|
let queryDbTableName = tableResource.name;
|
|
44
|
-
|
|
47
|
+
let sourceTopics = EventTopic$ReventlessCore.filter(allEventTopics, Belt_SetString.fromArray(sourceNames));
|
|
48
|
+
let consumesDcbLog = sourceNames.length === 0 || sourceNames.some(name => !(name in allEventTopics));
|
|
49
|
+
AutomationSliceRuntime_Builder_Single$ReventlessAws.registerAutomationSlice(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Automation.moduleUrl), "automation", queryDbTableName, queryDbOutputs.resources, context, sourceTopics, consumesDcbLog);
|
|
45
50
|
return as_;
|
|
46
51
|
};
|
|
47
52
|
return {
|
|
48
53
|
Spec: Spec,
|
|
49
54
|
Automation: Automation,
|
|
50
55
|
queryDbName: InnerMake.queryDbName,
|
|
51
|
-
sourceNames:
|
|
56
|
+
sourceNames: sourceNames,
|
|
52
57
|
make: make
|
|
53
58
|
};
|
|
54
59
|
});
|
|
@@ -38,13 +38,27 @@ module Make = (Api: {
|
|
|
38
38
|
type component = InnerMake.component
|
|
39
39
|
let queryDbName = InnerMake.queryDbName
|
|
40
40
|
|
|
41
|
-
let make = (~dcbEventLog, ~publishJsons, ~runtime=?, ~opts=?): component => {
|
|
42
|
-
let ots = InnerMake.make(~dcbEventLog, ~publishJsons, ~runtime?, ~opts?)
|
|
41
|
+
let make = (~dcbEventLog, ~allEventTopics=Dict.make(), ~publishJsons, ~runtime=?, ~opts=?): component => {
|
|
42
|
+
let ots = InnerMake.make(~dcbEventLog, ~allEventTopics, ~publishJsons, ~runtime?, ~opts?)
|
|
43
43
|
|
|
44
44
|
let queryDbOutputs = (ots->ReventlessCore.Component.outputs).queryDb
|
|
45
45
|
let tableResource = queryDbOutputs.resources->Array.getUnsafe(0)
|
|
46
46
|
let queryDbTableName = tableResource.name
|
|
47
47
|
|
|
48
|
+
// Which streams this slice's Lambda must actually listen on. The core
|
|
49
|
+
// `make` above has already validated every declared name and thrown on a
|
|
50
|
+
// typo, so this only has to select — a name absent from `allEventTopics`
|
|
51
|
+
// is necessarily the plugin's own DCB log, the one source that dict does
|
|
52
|
+
// not carry.
|
|
53
|
+
let declaredSources = Spec.sourceNames
|
|
54
|
+
let sourceTopics =
|
|
55
|
+
allEventTopics->ReventlessCore.EventTopic.filter(
|
|
56
|
+
declaredSources->Belt.Set.String.fromArray,
|
|
57
|
+
)
|
|
58
|
+
let consumesDcbLog =
|
|
59
|
+
declaredSources->Array.length == 0 ||
|
|
60
|
+
declaredSources->Array.some(name => !(allEventTopics->Dict.has(name)))
|
|
61
|
+
|
|
48
62
|
AutomationSliceRuntime_Builder_Single.registerAutomationSlice(
|
|
49
63
|
~name=Spec.name,
|
|
50
64
|
~specModulePath=Util_Bundle.getModuleSpecifier(Spec.moduleUrl),
|
|
@@ -52,6 +66,8 @@ module Make = (Api: {
|
|
|
52
66
|
~callbackType="outbound",
|
|
53
67
|
~queryDbTableName,
|
|
54
68
|
~queryDbResources=queryDbOutputs.resources,
|
|
69
|
+
~sourceTopics,
|
|
70
|
+
~consumesDcbLog,
|
|
55
71
|
)
|
|
56
72
|
|
|
57
73
|
ots
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Belt_SetString from "@rescript/runtime/lib/es6/Belt_SetString.js";
|
|
3
4
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
5
|
+
import * as EventTopic$ReventlessCore from "@reventlessdev/reventless-core/src/components/EventTopic/EventTopic.res.mjs";
|
|
4
6
|
import * as Util_Bundle$ReventlessAws from "../util/Util_Bundle.res.mjs";
|
|
5
7
|
import * as QueryDbStorage_DynamoDb$ReventlessAws from "../adapter/QueryDb/QueryDbStorage_DynamoDb.res.mjs";
|
|
6
8
|
import * as QueryDbResolvers_AppSync$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs";
|
|
@@ -43,12 +45,16 @@ function Make(Api) {
|
|
|
43
45
|
})(Api);
|
|
44
46
|
let Make$1 = Spec => (Translation => {
|
|
45
47
|
let InnerMake = Inner.Make(Spec)(Translation);
|
|
46
|
-
let make = (dcbEventLog, publishJsons, runtime, opts) => {
|
|
47
|
-
let
|
|
48
|
+
let make = (dcbEventLog, allEventTopicsOpt, publishJsons, runtime, opts) => {
|
|
49
|
+
let allEventTopics = allEventTopicsOpt !== undefined ? allEventTopicsOpt : ({});
|
|
50
|
+
let ots = InnerMake.make(dcbEventLog, allEventTopics, publishJsons, runtime, opts);
|
|
48
51
|
let queryDbOutputs = Component$ReventlessCore.outputs(ots).queryDb;
|
|
49
52
|
let tableResource = queryDbOutputs.resources[0];
|
|
50
53
|
let queryDbTableName = tableResource.name;
|
|
51
|
-
|
|
54
|
+
let declaredSources = Spec.sourceNames;
|
|
55
|
+
let sourceTopics = EventTopic$ReventlessCore.filter(allEventTopics, Belt_SetString.fromArray(declaredSources));
|
|
56
|
+
let consumesDcbLog = declaredSources.length === 0 || declaredSources.some(name => !(name in allEventTopics));
|
|
57
|
+
AutomationSliceRuntime_Builder_Single$ReventlessAws.registerAutomationSlice(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Translation.moduleUrl), "outbound", queryDbTableName, queryDbOutputs.resources, undefined, sourceTopics, consumesDcbLog);
|
|
52
58
|
return ots;
|
|
53
59
|
};
|
|
54
60
|
return {
|
|
@@ -20,6 +20,37 @@ let configRef: ref<adminConfig> = ref({
|
|
|
20
20
|
clonerEnabled: false,
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
Deploy-derived environment for a plugin's slice Lambdas, keyed by variable name.
|
|
25
|
+
|
|
26
|
+
A capability the platform provisions — a geocoder, and the same shape whenever
|
|
27
|
+
the next one arrives — is reachable only through a value the platform computes:
|
|
28
|
+
a URL, an index name, a queue. The deployer never types it, which is what keeps
|
|
29
|
+
it out of `commandHandlerConfig` (memory, timeout, the knobs a human chooses)
|
|
30
|
+
and out of the hand-edited config files this framework exists to remove.
|
|
31
|
+
|
|
32
|
+
Kept apart from `configRef` because the two are filled at different moments and
|
|
33
|
+
by different halves: `registerConfig` runs inside `deployPlatform` and replaces
|
|
34
|
+
its record wholesale, while these values are only knowable in `deployPlugin`,
|
|
35
|
+
where a plugin stack reads them off the platform's stack reference.
|
|
36
|
+
|
|
37
|
+
Keyed by the variable name rather than by capability so the runtime builders
|
|
38
|
+
merge a dict they need no knowledge of. Naming a capability's variable is then
|
|
39
|
+
a single decision at the single call site that knows which capability it is —
|
|
40
|
+
not a table two modules have to agree on.
|
|
41
|
+
*/
|
|
42
|
+
let capabilityEnvRef: dict<Pulumi.Output.t<string>> = Dict.make()
|
|
43
|
+
|
|
44
|
+
/** Registers one deploy-derived variable. Must be called before the plugin
|
|
45
|
+
builds — the slice runtimes read the dict when their finalizer runs. */
|
|
46
|
+
let registerCapabilityEnv = (name: string, value: Pulumi.Output.t<string>) =>
|
|
47
|
+
capabilityEnvRef->Dict.set(name, value)
|
|
48
|
+
|
|
49
|
+
/** The registered variables, for a runtime builder to merge into its Lambda's
|
|
50
|
+
environment. Empty is the ordinary answer for a platform that provisions no
|
|
51
|
+
capability, so this returns `[]` rather than throwing. */
|
|
52
|
+
let capabilityEnv = () => capabilityEnvRef->Dict.toArray
|
|
53
|
+
|
|
23
54
|
type sliceModulePaths = {
|
|
24
55
|
specPath: string,
|
|
25
56
|
behaviorPath: string,
|
|
@@ -34,6 +34,16 @@ let configRef = {
|
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
let capabilityEnvRef = {};
|
|
38
|
+
|
|
39
|
+
function registerCapabilityEnv(name, value) {
|
|
40
|
+
capabilityEnvRef[name] = value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function capabilityEnv() {
|
|
44
|
+
return Object.entries(capabilityEnvRef);
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
let dcbConfigRef = {
|
|
38
48
|
contents: {
|
|
39
49
|
pluginName: "",
|
|
@@ -493,6 +503,9 @@ function Make(EventCollectorChannel) {
|
|
|
493
503
|
export {
|
|
494
504
|
log,
|
|
495
505
|
configRef,
|
|
506
|
+
capabilityEnvRef,
|
|
507
|
+
registerCapabilityEnv,
|
|
508
|
+
capabilityEnv,
|
|
496
509
|
dcbConfigRef,
|
|
497
510
|
registeredSliceModulePaths,
|
|
498
511
|
registerStateChangeSliceSpec,
|