@reventlessdev/reventless-aws 3.0.0-alpha.220 → 3.0.0-alpha.221
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 +11 -0
- package/package.json +8 -8
- package/src/Platform.res.mjs +4 -0
- package/src/Platform_Stack.res +6 -0
- package/src/Platform_Stack.res.mjs +3 -1
- package/src/adapter/AWS_Tags.res +127 -12
- package/src/adapter/AWS_Tags.res.mjs +58 -17
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +13 -1
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +3 -4
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +13 -1
- package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +3 -4
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res +12 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res.mjs +4 -2
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res +6 -0
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res.mjs +2 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +2 -2
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res.mjs +2 -2
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +2 -2
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res.mjs +2 -2
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +2 -2
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res.mjs +2 -2
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +2 -2
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs +2 -2
- package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +1 -0
- package/src/adapter/Counter/CounterHandler_DynamoDbStream.res.mjs +1 -1
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res +1 -1
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res.mjs +1 -2
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +1 -1
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs +1 -1
- package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +2 -2
- package/src/adapter/EventCollector/EventCollectorChannel_SQS.res.mjs +2 -2
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +2 -2
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res.mjs +2 -2
- package/src/adapter/EventLog/EventLogStorage.res +3 -3
- package/src/adapter/EventLog/EventLogStorage.res.mjs +3 -3
- package/src/adapter/EventLog/EventLogStorage_DynamoDb.res +2 -2
- package/src/adapter/EventLog/EventLogStorage_DynamoDb.res.mjs +2 -2
- package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res +2 -2
- package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res.mjs +2 -2
- package/src/adapter/EventLog/EventLogStorage_Postgres.res +1 -1
- package/src/adapter/EventLog/EventLogStorage_Postgres.res.mjs +1 -1
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +7 -1
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +2 -2
- package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res +1 -1
- package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res.mjs +1 -1
- package/src/adapter/EventTopic/EventTopicPublisher_SNS.res +2 -2
- package/src/adapter/EventTopic/EventTopicPublisher_SNS.res.mjs +2 -2
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res +2 -2
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res.mjs +2 -2
- package/src/adapter/Mcp/MCP_Lambda.res.mjs +1 -1
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +7 -0
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +4 -2
- package/src/adapter/Postgres/PgMigration_Builder.res +1 -0
- package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
- package/src/adapter/QueryDb/PgQueryResolver_Builder.res +7 -0
- package/src/adapter/QueryDb/PgQueryResolver_Builder.res.mjs +4 -2
- package/src/adapter/QueryDb/QueryDbStorage.res +6 -6
- package/src/adapter/QueryDb/QueryDbStorage.res.mjs +6 -6
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res +2 -2
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res.mjs +2 -2
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res +2 -2
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res.mjs +2 -2
- package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +1 -1
- package/src/adapter/QueryDb/QueryDbStorage_Postgres.res.mjs +1 -1
- package/src/adapter/Runtime/AdminEventCollectorEntryPoint.mjs +24 -41
- package/src/adapter/Runtime/AggregateEntryPoint.mjs +52 -45
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +19 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res.mjs +8 -5
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +19 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res.mjs +8 -5
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +9 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res.mjs +4 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res +9 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res.mjs +4 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +9 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +4 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +9 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +4 -2
- package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +36 -22
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +1 -0
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +1 -1
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint.mjs +39 -42
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +7 -1
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res.mjs +4 -2
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +19 -1
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +5 -2
- package/src/adapter/Runtime/EventMapperEntryPoint.mjs +25 -23
- package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +25 -41
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +1 -0
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res.mjs +1 -1
- package/src/adapter/Runtime/HandlerFactoryHelpers.mjs +126 -0
- package/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs +24 -41
- package/src/adapter/Runtime/ReadModelEntryPoint.mjs +33 -24
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +52 -2
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +25 -7
- package/src/adapter/Runtime/SideEffectEntryPoint.mjs +25 -24
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +9 -1
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs +4 -2
- package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +1 -0
- package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs +1 -1
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +26 -21
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +2 -1
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +2 -2
- package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +1 -0
- package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res.mjs +1 -1
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res +12 -0
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res.mjs +4 -2
- package/src/adapter/StateTopic/StateTopic_AppSync.res +7 -1
- package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +2 -2
- package/src/adapter/Task/TaskBucket_S3.res +1 -0
- package/src/adapter/Task/TaskBucket_S3.res.mjs +7 -1
- package/src/components/Aggregate_Builder_Micro.res.mjs +2 -0
- package/src/components/Aggregate_Builder_NoResolver.res.mjs +2 -0
- package/src/components/Aggregate_Builder_PerAggregate.res.mjs +2 -0
- package/src/components/Aggregate_Builder_Single.res.mjs +2 -0
- package/src/components/Aggregate_Builder_Single_Async.res.mjs +2 -0
- package/src/components/Api/AppSync_Adapter.res +12 -0
- package/src/components/Api/AppSync_Adapter.res.mjs +6 -2
- package/src/components/Api/AppSync_MergedApi.res +12 -0
- package/src/components/Api/AppSync_MergedApi.res.mjs +5 -2
- package/src/components/AutomationSlice_Builder.res.mjs +2 -0
- package/src/components/ExtensionPoint_Builder.res.mjs +2 -0
- package/src/components/ForeignReadModel_Builder.res.mjs +4 -0
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +2 -0
- package/src/components/Plugin.res.mjs +2 -0
- package/src/components/ReadModel_Builder_NoResolver.res.mjs +2 -0
- package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +2 -0
- package/src/components/ReadModel_Builder_PerReadModel.res.mjs +2 -0
- package/src/components/ReadModel_Builder_Single.res.mjs +2 -0
- package/src/components/ReadModel_Builder_Single_Stream.res.mjs +2 -0
- package/src/components/SideEffectHandler_PerSideEffectHandler.res.mjs +6 -0
- package/src/components/SideEffectHandler_Single.res.mjs +4 -0
- package/src/components/StateViewSlice_Builder.res.mjs +2 -0
- package/src/components/StateViewSlice_Builder_Stream.res.mjs +2 -0
- package/src/components/Task_Builder_PerBucket.res.mjs +4 -0
- package/src/components/Vpc.res +10 -1
- package/src/components/Vpc.res.mjs +3 -1
- package/src/plugin/cloner/ClonerRunner_Fargate.res +26 -1
- package/src/plugin/cloner/ClonerRunner_Fargate.res.mjs +9 -6
- package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +6 -0
- package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res.mjs +4 -1
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +1 -0
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs +1 -1
- package/src/plugin/runtime/PluginRuntime_Builder.res +2 -0
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +2 -2
- package/src/plugin/stack/Plugin_ExtensionPoint_Builder.res.mjs +4 -0
- package/src/plugin/stack/Plugin_Stack.res +26 -1
- package/src/plugin/stack/Plugin_Stack.res.mjs +8 -3
- package/src/util/Util_CognitoGroupUser.res +6 -0
- package/src/util/Util_CognitoGroupUser.res.mjs +3 -1
- package/src/util/Util_DeadLetterQueue.res +8 -2
- package/src/util/Util_DeadLetterQueue.res.mjs +3 -4
- package/src/util/Util_LogAttribution.res +31 -0
- package/src/util/Util_LogAttribution.res.mjs +27 -0
- package/tests/AWS_TagsTest.res +236 -0
- package/tests/AWS_TagsTest.res.mjs +153 -0
- package/tests/HandlerFactoryDispatchTest.res +190 -0
- package/tests/HandlerFactoryDispatchTest.res.mjs +135 -0
- package/tests/integration/AggregateEntryPoint_IntegrationTest.res +5 -3
- package/tests/integration/AggregateEntryPoint_IntegrationTest.res.mjs +5 -3
|
@@ -3,48 +3,22 @@
|
|
|
3
3
|
// wires ExtensionPoint_Callback.Make + CommandTopic_Callback.Make,
|
|
4
4
|
// routes SQS events through handleQueueEvent.
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
import {
|
|
7
|
+
patchSpecId,
|
|
8
|
+
makeQueueRef,
|
|
9
|
+
log,
|
|
10
|
+
runEffect,
|
|
11
|
+
setRequestId,
|
|
12
|
+
extractMetaField,
|
|
13
|
+
extractSentTimestamp,
|
|
14
|
+
extractRetryCount,
|
|
15
|
+
} from "./HandlerFactoryHelpers.mjs";
|
|
9
16
|
import { Make as extensionPointCallbackMake } from "@reventlessdev/reventless-core/src/components/ExtensionPoint/ExtensionPoint_Callback.res.mjs";
|
|
10
17
|
import { Make as commandTopicCallbackMake } from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Callback.res.mjs";
|
|
11
18
|
import { handleQueueEvent, publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
|
|
12
19
|
|
|
13
20
|
const dynamicImport = (specifier) => import('/var/task/node_modules/' + specifier);
|
|
14
21
|
|
|
15
|
-
// Set at handler entry; read by runEffect to tag logs with the Lambda request id.
|
|
16
|
-
let _currentRequestId = "unknown";
|
|
17
|
-
|
|
18
|
-
function runEffect(correlationId, effect) {
|
|
19
|
-
let e = effect
|
|
20
|
-
// Promote correlationId/requestId/plugin to top-level JSON log fields —
|
|
21
|
-
// decoded by EffectLogger.install() from Effect log annotations. Harmless
|
|
22
|
-
// no-op if the unified logger isn't installed.
|
|
23
|
-
.pipe(Effect.annotateLogs("correlationId", correlationId || "unknown"))
|
|
24
|
-
.pipe(Effect.annotateLogs("requestId", _currentRequestId));
|
|
25
|
-
if (pluginName) e = e.pipe(Effect.annotateLogs("plugin", pluginName));
|
|
26
|
-
return e
|
|
27
|
-
.pipe(Effect.provideService(requestContextTag, { correlationId: correlationId || "unknown" }))
|
|
28
|
-
.pipe(Effect.runPromise);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function extractCorrelationId(records) {
|
|
32
|
-
const first = records[0];
|
|
33
|
-
if (!first) return undefined;
|
|
34
|
-
const body = first.body;
|
|
35
|
-
if (!body) return undefined;
|
|
36
|
-
try {
|
|
37
|
-
const parsed = JSON.parse(body);
|
|
38
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
39
|
-
const meta = parsed.meta;
|
|
40
|
-
if (meta && typeof meta === "object" && typeof meta.correlationId === "string") {
|
|
41
|
-
return meta.correlationId;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
} catch (_) {}
|
|
45
|
-
return undefined;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
22
|
async function buildHandler() {
|
|
49
23
|
const configStr = process.env["HANDLER_CONFIG"] || "{}";
|
|
50
24
|
const config = JSON.parse(configStr);
|
|
@@ -84,18 +58,28 @@ async function buildHandler() {
|
|
|
84
58
|
});
|
|
85
59
|
|
|
86
60
|
const resolvedQueue = makeQueueRef(config.queueUrl);
|
|
87
|
-
|
|
61
|
+
// Pair the handler with the extension point it serves so the invocation's log
|
|
62
|
+
// lines name the component, not just this Lambda.
|
|
63
|
+
return {
|
|
64
|
+
sqsHandler: handleQueueEvent(resolvedQueue, commandTopicCallback.handleJsonCommands),
|
|
65
|
+
comp: `ExtensionPoint(${patchedSpec.name})`,
|
|
66
|
+
};
|
|
88
67
|
}
|
|
89
68
|
|
|
90
69
|
const initPromise = buildHandler();
|
|
91
70
|
|
|
92
71
|
export async function handler(event, context) {
|
|
93
|
-
|
|
94
|
-
const sqsHandler = await initPromise;
|
|
72
|
+
setRequestId(context?.awsRequestId);
|
|
73
|
+
const { sqsHandler, comp } = await initPromise;
|
|
95
74
|
const records = event.Records || [];
|
|
96
|
-
const correlationId = extractCorrelationId(records);
|
|
97
75
|
|
|
98
76
|
log.debug("processing " + records.length.toString() + " record(s)", { comp: "ExtensionPointRuntime" });
|
|
99
|
-
await runEffect(
|
|
77
|
+
await runEffect(sqsHandler(event, context), {
|
|
78
|
+
correlationId: extractMetaField(records, "correlationId"),
|
|
79
|
+
causationId: extractMetaField(records, "causationId"),
|
|
80
|
+
comp,
|
|
81
|
+
timestamp: extractSentTimestamp(records),
|
|
82
|
+
retryCount: extractRetryCount(records),
|
|
83
|
+
});
|
|
100
84
|
return "";
|
|
101
85
|
}
|
|
@@ -39,7 +39,7 @@ function forCommandTopic(param, connect, memorySizeOpt, timeoutOpt, specModuleUr
|
|
|
39
39
|
packageDirs[specPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(specPkg);
|
|
40
40
|
packageDirs[mappingsPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(mappingsPkg);
|
|
41
41
|
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/ExtensionPointEntryPoint.mjs", packageDirs, undefined);
|
|
42
|
-
return connect(RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "CommandHandler", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts));
|
|
42
|
+
return connect(RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "CommandHandler", "ExtensionPoint", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts));
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function finish() {
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
// Shared runtime utilities for compiled Lambda entry points.
|
|
2
2
|
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
3
4
|
import { $$String as IdString } from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
5
|
+
import {
|
|
6
|
+
tag as requestContextTag,
|
|
7
|
+
fromOptions as makeRequestContext,
|
|
8
|
+
} from "@reventlessdev/reventless-core/src/RequestContext.res.mjs";
|
|
4
9
|
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
5
10
|
import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";
|
|
6
11
|
|
|
@@ -66,6 +71,127 @@ export const log = {
|
|
|
66
71
|
// Lambdas). Export so shim runEffect wrappers can annotate Effect logs with it.
|
|
67
72
|
export const pluginName = PLUGIN_NAME;
|
|
68
73
|
|
|
74
|
+
// ─── Dispatch boundary for the entry-point shells ───────────────────────────
|
|
75
|
+
// The deployed Lambda is an archive whose index.handler is one of the .mjs entry
|
|
76
|
+
// points here — the AWS runtime builders take `~handler as _` and discard the
|
|
77
|
+
// ReScript closure, so ReventlessCore.Runtime.annotateInvocation (the ReScript
|
|
78
|
+
// dispatch boundary, used by reventless-local) never runs in a Lambda. This is
|
|
79
|
+
// its counterpart: the single place where every deployed invocation gets its log
|
|
80
|
+
// annotations and its RequestContext, so `comp` / `causationId` attribution is
|
|
81
|
+
// the same on both platforms.
|
|
82
|
+
//
|
|
83
|
+
// Options object rather than positional arguments: the shells call this from
|
|
84
|
+
// twelve sites and the field set grows, so a new field must not be able to
|
|
85
|
+
// shift an existing one. Same reason RequestContext exposes `fromOptions` —
|
|
86
|
+
// ReScript labelled arguments compile to positional ones.
|
|
87
|
+
//
|
|
88
|
+
// See docs/plans/entrypoint-dispatch-parity-and-latency-fields.md.
|
|
89
|
+
|
|
90
|
+
// Set at handler entry via setRequestId; read on each dispatch to tag logs with
|
|
91
|
+
// the Lambda request id.
|
|
92
|
+
let _currentRequestId = "unknown";
|
|
93
|
+
|
|
94
|
+
export function setRequestId(id) {
|
|
95
|
+
_currentRequestId = id || "unknown";
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function runEffect(effect, opts = {}) {
|
|
99
|
+
const { correlationId, causationId, comp, timestamp, retryCount, plugin } = opts;
|
|
100
|
+
const cid = correlationId || "unknown";
|
|
101
|
+
// `plugin` per dispatch (from HANDLER_CONFIG, resolved at deploy time) beats
|
|
102
|
+
// the Lambda-name-derived fallback: a shared Lambda (AllReadModels,
|
|
103
|
+
// AllAggregatesCmdHandler) hosts components from several plugins, so its name
|
|
104
|
+
// carries no single plugin identity.
|
|
105
|
+
const resolvedPlugin = plugin || pluginName;
|
|
106
|
+
let e = effect
|
|
107
|
+
// Promote correlationId/requestId/plugin/comp/causationId to top-level JSON
|
|
108
|
+
// log fields — decoded by EffectLogger.install() from Effect log
|
|
109
|
+
// annotations. Harmless no-op if the unified logger isn't installed.
|
|
110
|
+
.pipe(Effect.annotateLogs("correlationId", cid))
|
|
111
|
+
.pipe(Effect.annotateLogs("requestId", _currentRequestId));
|
|
112
|
+
if (resolvedPlugin) e = e.pipe(Effect.annotateLogs("plugin", resolvedPlugin));
|
|
113
|
+
if (comp) e = e.pipe(Effect.annotateLogs("comp", comp));
|
|
114
|
+
if (causationId) e = e.pipe(Effect.annotateLogs("causationId", causationId));
|
|
115
|
+
// Only annotate a redelivery. A constant "1" on every line is noise, and its
|
|
116
|
+
// absence is what makes `filter retryCount > 1` a usable query.
|
|
117
|
+
if (retryCount > 1) e = e.pipe(Effect.annotateLogs("retryCount", String(retryCount)));
|
|
118
|
+
return e
|
|
119
|
+
.pipe(
|
|
120
|
+
Effect.provideService(
|
|
121
|
+
requestContextTag,
|
|
122
|
+
makeRequestContext({
|
|
123
|
+
correlationId: cid,
|
|
124
|
+
causationId,
|
|
125
|
+
component: comp,
|
|
126
|
+
pluginName: resolvedPlugin || undefined,
|
|
127
|
+
timestamp,
|
|
128
|
+
retryCount,
|
|
129
|
+
}),
|
|
130
|
+
),
|
|
131
|
+
)
|
|
132
|
+
.pipe(Effect.runPromise);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ─── Message-envelope + transport-attribute extraction ──────────────────────
|
|
136
|
+
// Mirrors RuntimeEnvironment_Lambda.extractMetaField (ReScript side, same
|
|
137
|
+
// position in the batch) so both dispatch boundaries read the same fields.
|
|
138
|
+
|
|
139
|
+
// Read one envelope meta field (correlationId, causationId, …) off the first
|
|
140
|
+
// record. Two transports, two shapes:
|
|
141
|
+
// SQS — the envelope is a JSON `body` string with a nested `meta`.
|
|
142
|
+
// DynamoDB stream — the EventLog row stores meta keys *flat* as top-level
|
|
143
|
+
// attributes (Message.composeMeta rebuilds `meta` from them),
|
|
144
|
+
// so read NewImage.<field>.S.
|
|
145
|
+
// Undefined when neither shape yields a string.
|
|
146
|
+
export function extractMetaField(records, field) {
|
|
147
|
+
const first = (records || [])[0];
|
|
148
|
+
if (!first) return undefined;
|
|
149
|
+
if (typeof first.body === "string") {
|
|
150
|
+
let parsed;
|
|
151
|
+
try {
|
|
152
|
+
parsed = JSON.parse(first.body);
|
|
153
|
+
} catch (_) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
const value = parsed?.meta?.[field];
|
|
157
|
+
return typeof value === "string" ? value : undefined;
|
|
158
|
+
}
|
|
159
|
+
const attr = first.dynamodb?.NewImage?.[field];
|
|
160
|
+
return typeof attr?.S === "string" ? attr.S : undefined;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Send time of the triggering message, ms since epoch. SQS stamps
|
|
164
|
+
// `SentTimestamp` (ms, server-side clock — not the producer's); a DynamoDB
|
|
165
|
+
// stream record carries `ApproximateCreationDateTime` in *seconds*. Undefined
|
|
166
|
+
// when neither is present, so a handler can tell "no send time" from "zero
|
|
167
|
+
// latency".
|
|
168
|
+
export function extractSentTimestamp(records) {
|
|
169
|
+
const first = (records || [])[0];
|
|
170
|
+
if (!first) return undefined;
|
|
171
|
+
const sqsSent = first.attributes?.SentTimestamp;
|
|
172
|
+
if (sqsSent !== undefined) {
|
|
173
|
+
const ms = Number(sqsSent);
|
|
174
|
+
return Number.isFinite(ms) ? ms : undefined;
|
|
175
|
+
}
|
|
176
|
+
const streamCreated = first.dynamodb?.ApproximateCreationDateTime;
|
|
177
|
+
if (streamCreated !== undefined) {
|
|
178
|
+
const secs = Number(streamCreated);
|
|
179
|
+
return Number.isFinite(secs) ? secs * 1000 : undefined;
|
|
180
|
+
}
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Delivery attempt: 1 on first delivery. SQS counts redeliveries in
|
|
185
|
+
// `ApproximateReceiveCount`; DynamoDB streams have no equivalent, so a stream
|
|
186
|
+
// record reports 1.
|
|
187
|
+
export function extractRetryCount(records) {
|
|
188
|
+
const first = (records || [])[0];
|
|
189
|
+
const raw = first?.attributes?.ApproximateReceiveCount;
|
|
190
|
+
if (raw === undefined) return 1;
|
|
191
|
+
const count = parseInt(raw, 10);
|
|
192
|
+
return Number.isFinite(count) && count > 0 ? count : 1;
|
|
193
|
+
}
|
|
194
|
+
|
|
69
195
|
// Patch a spec module's Id field to use IdString if undefined.
|
|
70
196
|
// Workaround for `module Id = Id.String` not producing an ESM export.
|
|
71
197
|
export const patchSpecId = (specModule) => ({ ...specModule, Id: specModule.Id || IdString });
|
|
@@ -2,9 +2,17 @@
|
|
|
2
2
|
// All imports are framework packages — no user modules needed.
|
|
3
3
|
// Handles Heartbeat, Cloner, and other plugin-level extension point commands.
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import {
|
|
6
|
+
patchSpecId,
|
|
7
|
+
makeQueueRef,
|
|
8
|
+
scanByTableName,
|
|
9
|
+
log,
|
|
10
|
+
runEffect,
|
|
11
|
+
setRequestId,
|
|
12
|
+
extractMetaField,
|
|
13
|
+
extractSentTimestamp,
|
|
14
|
+
extractRetryCount,
|
|
15
|
+
} from "./HandlerFactoryHelpers.mjs";
|
|
8
16
|
import { sendMessage } from "@reventlessdev/reventless-aws/src/plugin/runtime/Util_PluginMessage_Runtime.res.mjs";
|
|
9
17
|
import * as PluginExtensionPointSpec from "@reventlessdev/reventless-infra/src/types/PluginExtensionPointSpec.res.mjs";
|
|
10
18
|
import { Make as pluginEPPluginMake } from "@reventlessdev/reventless-core/src/plugin/connect/PluginExtensionPoint_Plugin.res.mjs";
|
|
@@ -13,39 +21,6 @@ import { Make as commandTopicCallbackMake } from "@reventlessdev/reventless-core
|
|
|
13
21
|
import { handleQueueEvent, publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
|
|
14
22
|
import { createSchedule as cwCreateSchedule, deleteSchedule as cwDeleteSchedule } from "@reventlessdev/reventless-aws/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res.mjs";
|
|
15
23
|
|
|
16
|
-
// Set at handler entry; read by runEffect to tag logs with the Lambda request id.
|
|
17
|
-
let _currentRequestId = "unknown";
|
|
18
|
-
|
|
19
|
-
function runEffect(correlationId, effect) {
|
|
20
|
-
let e = effect
|
|
21
|
-
// Promote correlationId/requestId/plugin to top-level JSON log fields —
|
|
22
|
-
// decoded by EffectLogger.install() from Effect log annotations. Harmless
|
|
23
|
-
// no-op if the unified logger isn't installed.
|
|
24
|
-
.pipe(Effect.annotateLogs("correlationId", correlationId || "unknown"))
|
|
25
|
-
.pipe(Effect.annotateLogs("requestId", _currentRequestId));
|
|
26
|
-
if (pluginName) e = e.pipe(Effect.annotateLogs("plugin", pluginName));
|
|
27
|
-
return e
|
|
28
|
-
.pipe(Effect.provideService(requestContextTag, { correlationId: correlationId || "unknown" }))
|
|
29
|
-
.pipe(Effect.runPromise);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function extractCorrelationId(records) {
|
|
33
|
-
const first = records[0];
|
|
34
|
-
if (!first) return undefined;
|
|
35
|
-
const body = first.body;
|
|
36
|
-
if (!body) return undefined;
|
|
37
|
-
try {
|
|
38
|
-
const parsed = JSON.parse(body);
|
|
39
|
-
if (typeof parsed === "object" && parsed !== null) {
|
|
40
|
-
const meta = parsed.meta;
|
|
41
|
-
if (meta && typeof meta === "object" && typeof meta.correlationId === "string") {
|
|
42
|
-
return meta.correlationId;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
} catch (_) {}
|
|
46
|
-
return undefined;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
24
|
function buildHandler() {
|
|
50
25
|
const configStr = process.env["HANDLER_CONFIG"] || "{}";
|
|
51
26
|
const config = JSON.parse(configStr);
|
|
@@ -121,17 +96,25 @@ function buildHandler() {
|
|
|
121
96
|
commandsHandler: callback.handleIncomingCommands,
|
|
122
97
|
});
|
|
123
98
|
|
|
124
|
-
return
|
|
99
|
+
return {
|
|
100
|
+
sqsHandler: handleQueueEvent(makeQueueRef(config.queueUrl), commandTopicCallback.handleJsonCommands),
|
|
101
|
+
comp: `ExtensionPoint(${patchedSpec.name})`,
|
|
102
|
+
};
|
|
125
103
|
}
|
|
126
104
|
|
|
127
|
-
const sqsHandler = buildHandler();
|
|
105
|
+
const { sqsHandler, comp } = buildHandler();
|
|
128
106
|
|
|
129
107
|
export async function handler(event, context) {
|
|
130
|
-
|
|
108
|
+
setRequestId(context?.awsRequestId);
|
|
131
109
|
const records = event.Records || [];
|
|
132
|
-
const correlationId = extractCorrelationId(records);
|
|
133
110
|
|
|
134
111
|
log.debug("processing " + records.length.toString() + " record(s)", { comp: "PluginExtensionPointRuntime" });
|
|
135
|
-
await runEffect(
|
|
112
|
+
await runEffect(sqsHandler(event, context), {
|
|
113
|
+
correlationId: extractMetaField(records, "correlationId"),
|
|
114
|
+
causationId: extractMetaField(records, "causationId"),
|
|
115
|
+
comp,
|
|
116
|
+
timestamp: extractSentTimestamp(records),
|
|
117
|
+
retryCount: extractRetryCount(records),
|
|
118
|
+
});
|
|
136
119
|
return "";
|
|
137
120
|
}
|
|
@@ -2,9 +2,15 @@
|
|
|
2
2
|
// At cold start: reads HANDLER_CONFIG, dynamically imports Spec/Mappings modules,
|
|
3
3
|
// wires ReadModel_Callback.Make, builds handler map keyed by source URN.
|
|
4
4
|
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import {
|
|
6
|
+
patchSpecId,
|
|
7
|
+
log,
|
|
8
|
+
runEffect,
|
|
9
|
+
setRequestId,
|
|
10
|
+
extractMetaField,
|
|
11
|
+
extractSentTimestamp,
|
|
12
|
+
extractRetryCount,
|
|
13
|
+
} from "./HandlerFactoryHelpers.mjs";
|
|
8
14
|
import { Make as readModelCallbackMake } from "@reventlessdev/reventless-core/src/components/ReadModel/ReadModel_Callback.res.mjs";
|
|
9
15
|
// Typed cold-start core — DynamoDB QueryDb ops, compiler-checked against the
|
|
10
16
|
// framework signatures (see the module header and
|
|
@@ -48,22 +54,6 @@ function fixMappingsModule(mod) {
|
|
|
48
54
|
return mod;
|
|
49
55
|
}
|
|
50
56
|
|
|
51
|
-
// Set at handler entry; read by runEffect to tag logs with the Lambda request id.
|
|
52
|
-
let _currentRequestId = "unknown";
|
|
53
|
-
|
|
54
|
-
function runEffect(correlationId, effect) {
|
|
55
|
-
let e = effect
|
|
56
|
-
// Promote correlationId/requestId/plugin to top-level JSON log fields —
|
|
57
|
-
// decoded by EffectLogger.install() from Effect log annotations. Harmless
|
|
58
|
-
// no-op if the unified logger isn't installed.
|
|
59
|
-
.pipe(Effect.annotateLogs("correlationId", correlationId || "unknown"))
|
|
60
|
-
.pipe(Effect.annotateLogs("requestId", _currentRequestId));
|
|
61
|
-
if (pluginName) e = e.pipe(Effect.annotateLogs("plugin", pluginName));
|
|
62
|
-
return e
|
|
63
|
-
.pipe(Effect.provideService(requestContextTag, { correlationId: correlationId || "unknown" }))
|
|
64
|
-
.pipe(Effect.runPromise);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
57
|
function groupBySource(records) {
|
|
68
58
|
const dict = {};
|
|
69
59
|
records.forEach(record => {
|
|
@@ -130,13 +120,20 @@ async function buildAllHandlers() {
|
|
|
130
120
|
const specModule = await dynamicImport(h.specModule);
|
|
131
121
|
const mappingsModule = await dynamicImport(h.mappingsModule);
|
|
132
122
|
const handler = buildReadModelHandler(specModule, mappingsModule, h.queryDbTableName, h.pgConnection, h.stateTopicName);
|
|
123
|
+
// `comp` / `plugin` are resolved at deploy time and baked into
|
|
124
|
+
// HANDLER_CONFIG (EventCollectorRuntime_Builder_Single) so the shell doesn't
|
|
125
|
+
// re-derive component identity from module paths, and so the string matches
|
|
126
|
+
// the ReScript dispatch boundary's `EventCollector(<Name>)` byte for byte.
|
|
127
|
+
// Kept next to the handler: this Lambda hosts every read model, so the comp
|
|
128
|
+
// is what separates their log lines.
|
|
129
|
+
const registered = { handler, comp: h.comp, plugin: h.plugin };
|
|
133
130
|
// Multiple read models can share one source stream — e.g. every admin read
|
|
134
131
|
// model (Plugins, PluginHistory, PlatformEventGraph, UIFragmentRegistry)
|
|
135
132
|
// projects the Plugin aggregate's EventLog stream. Accumulate ALL handlers
|
|
136
133
|
// per source ARN; a plain `handlers[urn] = handler` collapses them to one
|
|
137
134
|
// (whichever async builder wins the Promise.all race), silently dropping the
|
|
138
135
|
// rest — which leaves their QueryDbs empty.
|
|
139
|
-
(handlers[h.sourceUrn] ||= []).push(
|
|
136
|
+
(handlers[h.sourceUrn] ||= []).push(registered);
|
|
140
137
|
}));
|
|
141
138
|
|
|
142
139
|
return handlers;
|
|
@@ -145,7 +142,7 @@ async function buildAllHandlers() {
|
|
|
145
142
|
const initPromise = buildAllHandlers();
|
|
146
143
|
|
|
147
144
|
export async function handler(event, context) {
|
|
148
|
-
|
|
145
|
+
setRequestId(context?.awsRequestId);
|
|
149
146
|
const handlers = await initPromise;
|
|
150
147
|
const records = event.Records || [];
|
|
151
148
|
const grouped = groupBySource(records);
|
|
@@ -154,11 +151,23 @@ export async function handler(event, context) {
|
|
|
154
151
|
const streamHandlers = handlers[arn];
|
|
155
152
|
if (streamHandlers !== undefined && streamHandlers.length > 0) {
|
|
156
153
|
log.debug("found " + streamHandlers.length + " handler(s) for " + arn, { comp: "ReadModelRuntime" });
|
|
154
|
+
const correlationId = extractMetaField(subRecords, "correlationId");
|
|
155
|
+
const causationId = extractMetaField(subRecords, "causationId");
|
|
156
|
+
const timestamp = extractSentTimestamp(subRecords);
|
|
157
|
+
const retryCount = extractRetryCount(subRecords);
|
|
157
158
|
// Run every read model registered for this source stream (independent
|
|
158
|
-
// QueryDbs, so concurrent is safe).
|
|
159
|
+
// QueryDbs, so concurrent is safe). Each carries its own comp so the
|
|
160
|
+
// shared Lambda's log lines stay separable per read model.
|
|
159
161
|
await Promise.all(
|
|
160
|
-
streamHandlers.map(streamHandler =>
|
|
161
|
-
runEffect(
|
|
162
|
+
streamHandlers.map(({ handler: streamHandler, comp, plugin }) =>
|
|
163
|
+
runEffect(streamHandler({ Records: subRecords }, context), {
|
|
164
|
+
correlationId,
|
|
165
|
+
causationId,
|
|
166
|
+
comp,
|
|
167
|
+
plugin,
|
|
168
|
+
timestamp,
|
|
169
|
+
retryCount,
|
|
170
|
+
})
|
|
162
171
|
)
|
|
163
172
|
);
|
|
164
173
|
} else {
|
|
@@ -33,7 +33,9 @@ let make: ReventlessCore.Runtime.environmentMaker<'event, context, 'result, part
|
|
|
33
33
|
~opts?,
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
// Legacy path (see above): no component kind reaches here because nothing calls
|
|
37
|
+
// it, so it is attributed to the Platform rather than to an invented domain kind.
|
|
38
|
+
let tags = AWS.Tags.make(~name, ~kind=ReventlessCore.ComponentType.Platform, ~role=Runtime, ~scope=Platform)
|
|
37
39
|
let lambda =
|
|
38
40
|
handler->Pulumi.Output.apply(handler =>
|
|
39
41
|
Lambda.CallbackFunction.make(
|
|
@@ -70,6 +72,13 @@ let makeFromCodeAsset: (
|
|
|
70
72
|
kind; `makeFromCodeAsset` announces the provisioned Lambda to the
|
|
71
73
|
`Monitoring` registry (no-op unless an extension registered a backend). */
|
|
72
74
|
~unitKind: ReventlessCore.Monitoring.unitKind,
|
|
75
|
+
/** The modelling kind of the component this Lambda runs, for resource
|
|
76
|
+
attribution (`reventless:kind`). Distinct from `~unitKind`, which grades
|
|
77
|
+
the unit by what its failure means — `CommandHandler` covers aggregates,
|
|
78
|
+
state-change slices and extension points alike, so it cannot identify the
|
|
79
|
+
owning component. Shared Lambdas that host several components of one kind
|
|
80
|
+
(e.g. AllAggregates) pass that kind. */
|
|
81
|
+
~componentKind: ReventlessCore.ComponentType.t,
|
|
73
82
|
~code: Pulumi.Archive.t,
|
|
74
83
|
~sourceCodeHash: string,
|
|
75
84
|
~envVars: dict<Pulumi.Input.t<string>>=?,
|
|
@@ -91,6 +100,7 @@ let makeFromCodeAsset: (
|
|
|
91
100
|
) => ReventlessCore.Runtime.environment<parts> = (
|
|
92
101
|
~name,
|
|
93
102
|
~unitKind,
|
|
103
|
+
~componentKind,
|
|
94
104
|
~code,
|
|
95
105
|
~sourceCodeHash,
|
|
96
106
|
~envVars=Dict.make(),
|
|
@@ -107,9 +117,17 @@ let makeFromCodeAsset: (
|
|
|
107
117
|
let opts =
|
|
108
118
|
opts->Option.map(ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions)
|
|
109
119
|
|
|
120
|
+
// One attribution identity for every resource provisioned here: same owning
|
|
121
|
+
// component and kind throughout, distinct piece roles — the function itself,
|
|
122
|
+
// the execution identity it assumes, and its log group are three pieces of one
|
|
123
|
+
// component, and an inventory needs to roll all three up to it.
|
|
124
|
+
let tagsFor = (~resourceName, ~role) =>
|
|
125
|
+
AWS.Tags.make(~name=resourceName, ~kind=componentKind, ~role, ~component=name)
|
|
126
|
+
|
|
110
127
|
let lambdaRole = IAM.Role.makeWithDefaultPolicy(
|
|
111
128
|
~name,
|
|
112
129
|
~servicePrincipal=AWS.Lambda.principal->Pulumi.Output.make,
|
|
130
|
+
~tags=tagsFor(~resourceName=name, ~role=Identity),
|
|
113
131
|
~opts?,
|
|
114
132
|
)
|
|
115
133
|
|
|
@@ -195,7 +213,7 @@ let makeFromCodeAsset: (
|
|
|
195
213
|
variables->Dict.set("NODE_OPTIONS", Util_Bundle.esmLoaderNodeOptions->Pulumi.Input.make)
|
|
196
214
|
variables->Dict.set("ESM_FALLBACK_DIRS", Util_Bundle.esmFallbackDirs->Pulumi.Input.make)
|
|
197
215
|
|
|
198
|
-
let tags =
|
|
216
|
+
let tags = tagsFor(~resourceName=name, ~role=Runtime)
|
|
199
217
|
let lambda = Lambda.Function.make(
|
|
200
218
|
~name,
|
|
201
219
|
~args={
|
|
@@ -227,6 +245,7 @@ let makeFromCodeAsset: (
|
|
|
227
245
|
~args={
|
|
228
246
|
name: `/aws/lambda/${name}`->Pulumi.Input.make,
|
|
229
247
|
retentionInDays: days->Pulumi.Input.make,
|
|
248
|
+
tags: tagsFor(~resourceName=`${name}LogGroup`, ~role=Logs),
|
|
230
249
|
},
|
|
231
250
|
~opts?,
|
|
232
251
|
)
|
|
@@ -305,6 +324,37 @@ let extractMetaField = (event: event, field) =>
|
|
|
305
324
|
let extractCorrelationId = (event: event) => event->extractMetaField("correlationId")
|
|
306
325
|
let extractCausationId = (event: event) => event->extractMetaField("causationId")
|
|
307
326
|
|
|
327
|
+
// SQS attributes ride on the record but are absent from the minimal record type
|
|
328
|
+
// (see the commented-out `attributes` field in SQS_Queue.res). Read them the same
|
|
329
|
+
// way `recordBody` reads `body`: as a typed getter over the untyped record.
|
|
330
|
+
type recordAttributes = {
|
|
331
|
+
@as("SentTimestamp") sentTimestamp?: string,
|
|
332
|
+
@as("ApproximateReceiveCount") approximateReceiveCount?: string,
|
|
333
|
+
}
|
|
334
|
+
@get
|
|
335
|
+
external recordAttributes: PulumiAws.Lambda.CallbackFunction.record => option<recordAttributes> =
|
|
336
|
+
"attributes"
|
|
337
|
+
|
|
338
|
+
let firstRecordAttributes = (event: event) =>
|
|
339
|
+
event.records->Array.get(0)->Option.flatMap(recordAttributes)
|
|
340
|
+
|
|
341
|
+
// Send time of the triggering message, ms since epoch. SQS stamps
|
|
342
|
+
// `SentTimestamp` server-side (not the producer's clock). DynamoDB-stream
|
|
343
|
+
// sources carry `ApproximateCreationDateTime` instead and are dispatched through
|
|
344
|
+
// the entry-point shells, so None here rather than a fabricated value.
|
|
345
|
+
let extractSentTimestamp = (event: event) =>
|
|
346
|
+
event
|
|
347
|
+
->firstRecordAttributes
|
|
348
|
+
->Option.flatMap(attrs => attrs.sentTimestamp)
|
|
349
|
+
->Option.flatMap(Float.fromString)
|
|
350
|
+
|
|
351
|
+
// Delivery attempt, 1 on first delivery.
|
|
352
|
+
let extractRetryCount = (event: event) =>
|
|
353
|
+
event
|
|
354
|
+
->firstRecordAttributes
|
|
355
|
+
->Option.flatMap(attrs => attrs.approximateReceiveCount)
|
|
356
|
+
->Option.flatMap(Int.fromString(_))
|
|
357
|
+
|
|
308
358
|
external asEventHandler: 'a => ReventlessCore.Runtime.eventHandler<event, context, 'result> =
|
|
309
359
|
"%identity"
|
|
310
360
|
external asEffectHandler: 'a => ReventlessCore.Runtime.effectHandler<
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Stdlib_Int from "@rescript/runtime/lib/es6/Stdlib_Int.js";
|
|
3
4
|
import * as Aws from "@pulumi/aws";
|
|
4
5
|
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
5
6
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
7
|
+
import * as Stdlib_Float from "@rescript/runtime/lib/es6/Stdlib_Float.js";
|
|
6
8
|
import * as IAM$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/IAM.res.mjs";
|
|
7
9
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
10
|
import * as Pulumi from "@pulumi/pulumi";
|
|
@@ -17,7 +19,6 @@ import * as Monitoring$ReventlessCore from "@reventlessdev/reventless-core/src/a
|
|
|
17
19
|
import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
18
20
|
import * as Util_Lambda$ReventlessAws from "../../util/Util_Lambda.res.mjs";
|
|
19
21
|
import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
|
|
20
|
-
import * as CommandTopic$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic.res.mjs";
|
|
21
22
|
import * as Util_IAM_Role$ReventlessAws from "../../util/Util_IAM_Role.res.mjs";
|
|
22
23
|
|
|
23
24
|
let additionalEnvVars = {};
|
|
@@ -28,8 +29,8 @@ function make(name, handler, memorySizeOpt, timeoutOpt, opts) {
|
|
|
28
29
|
let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : 1024;
|
|
29
30
|
let timeout = timeoutOpt !== undefined ? timeoutOpt : 30;
|
|
30
31
|
let opts$1 = Stdlib_Option.map(opts, Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions);
|
|
31
|
-
let lambdaRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name, Pulumi.output(AWS$ReventlessAws.Lambda.principal), opts$1);
|
|
32
|
-
let tags = AWS_Tags$ReventlessAws.make(name,
|
|
32
|
+
let lambdaRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name, Pulumi.output(AWS$ReventlessAws.Lambda.principal), undefined, opts$1);
|
|
33
|
+
let tags = AWS_Tags$ReventlessAws.make(name, "Platform", "Runtime", "Platform", undefined, undefined, undefined, undefined);
|
|
33
34
|
let lambda = handler.apply(handler => new (Aws.lambda.CallbackFunction)(name, Lambda$PulumiAws.CallbackFunction.Args.make(handler, lambdaRole, undefined, undefined, undefined, memorySize, timeout, undefined, undefined, undefined, tags, undefined), opts$1 !== undefined ? Primitive_option.valFromOption(opts$1) : undefined));
|
|
34
35
|
return {
|
|
35
36
|
parts: {
|
|
@@ -43,13 +44,14 @@ function make(name, handler, memorySizeOpt, timeoutOpt, opts) {
|
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
function makeFromCodeAsset(name, unitKind, code, sourceCodeHash, envVarsOpt, memorySizeOpt, timeoutOpt, reservedConcurrency, ephemeralStorageMb, logRetentionDays, dcbMetricsOpt, vpcConfig, opts) {
|
|
47
|
+
function makeFromCodeAsset(name, unitKind, componentKind, code, sourceCodeHash, envVarsOpt, memorySizeOpt, timeoutOpt, reservedConcurrency, ephemeralStorageMb, logRetentionDays, dcbMetricsOpt, vpcConfig, opts) {
|
|
47
48
|
let envVars = envVarsOpt !== undefined ? envVarsOpt : ({});
|
|
48
49
|
let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : Runtime$ReventlessCore.CommandHandlerDefaults.memorySize;
|
|
49
50
|
let timeout = timeoutOpt !== undefined ? timeoutOpt : Runtime$ReventlessCore.CommandHandlerDefaults.timeout;
|
|
50
51
|
let dcbMetrics = dcbMetricsOpt !== undefined ? dcbMetricsOpt : false;
|
|
51
52
|
let opts$1 = Stdlib_Option.map(opts, Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions);
|
|
52
|
-
let
|
|
53
|
+
let tagsFor = (resourceName, role) => AWS_Tags$ReventlessAws.make(resourceName, componentKind, role, undefined, name, undefined, undefined, undefined);
|
|
54
|
+
let lambdaRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name, Pulumi.output(AWS$ReventlessAws.Lambda.principal), tagsFor(name, "Identity"), opts$1);
|
|
53
55
|
Stdlib_Option.forEach(vpcConfig, param => {
|
|
54
56
|
new (Aws.iam.RolePolicy)(name + `VpcAccess`, {
|
|
55
57
|
policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + `VpcAccessPolicy`, [{
|
|
@@ -95,7 +97,7 @@ function makeFromCodeAsset(name, unitKind, code, sourceCodeHash, envVarsOpt, mem
|
|
|
95
97
|
});
|
|
96
98
|
variables["NODE_OPTIONS"] = Util_Bundle$ReventlessAws.esmLoaderNodeOptions;
|
|
97
99
|
variables["ESM_FALLBACK_DIRS"] = Util_Bundle$ReventlessAws.esmFallbackDirs;
|
|
98
|
-
let tags =
|
|
100
|
+
let tags = tagsFor(name, "Runtime");
|
|
99
101
|
let lambda = new (Aws.lambda.Function)(name, {
|
|
100
102
|
handler: "index.handler",
|
|
101
103
|
runtime: "nodejs22.x",
|
|
@@ -118,7 +120,8 @@ function makeFromCodeAsset(name, unitKind, code, sourceCodeHash, envVarsOpt, mem
|
|
|
118
120
|
Stdlib_Option.forEach(logRetentionDays, days => {
|
|
119
121
|
let logGroup = new (Aws.cloudwatch.LogGroup)(name + `LogGroup`, {
|
|
120
122
|
name: `/aws/lambda/` + name,
|
|
121
|
-
retentionInDays: days
|
|
123
|
+
retentionInDays: days,
|
|
124
|
+
tags: tagsFor(name + `LogGroup`, "Logs")
|
|
122
125
|
}, opts$1 !== undefined ? Primitive_option.valFromOption(opts$1) : undefined);
|
|
123
126
|
if (dcbMetrics) {
|
|
124
127
|
[
|
|
@@ -190,6 +193,18 @@ function extractCausationId(event) {
|
|
|
190
193
|
return extractMetaField(event, "causationId");
|
|
191
194
|
}
|
|
192
195
|
|
|
196
|
+
function firstRecordAttributes(event) {
|
|
197
|
+
return Stdlib_Option.flatMap(event.Records[0], prim => prim.attributes);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function extractSentTimestamp(event) {
|
|
201
|
+
return Stdlib_Option.flatMap(Stdlib_Option.flatMap(firstRecordAttributes(event), attrs => attrs.SentTimestamp), Stdlib_Float.fromString);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function extractRetryCount(event) {
|
|
205
|
+
return Stdlib_Option.flatMap(Stdlib_Option.flatMap(firstRecordAttributes(event), attrs => attrs.ApproximateReceiveCount), __x => Stdlib_Int.fromString(__x, undefined));
|
|
206
|
+
}
|
|
207
|
+
|
|
193
208
|
export {
|
|
194
209
|
additionalEnvVars,
|
|
195
210
|
additionalIamPolicies,
|
|
@@ -199,5 +214,8 @@ export {
|
|
|
199
214
|
extractMetaField,
|
|
200
215
|
extractCorrelationId,
|
|
201
216
|
extractCausationId,
|
|
217
|
+
firstRecordAttributes,
|
|
218
|
+
extractSentTimestamp,
|
|
219
|
+
extractRetryCount,
|
|
202
220
|
}
|
|
203
221
|
/* @pulumi/aws Not a pure module */
|