@reventlessdev/reventless-aws 3.0.0-alpha.343 → 3.0.0-alpha.345
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 +15 -0
- package/package.json +11 -11
- package/scripts/ProvisionIdentity.res +8 -3
- package/src/Platform.res +327 -328
- package/src/Platform.res.mjs +4 -2
- package/src/Platform_Casts.res +13 -4
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res +8 -4
- package/src/adapter/Api/AppSync_EventsApi.res +12 -14
- package/src/adapter/Api/AppSync_Resolver_Native.res +3 -4
- package/src/adapter/Api/AppSync_Resolver_Retrying.res +78 -70
- package/src/adapter/Api/AppSync_SourceApiAssociation_Retrying.res +11 -8
- package/src/adapter/Api/Platform_AdminScan_Ops.res +15 -14
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +10 -2
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +40 -50
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +39 -32
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +5 -2
- package/src/adapter/Auth/Auth_ActiveRolePoolAttachment.res +0 -1
- package/src/adapter/Auth/Auth_ActiveRoleStore.res +1 -2
- package/src/adapter/Auth/Auth_ActiveRoleStore_Schema.res +1 -4
- package/src/adapter/Auth/Auth_ActiveRoleTrigger_Ops.res +5 -2
- package/src/adapter/Auth/Auth_Cognito.res +3 -9
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res +1 -4
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_AppSync_NoOp.res +3 -1
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_AppSync.res +1 -4
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS.res +7 -4
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Async.res +6 -1
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_FIFO.res +7 -4
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res +24 -18
- package/src/adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res +9 -4
- package/src/adapter/CommandTopic/CommandTopicRegistry.res +1 -4
- package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +15 -15
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb.res +9 -5
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +179 -213
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +5 -5
- package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res +6 -2
- package/src/adapter/EventCollector/EventCollectorChannel_SQS.res +14 -7
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_FIFO.res +14 -7
- package/src/adapter/EventCollector/EventCollectorChannel_SQS_Runtime.res +6 -3
- package/src/adapter/EventLog/EventLogStorage_DynamoDbStream.res +6 -1
- package/src/adapter/EventLog/EventLogStorage_DynamoDb_Runtime.res +13 -17
- package/src/adapter/EventLog/EventLogStorage_Postgres.res +7 -4
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +12 -7
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +4 -2
- package/src/adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res +3 -2
- package/src/adapter/EventTopic/EventTopicPublisher_SNS.res +20 -12
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_FIFO.res +21 -9
- package/src/adapter/EventTopic/EventTopicPublisher_SNS_Runtime.res +5 -3
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver.res +6 -3
- package/src/adapter/Mcp/MCP_Lambda.res +31 -46
- package/src/adapter/Messaging/Messaging_Ses_Backend.res +11 -7
- package/src/adapter/Messaging/Messaging_Ses_Backend.res.mjs +2 -4
- package/src/adapter/Monitoring/Monitoring_CloudWatch.res +16 -16
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +5 -10
- package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +4 -2
- package/src/adapter/Postgres/PgConnection.res +6 -5
- package/src/adapter/Postgres/PgMigration_Builder.res +4 -8
- package/src/adapter/Postgres/PgRuntime.res +4 -7
- package/src/adapter/QueryDb/PgQueryResolver_Builder.res +9 -11
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +22 -21
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +114 -111
- package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +1 -7
- package/src/adapter/QueryDb/QueryDbStorage.res +4 -2
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb.res +23 -24
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDbStream.res +8 -2
- package/src/adapter/QueryDb/QueryDbStorage_DynamoDb_Runtime.res +71 -73
- package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +5 -2
- package/src/adapter/QueryDb/QueryInterceptor_Provisioning.res +1 -4
- package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +8 -2
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro.res +73 -81
- package/src/adapter/Runtime/AggregateRuntime_Builder_Micro_Async.res +72 -78
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate.res +44 -67
- package/src/adapter/Runtime/AggregateRuntime_Builder_PerAggregate_Async.res +44 -67
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +53 -82
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +47 -77
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +18 -11
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +80 -97
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res +2 -7
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -10
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +24 -15
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res +17 -37
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +31 -57
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +18 -24
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +1 -5
- package/src/adapter/Runtime/ExtensionPointRuntime_Builder_PerExtensionPoint.res +4 -3
- package/src/adapter/Runtime/HeartbeatEntryPoint.res +0 -1
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +4 -2
- package/src/adapter/Runtime/PgMigrationEntryPoint.res +0 -1
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +3 -2
- package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res +1 -2
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +4 -5
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +22 -10
- package/src/adapter/Runtime/RuntimeExtensionEntryPoint_Ops.res +8 -2
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +14 -31
- package/src/adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res +16 -17
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +10 -9
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +46 -52
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +36 -41
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +31 -37
- package/src/adapter/Runtime/TaskRuntime_Builder_PerBucket.res +6 -7
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents.res +4 -6
- package/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res +2 -6
- package/src/adapter/StateTopic/StateTopic_AppSync.res +21 -16
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +36 -32
- package/src/adapter/Task/TaskBucket_S3.res +6 -1
- package/src/adapter/Upload/Upload_Claim_S3.res +17 -20
- package/src/adapter/Upload/Upload_Claim_S3_Ops.res +5 -2
- package/src/adapter/Upload/Upload_Presign_S3.res +51 -49
- package/src/capability/Capability_Geocoding_AwsLocation.res +4 -1
- package/src/capability/Capability_Messaging.res +4 -5
- package/src/capability/Capability_Messaging_Ses.res +3 -1
- package/src/capability/Capability_ObjectStore_S3.res +1 -7
- package/src/components/Api/AppSync_Adapter.res +28 -41
- package/src/components/Api/AppSync_SdlDecorate.res +38 -30
- package/src/components/AutomationSlice_Builder.res +6 -4
- package/src/components/ExtensionPoint_Builder.res +9 -10
- package/src/components/InboundTranslationSlice_Builder.res +7 -8
- package/src/components/OutboundTranslationSlice_Builder.res +14 -8
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
- package/src/components/SideEffectHandler_Single.res +4 -4
- package/src/components/StateViewSlice_Builder.res +6 -4
- package/src/components/StateViewSlice_Builder_Stream.res +6 -4
- package/src/components/Task_Builder_PerBucket.res +10 -11
- package/src/plugin/cloner/ClonerRunner_Fargate.res +3 -10
- package/src/plugin/cloner/ClonerRunner_Fargate_Runtime.res +1 -4
- package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +5 -5
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +19 -21
- package/src/plugin/runtime/PluginRuntime_Builder.res +167 -188
- package/src/plugin/runtime/Util_PluginMessage_Runtime.res +6 -5
- package/src/plugin/stack/Plugin_Stack.res +104 -103
- package/src/util/Util_AlarmSpec.res +2 -1
- package/src/util/Util_AppSync.res +8 -2
- package/src/util/Util_AppSync_Caller.res +15 -14
- package/src/util/Util_Bundle.res +144 -67
- package/src/util/Util_Bundle.res.mjs +64 -13
- package/src/util/Util_CognitoGroupUser_Runtime.res +32 -34
- package/src/util/Util_Cognito_Runtime.res +11 -7
- package/src/util/Util_DeadLetterQueue.res +12 -2
- package/src/util/Util_DynamoDb.res +5 -3
- package/src/util/Util_DynamoDbStream.res +7 -3
- package/src/util/Util_DynamoDbStream_Runtime.res +1 -2
- package/src/util/Util_DynamoDb_Runtime.res +23 -41
- package/src/util/Util_HostUiDomain.res +6 -1
- package/src/util/Util_Kinesis_Runtime.res +10 -11
- package/src/util/Util_Lambda.res +12 -5
- package/src/util/Util_LambdaEnvBudget.res +5 -5
- package/src/util/Util_LambdaLogging.res +1 -2
- package/src/util/Util_LogGroup_Adopting.res +1 -4
- package/src/util/Util_ResourceNaming.res +1 -2
- package/src/util/Util_SNS.res +8 -3
- package/src/util/Util_SNS_FIFO.res +1 -1
- package/src/util/Util_SNS_Runtime.res +11 -10
- package/src/util/Util_SQS.res +4 -2
- package/src/util/Util_SQS_FIFO.res +5 -2
- package/src/util/Util_SQS_Runtime.res +37 -36
- package/src/util/Util_ShellConfig.res +3 -2
- package/src/util/Util_StaticBundle.res +1 -3
- package/src/util/Util_StoreLayout.res +4 -1
- package/tests/AWS_TagsTest.res +16 -5
- package/tests/AppSyncEventsSigner_OpsTest.res +11 -2
- package/tests/AppSync_AdapterTest.res +203 -224
- package/tests/AppSync_Resolver_RetryingTest.res +64 -44
- package/tests/AppSync_RetirementNarrowingTest.res +59 -46
- package/tests/AppSync_SdlDecorateTest.res +22 -20
- package/tests/AppSync_SourceApiAssociation_RetryingTest.res +40 -22
- package/tests/Auth_ActiveRolePoolAttachmentTest.res +114 -107
- package/tests/Auth_ActiveRoleStoreTest.res +12 -9
- package/tests/Auth_ActiveRoleStore_SchemaTest.res +2 -1
- package/tests/Auth_ActiveRoleTrigger_OpsTest.res +20 -16
- package/tests/Auth_CognitoTest.res +31 -46
- package/tests/AutomationSliceEntryPoint_OpsTest.res +69 -67
- package/tests/BakeConvergenceTest.res +22 -25
- package/tests/Capability_MessagingTest.res +28 -12
- package/tests/CommandTopicQueuePolicyTest.res +1 -2
- package/tests/CounterEntryPoint_OpsTest.res +3 -7
- package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +306 -210
- package/tests/EventCollectorConnectLambdaPreviewTest.res +14 -7
- package/tests/EventCollectorEntryPoint_OpsTest.res +12 -4
- package/tests/EventCollectorPluginDefinitionAssetTest.res +18 -11
- package/tests/EventCollectorReadyGateTest.res +5 -4
- package/tests/EventLogStorage_DynamoDb_RuntimeTest.res +1 -2
- package/tests/EventMapperEntryPoint_OpsTest.res +1 -3
- package/tests/ExtensionPointEntryPoint_OpsTest.res +1 -4
- package/tests/HandlerFactoryDispatchTest.res +97 -70
- package/tests/MCP_LambdaTest.res +16 -27
- package/tests/PgChangeFeedRelay_IntegrationTest.res +123 -93
- package/tests/PgChangeFeedRelay_RuntimeTest.res +13 -10
- package/tests/PgMigrationEntryPoint_IntegrationTest.res +0 -1
- package/tests/PgPipeline_IntegrationTest.res +80 -66
- package/tests/PgQueryDbUnion_IntegrationTest.res +1 -2
- package/tests/PgQueryResolverEntryPoint_OpsTest.res +2 -6
- package/tests/PgQueryResolver_LambdaTest.res +41 -24
- package/tests/Platform_AdminScan_OpsTest.res +4 -1
- package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +137 -129
- package/tests/ProjectionEntryPoint_DisplayNameTest.res +7 -3
- package/tests/ProjectionEntryPoint_UnionStampTest.res +3 -2
- package/tests/ProvisionIdentityTest.res +2 -6
- package/tests/QueryDbOwnerIndexTableTest.res +6 -4
- package/tests/QueryDbResolvers_AppSyncTest.res +23 -16
- package/tests/QueryInterceptor_LambdaTest.res +11 -13
- package/tests/ReadModelEntryPoint_OpsTest.res +43 -41
- package/tests/RuntimeExtensionEntryPoint_OpsTest.res +4 -3
- package/tests/SideEffectEntryPoint_OpsTest.res +23 -18
- package/tests/StateChangeDescriptorParityTest.res +28 -26
- package/tests/StateTopicKeySchemaTest.res +18 -15
- package/tests/StateViewSliceEntryPoint_OpsTest.res +55 -44
- package/tests/TaskBucketEntryPoint_OpsTest.res +13 -13
- package/tests/Upload_ClaimTest.res +3 -3
- package/tests/Util_AwsErrorTest.res +4 -7
- package/tests/Util_BundleDependencyClosureTest.res +41 -3
- package/tests/Util_BundleDependencyClosureTest.res.mjs +11 -0
- package/tests/Util_BundleEsmLoaderTest.res +3 -3
- package/tests/Util_BundleResolveTest.res +4 -6
- package/tests/Util_BundleRuntimeExtensionTest.res +17 -18
- package/tests/Util_DcbMetricsTest.res +6 -6
- package/tests/Util_DynamoDbStreamTest.res +4 -5
- package/tests/Util_DynamoDbTest.res +3 -2
- package/tests/Util_LambdaEnvBudgetTest.res +22 -18
- package/tests/Util_LambdaLoggingTest.res +9 -8
- package/tests/Util_LogGroup_AdoptingTest.res +10 -5
- package/tests/Util_LogRetentionTest.res +6 -2
- package/tests/Util_OwnerScopeEnvTest.res +10 -8
- package/tests/Util_SQS_RuntimeTest.res +5 -7
- package/tests/Util_ShellConfigTest.res +7 -17
- package/tests/Util_StaticBundleTest.res +3 -8
- package/tests/Util_StoreLayoutTest.res +17 -12
- package/tests/integration/AggregateEntryPoint_IntegrationTest.res +1 -4
- package/tests/integration/DcbCommandTopicEntryPoint_IntegrationTest.res +15 -15
- package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +296 -232
- package/tests/integration/DcbIntegrationHarness.res +8 -8
- package/tests/integration/EpCompositeSlice.res +16 -8
- package/tests/integration/QueryDbUnion_DynamoDb_IntegrationTest.res +8 -10
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +10 -10
- package/tests/integration/SvsTestSlice_Projection.res +1 -3
- package/tests/util/Util_AlarmSpecTest.res +62 -49
|
@@ -93,9 +93,9 @@ let addToRegistry = (
|
|
|
93
93
|
| None => registry->Dict.set(sourceUrn, [handler])
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
let groupBySource = (
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
let groupBySource = (records: array<PulumiAws.Lambda.CallbackFunction.record>): dict<
|
|
97
|
+
array<PulumiAws.Lambda.CallbackFunction.record>,
|
|
98
|
+
> => {
|
|
99
99
|
let grouped = Dict.make()
|
|
100
100
|
records->Array.forEach(record =>
|
|
101
101
|
switch grouped->Dict.get(record.eventSourceARN) {
|
|
@@ -110,47 +110,42 @@ let groupBySource = (
|
|
|
110
110
|
// handler registered for each source (independent storage, so concurrent is
|
|
111
111
|
// safe). Each handler carries its own comp so the shared Lambda's log lines
|
|
112
112
|
// stay separable per component.
|
|
113
|
-
let makeRoutedHandler = (
|
|
114
|
-
~comp: string,
|
|
115
|
-
registryPromise: promise<dict<array<registeredHandler>>>,
|
|
116
|
-
) =>
|
|
113
|
+
let makeRoutedHandler = (~comp: string, registryPromise: promise<dict<array<registeredHandler>>>) =>
|
|
117
114
|
async (event: PulumiAws.Lambda.CallbackFunction.event, context: PulumiAws.Lambda.context) => {
|
|
118
115
|
setRequestId(context.awsRequestId)
|
|
119
116
|
let registry = await registryPromise
|
|
120
|
-
let _ =
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
->
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
117
|
+
let _ = await groupBySource(event.records)
|
|
118
|
+
->Dict.toArray
|
|
119
|
+
->Array.map(async ((arn, subRecords)) =>
|
|
120
|
+
switch registry->Dict.get(arn)->Option.filter(hs => hs->Array.length > 0) {
|
|
121
|
+
| Some(streamHandlers) =>
|
|
122
|
+
logDebug(
|
|
123
|
+
`found ${streamHandlers->Array.length->Int.toString} handler(s) for ${arn}`,
|
|
124
|
+
{comp: comp},
|
|
125
|
+
)
|
|
126
|
+
let correlationId = extractMetaField(subRecords, "correlationId")
|
|
127
|
+
let causationId = extractMetaField(subRecords, "causationId")
|
|
128
|
+
let timestamp = extractSentTimestamp(subRecords)
|
|
129
|
+
let retryCount = extractRetryCount(subRecords)
|
|
130
|
+
let subEvent: PulumiAws.Lambda.CallbackFunction.event = {records: subRecords}
|
|
131
|
+
let _ = await streamHandlers
|
|
132
|
+
->Array.map(registered =>
|
|
133
|
+
runEffect(
|
|
134
|
+
registered.handler(subEvent, context),
|
|
135
|
+
{
|
|
136
|
+
?correlationId,
|
|
137
|
+
?causationId,
|
|
138
|
+
comp: ?registered.comp,
|
|
139
|
+
plugin: ?registered.plugin,
|
|
140
|
+
?timestamp,
|
|
141
|
+
retryCount,
|
|
142
|
+
},
|
|
129
143
|
)
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
await streamHandlers
|
|
137
|
-
->Array.map(registered =>
|
|
138
|
-
runEffect(
|
|
139
|
-
registered.handler(subEvent, context),
|
|
140
|
-
{
|
|
141
|
-
correlationId: ?correlationId,
|
|
142
|
-
causationId: ?causationId,
|
|
143
|
-
comp: ?registered.comp,
|
|
144
|
-
plugin: ?registered.plugin,
|
|
145
|
-
timestamp: ?timestamp,
|
|
146
|
-
retryCount,
|
|
147
|
-
},
|
|
148
|
-
)
|
|
149
|
-
)
|
|
150
|
-
->Promise.all
|
|
151
|
-
| None => logWarn("no handler found: " ++ arn, {comp: comp})
|
|
152
|
-
}
|
|
153
|
-
)
|
|
154
|
-
->Promise.all
|
|
144
|
+
)
|
|
145
|
+
->Promise.all
|
|
146
|
+
| None => logWarn("no handler found: " ++ arn, {comp: comp})
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
->Promise.all
|
|
155
150
|
""
|
|
156
151
|
}
|
|
@@ -140,40 +140,36 @@ let dispatchTaskActions = async (
|
|
|
140
140
|
~publishCommands: dict<ReventlessCore.CommandTopic.publishJsons>,
|
|
141
141
|
~schedulerOps: unit => option<schedulerOps>,
|
|
142
142
|
): unit => {
|
|
143
|
-
let _ =
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
logWarn(
|
|
152
|
-
`no publish function for aggregate "${aggregateName}"`,
|
|
153
|
-
{comp: "TaskBucketRuntime"},
|
|
154
|
-
)
|
|
155
|
-
}
|
|
156
|
-
| CreateSchedule(schedule) =>
|
|
157
|
-
switch schedulerOps() {
|
|
158
|
-
| Some(ops) => await ops.createSchedule(schedule)
|
|
159
|
-
| None =>
|
|
160
|
-
logWarn(
|
|
161
|
-
"CreateSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)",
|
|
162
|
-
{comp: "TaskBucketRuntime"},
|
|
163
|
-
)
|
|
164
|
-
}
|
|
165
|
-
| DeleteSchedule(name) =>
|
|
166
|
-
switch schedulerOps() {
|
|
167
|
-
| Some(ops) => await ops.deleteSchedule(name)
|
|
168
|
-
| None =>
|
|
169
|
-
logWarn(
|
|
170
|
-
"DeleteSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)",
|
|
171
|
-
{comp: "TaskBucketRuntime"},
|
|
172
|
-
)
|
|
173
|
-
}
|
|
143
|
+
let _ = await actions
|
|
144
|
+
->Array.map(async action =>
|
|
145
|
+
switch action {
|
|
146
|
+
| Reventless.Task.PublishCommands(aggregateName, cmdJsons) =>
|
|
147
|
+
switch publishCommands->Dict.get(aggregateName) {
|
|
148
|
+
| Some(publish) => await publish(cmdJsons)
|
|
149
|
+
| None =>
|
|
150
|
+
logWarn(`no publish function for aggregate "${aggregateName}"`, {comp: "TaskBucketRuntime"})
|
|
174
151
|
}
|
|
175
|
-
)
|
|
176
|
-
|
|
152
|
+
| CreateSchedule(schedule) =>
|
|
153
|
+
switch schedulerOps() {
|
|
154
|
+
| Some(ops) => await ops.createSchedule(schedule)
|
|
155
|
+
| None =>
|
|
156
|
+
logWarn(
|
|
157
|
+
"CreateSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)",
|
|
158
|
+
{comp: "TaskBucketRuntime"},
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
| DeleteSchedule(name) =>
|
|
162
|
+
switch schedulerOps() {
|
|
163
|
+
| Some(ops) => await ops.deleteSchedule(name)
|
|
164
|
+
| None =>
|
|
165
|
+
logWarn(
|
|
166
|
+
"DeleteSchedule skipped — no scheduler configured for this Task (add sideEffects to setup)",
|
|
167
|
+
{comp: "TaskBucketRuntime"},
|
|
168
|
+
)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
->Promise.all
|
|
177
173
|
}
|
|
178
174
|
|
|
179
175
|
// ── Exported Lambda handler ─────────────────────────────────────────────────
|
|
@@ -187,10 +183,8 @@ let makeHandler = (~callback: ReventlessCore.Task.bucketCallback, ~config: handl
|
|
|
187
183
|
let publishCommands = buildPublishCommands(config.publishToAggregates)
|
|
188
184
|
async (event: PulumiAws.S3.Bucket.event, context: PulumiAws.Lambda.context) => {
|
|
189
185
|
let actions = await handleEvents(event, context)
|
|
190
|
-
await dispatchTaskActions(
|
|
191
|
-
|
|
192
|
-
~publishCommands,
|
|
193
|
-
~schedulerOps=() => makeSchedulerOps(config.scheduler),
|
|
186
|
+
await dispatchTaskActions(~actions, ~publishCommands, ~schedulerOps=() =>
|
|
187
|
+
makeSchedulerOps(config.scheduler)
|
|
194
188
|
)
|
|
195
189
|
""
|
|
196
190
|
}
|
|
@@ -35,13 +35,14 @@ let forBucketCallback = (
|
|
|
35
35
|
})
|
|
36
36
|
|
|
37
37
|
// Build HANDLER_CONFIG JSON
|
|
38
|
-
let callbackModule =
|
|
39
|
-
callbackModulePath->JSON.stringifyAny->Option.getOr(`""`)
|
|
38
|
+
let callbackModule = callbackModulePath->JSON.stringifyAny->Option.getOr(`""`)
|
|
40
39
|
let publishToAggregatesJson =
|
|
41
40
|
publishToAggregatesEnvVars
|
|
42
41
|
->Dict.toArray
|
|
43
42
|
->Array.map(((aggName, envVar)) =>
|
|
44
|
-
`${aggName->JSON.stringifyAny->Option.getOr(`""`)}: ${envVar
|
|
43
|
+
`${aggName->JSON.stringifyAny->Option.getOr(`""`)}: ${envVar
|
|
44
|
+
->JSON.stringifyAny
|
|
45
|
+
->Option.getOr(`""`)}`
|
|
45
46
|
)
|
|
46
47
|
->Array.join(",")
|
|
47
48
|
|
|
@@ -50,14 +51,12 @@ let forBucketCallback = (
|
|
|
50
51
|
// runtime. None for tasks without a side-effect handler.
|
|
51
52
|
let handlerConfigJson = switch schedulerConfig {
|
|
52
53
|
| None =>
|
|
53
|
-
`{"callbackModule":${callbackModule},"publishToAggregates":{${publishToAggregatesJson}}}
|
|
54
|
-
->Pulumi.Output.make
|
|
54
|
+
`{"callbackModule":${callbackModule},"publishToAggregates":{${publishToAggregatesJson}}}`->Pulumi.Output.make
|
|
55
55
|
| Some({schedulerRoleUrn, targetUrn, targetName}) =>
|
|
56
56
|
envVars->Dict.set("SCHEDULER_ROLE_ARN", schedulerRoleUrn->Pulumi.Output.asInput)
|
|
57
57
|
envVars->Dict.set("SCHEDULER_TARGET_ARN", targetUrn->Pulumi.Output.asInput)
|
|
58
58
|
envVars->Dict.set("SCHEDULER_TARGET_NAME", targetName->Pulumi.Output.asInput)
|
|
59
|
-
`{"callbackModule":${callbackModule},"publishToAggregates":{${publishToAggregatesJson}},"scheduler":{"roleArnEnv":"SCHEDULER_ROLE_ARN","targetArnEnv":"SCHEDULER_TARGET_ARN","targetNameEnv":"SCHEDULER_TARGET_NAME"}}
|
|
60
|
-
->Pulumi.Output.make
|
|
59
|
+
`{"callbackModule":${callbackModule},"publishToAggregates":{${publishToAggregatesJson}},"scheduler":{"roleArnEnv":"SCHEDULER_ROLE_ARN","targetArnEnv":"SCHEDULER_TARGET_ARN","targetNameEnv":"SCHEDULER_TARGET_NAME"}}`->Pulumi.Output.make
|
|
61
60
|
}
|
|
62
61
|
envVars->Dict.set("HANDLER_CONFIG", handlerConfigJson->Pulumi.Output.asInput)
|
|
63
62
|
|
|
@@ -56,11 +56,9 @@ let make: ReventlessCore.Scheduler_Adapter.scheduledPublisherMaker = (~name, ~op
|
|
|
56
56
|
~service="CloudWatchEvents"->Pulumi.Output.make,
|
|
57
57
|
~resourceType="aws:cloudwatch:EventRule"->Pulumi.Output.make,
|
|
58
58
|
),
|
|
59
|
-
operations: role.arn->Pulumi.Output.apply(roleArn =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}: ReventlessInfra.Scheduler.operations
|
|
64
|
-
)),
|
|
59
|
+
operations: role.arn->Pulumi.Output.apply((roleArn): ReventlessInfra.Scheduler.operations => {
|
|
60
|
+
createSchedule: ScheduledPublisher_CloudWatchEvents_Runtime.createSchedule(~roleArn),
|
|
61
|
+
deleteSchedule: ScheduledPublisher_CloudWatchEvents_Runtime.deleteSchedule,
|
|
62
|
+
}),
|
|
65
63
|
}
|
|
66
64
|
}
|
|
@@ -28,9 +28,7 @@ let createSchedule = (~roleArn: string): ReventlessCore.Scheduler.createSchedule
|
|
|
28
28
|
| [] =>
|
|
29
29
|
let err = "createSchedule not possible: no Queue configured !"
|
|
30
30
|
ReventlessCore.EffectLogger.logError(~comp=__MODULE__, err)
|
|
31
|
-
->Effect.flatMap(_ =>
|
|
32
|
-
Effect.sync(() => JsError.throwWithMessage(err))
|
|
33
|
-
)
|
|
31
|
+
->Effect.flatMap(_ => Effect.sync(() => JsError.throwWithMessage(err)))
|
|
34
32
|
->Effect.runPromise
|
|
35
33
|
| resources =>
|
|
36
34
|
let resource = resources->Array.getUnsafe(0) // FIXME
|
|
@@ -69,9 +67,7 @@ let deleteSchedule: ReventlessCore.Scheduler.deleteSchedule = (queueResources, n
|
|
|
69
67
|
| [] =>
|
|
70
68
|
let err = "deleteSchedule not possible: no Queue configured !"
|
|
71
69
|
ReventlessCore.EffectLogger.logError(~comp=__MODULE__, err)
|
|
72
|
-
->Effect.flatMap(_ =>
|
|
73
|
-
Effect.sync(() => JsError.throwWithMessage(err))
|
|
74
|
-
)
|
|
70
|
+
->Effect.flatMap(_ => Effect.sync(() => JsError.throwWithMessage(err)))
|
|
75
71
|
->Effect.runPromise
|
|
76
72
|
| resources =>
|
|
77
73
|
let resource = resources->Array.getUnsafe(0) // FIXME
|
|
@@ -126,7 +126,6 @@ let registry: dict<array<streamEntry>> = Dict.make()
|
|
|
126
126
|
// Uses native Node.js crypto + fetch with SigV4 — no extra SDK packages needed.
|
|
127
127
|
// Credentials come from the Lambda execution role via the process.env AWS_* variables.
|
|
128
128
|
|
|
129
|
-
|
|
130
129
|
// ── Registration ──────────────────────────────────────────────────────────────
|
|
131
130
|
|
|
132
131
|
/** Register any stream-enabled DynamoDB table with the relay. Read-model and
|
|
@@ -206,10 +205,7 @@ let make = (
|
|
|
206
205
|
|
|
207
206
|
// ── Finalize: build the shared Lambda + IAM + ESMs ────────────────────────────
|
|
208
207
|
|
|
209
|
-
let finish = (
|
|
210
|
-
~eventsApi: AppSync_EventsApi.t,
|
|
211
|
-
~opts: Pulumi.CustomResourceOptions.t,
|
|
212
|
-
) => {
|
|
208
|
+
let finish = (~eventsApi: AppSync_EventsApi.t, ~opts: Pulumi.CustomResourceOptions.t) => {
|
|
213
209
|
let key = eventsApi.name
|
|
214
210
|
switch registry->Dict.get(key) {
|
|
215
211
|
| None => ()
|
|
@@ -231,8 +227,7 @@ let finish = (
|
|
|
231
227
|
)
|
|
232
228
|
|
|
233
229
|
// IAM policy: read from every stream + publish to the events API.
|
|
234
|
-
let streamArnsOutput =
|
|
235
|
-
entries->Array.map(e => e.streamArn)->Pulumi.Output.all
|
|
230
|
+
let streamArnsOutput = entries->Array.map(e => e.streamArn)->Pulumi.Output.all
|
|
236
231
|
let _ =
|
|
237
232
|
(streamArnsOutput, eventsApi.api.apiArn)
|
|
238
233
|
->Pulumi.Output.all2
|
|
@@ -306,13 +301,15 @@ let finish = (
|
|
|
306
301
|
let dict = Dict.make()
|
|
307
302
|
tableNames->Array.forEachWithIndex((tableName, i) => {
|
|
308
303
|
let entry = entries->Array.getUnsafe(i)
|
|
309
|
-
entry.retiredField->Option.forEach(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
304
|
+
entry.retiredField->Option.forEach(
|
|
305
|
+
f => {
|
|
306
|
+
let obj = Dict.fromArray([("field", f->JSON.Encode.string)])
|
|
307
|
+
entry.retiredValues->Option.forEach(
|
|
308
|
+
vs => obj->Dict.set("values", vs->Array.map(JSON.Encode.string)->JSON.Encode.array),
|
|
309
|
+
)
|
|
310
|
+
dict->Dict.set(tableName, obj->JSON.Encode.object)
|
|
311
|
+
},
|
|
312
|
+
)
|
|
316
313
|
})
|
|
317
314
|
dict->JSON.Encode.object->JSON.stringify
|
|
318
315
|
})
|
|
@@ -324,7 +321,10 @@ let finish = (
|
|
|
324
321
|
// buildCodeArchive ships the ESM resolve-hook so `@rescript/runtime` and the
|
|
325
322
|
// handler's bare @aws-sdk/util-dynamodb resolve from layer / managed runtime.
|
|
326
323
|
let packageDirs = Dict.fromArray([
|
|
327
|
-
(
|
|
324
|
+
(
|
|
325
|
+
"@reventlessdev/reventless-aws",
|
|
326
|
+
Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-aws"),
|
|
327
|
+
),
|
|
328
328
|
])
|
|
329
329
|
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
330
330
|
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs",
|
|
@@ -365,7 +365,12 @@ let finish = (
|
|
|
365
365
|
memorySize: 256->Pulumi.Input.make,
|
|
366
366
|
timeout: 30->Pulumi.Input.make,
|
|
367
367
|
layers,
|
|
368
|
-
tags: AWS.Tags.make(
|
|
368
|
+
tags: AWS.Tags.make(
|
|
369
|
+
~name=name ++ "StateTopic",
|
|
370
|
+
~kind=ReventlessCore.QueryDb.componentType,
|
|
371
|
+
~role=StateTopic,
|
|
372
|
+
~component=name,
|
|
373
|
+
),
|
|
369
374
|
environment: (
|
|
370
375
|
{
|
|
371
376
|
Lambda.Function.variables: Dict.fromArray([
|
|
@@ -17,19 +17,21 @@
|
|
|
17
17
|
// a core import can't drag deploy-time code into the Lambda's import graph — so
|
|
18
18
|
// `StateChangeDescriptorParityTest` drives all three and asserts they agree.
|
|
19
19
|
|
|
20
|
-
|
|
21
20
|
let endpoint = NodeProcess.env->Dict.get("APPSYNC_ENDPOINT")->Option.getOr("")
|
|
22
21
|
|
|
23
22
|
// STATE_TOPIC_MAP: `{ <tableName>: <topicName> }`, injected at deploy time.
|
|
24
|
-
let topicMap: dict<string> =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
let topicMap: dict<string> = switch NodeProcess.env
|
|
24
|
+
->Dict.get("STATE_TOPIC_MAP")
|
|
25
|
+
->Option.getOr("{}")
|
|
26
|
+
->JSON.parseOrThrow
|
|
27
|
+
->JSON.Decode.object {
|
|
28
|
+
| Some(obj) =>
|
|
29
|
+
obj
|
|
30
|
+
->Dict.toArray
|
|
31
|
+
->Array.filterMap(((k, v)) => v->JSON.Decode.string->Option.map(s => (k, s)))
|
|
32
|
+
->Dict.fromArray
|
|
33
|
+
| None => Dict.make()
|
|
34
|
+
}
|
|
33
35
|
|
|
34
36
|
// ── DynamoDB stream event (only the fields this handler reads) ───────────────
|
|
35
37
|
|
|
@@ -64,34 +66,35 @@ type event = {@as("Records") records: array<record>}
|
|
|
64
66
|
// writer the other, and the two drift the first time a state name contains the
|
|
65
67
|
// separator — an argument that only gets stronger now that a lifecycle can name
|
|
66
68
|
// several states.
|
|
67
|
-
let retiredMap: dict<Reventless.OwnerScope.retiredScope> =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
69
|
+
let retiredMap: dict<Reventless.OwnerScope.retiredScope> = switch NodeProcess.env
|
|
70
|
+
->Dict.get("STATE_RETIRED_MAP")
|
|
71
|
+
->Option.getOr("{}")
|
|
72
|
+
->JSON.parseOrThrow
|
|
73
|
+
->JSON.Decode.object {
|
|
74
|
+
| Some(d) =>
|
|
75
|
+
let out = Dict.make()
|
|
76
|
+
d->Dict.forEachWithKey((v, k) =>
|
|
77
|
+
v
|
|
78
|
+
->JSON.Decode.object
|
|
79
|
+
->Option.flatMap(o =>
|
|
80
|
+
o
|
|
81
|
+
->Dict.get("field")
|
|
82
|
+
->Option.flatMap(JSON.Decode.string)
|
|
83
|
+
->Option.map(
|
|
84
|
+
field => {
|
|
83
85
|
Reventless.OwnerScope.field,
|
|
84
86
|
values: o
|
|
85
87
|
->Dict.get("values")
|
|
86
88
|
->Option.flatMap(JSON.Decode.array)
|
|
87
89
|
->Option.map(vs => vs->Array.filterMap(JSON.Decode.string)),
|
|
88
|
-
}
|
|
90
|
+
},
|
|
89
91
|
)
|
|
90
|
-
->Option.forEach(scope => out->Dict.set(k, scope))
|
|
91
92
|
)
|
|
92
|
-
out
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
->Option.forEach(scope => out->Dict.set(k, scope))
|
|
94
|
+
)
|
|
95
|
+
out
|
|
96
|
+
| None => Dict.make()
|
|
97
|
+
}
|
|
95
98
|
|
|
96
99
|
let tableNameFromEventSourceArn = (arn: string): option<string> => {
|
|
97
100
|
let parts = arn->String.split("/")
|
|
@@ -166,7 +169,8 @@ let pickSortKeyValue = (image: dict<JSON.t>): option<string> =>
|
|
|
166
169
|
/** Cap on the serialised state payload, in characters. Must match
|
|
167
170
|
`LocalStateChangeDescriptor.maxStateChars` and `StateTopicPublish.mjs`'s
|
|
168
171
|
MAX_STATE_CHARS — see the local module for the reasoning. */
|
|
169
|
-
let maxStateChars =
|
|
172
|
+
let maxStateChars =
|
|
173
|
+
60 * 1024
|
|
170
174
|
|
|
171
175
|
/** Build the change descriptor. Split out of `processRecord` so the wire format
|
|
172
176
|
can be asserted against the other two implementations without a network call.
|
|
@@ -3,7 +3,12 @@ type callbackEvent = PulumiAws.S3.Bucket.event
|
|
|
3
3
|
type context = PulumiAws.Lambda.context
|
|
4
4
|
type bucketParts = PulumiAws.S3.Bucket.t
|
|
5
5
|
|
|
6
|
-
let subscribeLambda2S3Bucket = (
|
|
6
|
+
let subscribeLambda2S3Bucket = (
|
|
7
|
+
lambda: Pulumi.Output.t<PulumiAws.Lambda.Function.t>,
|
|
8
|
+
name,
|
|
9
|
+
bucket,
|
|
10
|
+
opts,
|
|
11
|
+
) => {
|
|
7
12
|
// Coerce Function.t → CallbackFunction.t for S3 bucket notification bindings
|
|
8
13
|
// (structurally compatible: both have arn, id, name)
|
|
9
14
|
let handler: Pulumi.Output.t<PulumiAws.Lambda.CallbackFunction.t> = lambda->Obj.magic
|
|
@@ -127,8 +127,7 @@ let finish = (
|
|
|
127
127
|
log.warn(
|
|
128
128
|
~comp="Upload_Claim",
|
|
129
129
|
`event log(s) ${logs->Array.join(", ")} declare storage-ref fields but publish through an ` ++
|
|
130
|
-
`SNS event topic, which the claimer does not subscribe to — objects those events reference ` ++
|
|
131
|
-
`keep the pending tag. Do not enable a store's expiry rule while this is true.`,
|
|
130
|
+
`SNS event topic, which the claimer does not subscribe to — objects those events reference ` ++ `keep the pending tag. Do not enable a store's expiry rule while this is true.`,
|
|
132
131
|
)
|
|
133
132
|
unreachable->Dict.set(plugin, [])
|
|
134
133
|
| _ => ()
|
|
@@ -161,8 +160,7 @@ let finish = (
|
|
|
161
160
|
->Pulumi.Output.all2
|
|
162
161
|
->Pulumi.Output.apply(((streamArns, stores)) => {
|
|
163
162
|
open PolicyDocument
|
|
164
|
-
let objectArns =
|
|
165
|
-
stores->Array.map(s => `arn:aws:s3:::${s.bucketName}/${s.servedPrefix}/*`)
|
|
163
|
+
let objectArns = stores->Array.map(s => `arn:aws:s3:::${s.bucketName}/${s.servedPrefix}/*`)
|
|
166
164
|
let _rolePolicy = IAM.RolePolicy.make(
|
|
167
165
|
~name=name ++ "Policy",
|
|
168
166
|
~args={
|
|
@@ -226,20 +224,19 @@ let finish = (
|
|
|
226
224
|
|
|
227
225
|
// `UPLOAD_STORES` — the same qualified-name → {bucket, prefix} shape the
|
|
228
226
|
// presign service reads, so mint and claim resolve a store identically.
|
|
229
|
-
let uploadStoresJson =
|
|
230
|
-
stores
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
)
|
|
227
|
+
let uploadStoresJson = stores->Pulumi.Output.apply(stores =>
|
|
228
|
+
stores
|
|
229
|
+
->Array.map(s => (
|
|
230
|
+
s.qualified,
|
|
231
|
+
Dict.fromArray([
|
|
232
|
+
("bucket", JSON.Encode.string(s.bucketName)),
|
|
233
|
+
("prefix", JSON.Encode.string(s.servedPrefix)),
|
|
234
|
+
])->JSON.Encode.object,
|
|
235
|
+
))
|
|
236
|
+
->Dict.fromArray
|
|
237
|
+
->JSON.Encode.object
|
|
238
|
+
->JSON.stringify
|
|
239
|
+
)
|
|
243
240
|
|
|
244
241
|
// Compiled EntryPoint rather than a serialized closure, for the reason the
|
|
245
242
|
// presign service is one: the handler reaches the AWS SDK v3 S3 client, and
|
|
@@ -359,8 +356,8 @@ let finish = (
|
|
|
359
356
|
->Pulumi.Output.asInput,
|
|
360
357
|
treatMissingData: "notBreaching"->Pulumi.Input.make,
|
|
361
358
|
alarmDescription: (`${name} is behind on committed events. While it is behind, objects an ` ++
|
|
362
|
-
`event already references still carry the pending tag — and a store with an expiry rule ` ++
|
|
363
|
-
|
|
359
|
+
`event already references still carry the pending tag — and a store with an expiry rule ` ++ `enabled will delete them.`)
|
|
360
|
+
->Pulumi.Input.make,
|
|
364
361
|
tags: AWS.Tags.make(
|
|
365
362
|
~name=name ++ "Lag",
|
|
366
363
|
~kind=ReventlessCore.ComponentType.Plugin,
|
|
@@ -236,7 +236,9 @@ type event = {@as("Records") records: array<record>}
|
|
|
236
236
|
in it. */
|
|
237
237
|
let targetsOfRecord = (record: record): array<target> =>
|
|
238
238
|
switch (
|
|
239
|
-
record.eventSourceARN
|
|
239
|
+
record.eventSourceARN
|
|
240
|
+
->tableNameFromEventSourceArn
|
|
241
|
+
->Option.flatMap(t => refFieldsByTable->Dict.get(t)),
|
|
240
242
|
record.dynamodb->Option.flatMap(d => d.newImage),
|
|
241
243
|
) {
|
|
242
244
|
| (Some(byEventType), Some(image)) =>
|
|
@@ -269,7 +271,8 @@ let targetsOfRecord = (record: record): array<target> =>
|
|
|
269
271
|
let handler = async (event: event): unit => {
|
|
270
272
|
// An append-only log only ever inserts events; MODIFY rows are fence and
|
|
271
273
|
// snapshot updates, which `targetsOfRecord` would drop anyway.
|
|
272
|
-
let targets =
|
|
274
|
+
let targets =
|
|
275
|
+
event.records->Array.filter(r => r.eventName == "INSERT")->Array.flatMap(targetsOfRecord)
|
|
273
276
|
// Sequential: a batch carries a handful of refs at most, and a failure must
|
|
274
277
|
// reach the ESM as a throw so the record is retried rather than silently
|
|
275
278
|
// leaving an object tagged.
|
|
@@ -111,58 +111,60 @@ let make = (
|
|
|
111
111
|
// presigned request runs with the *signer's* permissions, so tag-on-put fails
|
|
112
112
|
// with AccessDenied without it — and an untagged object is one the sweep can
|
|
113
113
|
// never reach.
|
|
114
|
-
let _policy =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
114
|
+
let _policy = resolvedStores->Pulumi.Output.apply(list => {
|
|
115
|
+
let arns = list->Array.map(((_, bucket, prefix)) => `arn:aws:s3:::${bucket}/${prefix}/*`)
|
|
116
|
+
let _ = IAM.RolePolicy.make(
|
|
117
|
+
~name=name ++ "LambdaPolicy",
|
|
118
|
+
~args={
|
|
119
|
+
policy: PolicyDocument.make(
|
|
120
|
+
~id=name ++ "LambdaPolicy",
|
|
121
|
+
~statements=[
|
|
122
|
+
{
|
|
123
|
+
sid: "AllowLambdaLogging",
|
|
124
|
+
effect: Allow,
|
|
125
|
+
actions: Actions([
|
|
126
|
+
"logs:CreateLogGroup",
|
|
127
|
+
"logs:CreateLogStream",
|
|
128
|
+
"logs:PutLogEvents",
|
|
129
|
+
]),
|
|
130
|
+
resources: Resource("arn:aws:logs:*:*:*"),
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
sid: "AllowUploadObjectAccess",
|
|
134
|
+
effect: Allow,
|
|
135
|
+
actions: Actions([
|
|
136
|
+
"s3:PutObject",
|
|
137
|
+
"s3:PutObjectTagging",
|
|
138
|
+
"s3:DeleteObject",
|
|
139
|
+
"s3:GetObject",
|
|
140
|
+
]),
|
|
141
|
+
resources: Resources(arns),
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
)
|
|
145
|
+
->PolicyDocument.toJsonString
|
|
146
|
+
->Pulumi.Input.make,
|
|
147
|
+
role: lambdaRole.id->Pulumi.Output.asInput,
|
|
148
|
+
},
|
|
149
|
+
~opts,
|
|
150
|
+
)
|
|
151
|
+
})
|
|
149
152
|
|
|
150
153
|
// `UPLOAD_STORES` — the qualified-name → {bucket, prefix} map the handler resolves
|
|
151
154
|
// the caller's `store` argument against.
|
|
152
|
-
let uploadStoresJson =
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
)
|
|
155
|
+
let uploadStoresJson = resolvedStores->Pulumi.Output.apply(list =>
|
|
156
|
+
list
|
|
157
|
+
->Array.map(((qualified, bucket, prefix)) => (
|
|
158
|
+
qualified,
|
|
159
|
+
Dict.fromArray([
|
|
160
|
+
("bucket", JSON.Encode.string(bucket)),
|
|
161
|
+
("prefix", JSON.Encode.string(prefix)),
|
|
162
|
+
])->JSON.Encode.object,
|
|
163
|
+
))
|
|
164
|
+
->Dict.fromArray
|
|
165
|
+
->JSON.Encode.object
|
|
166
|
+
->JSON.stringify
|
|
167
|
+
)
|
|
166
168
|
|
|
167
169
|
// Bundle reventless-aws (the compiled `_Ops` handler lives inside it) and
|
|
168
170
|
// re-export its `handler`; buildCodeArchive also ships the ESM resolve-hook.
|