@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
|
@@ -120,8 +120,7 @@ let alarmFor = (
|
|
|
120
120
|
// also where the log group goes — an alert names the unit and the metric and
|
|
121
121
|
// never where to read what happened. `~logLocator` is an Output, so the whole
|
|
122
122
|
// description resolves late; `""` is a unit with no logs of its own.
|
|
123
|
-
let describe = logs =>
|
|
124
|
-
Util_AlarmSpec.description(~kind, ~name, ~plugin, ~platform, ~spec, ~logs)
|
|
123
|
+
let describe = logs => Util_AlarmSpec.description(~kind, ~name, ~plugin, ~platform, ~spec, ~logs)
|
|
125
124
|
let alarmDescription =
|
|
126
125
|
logLocator
|
|
127
126
|
->Pulumi.Output.map(g => describe(g == "" ? None : Some(g)))
|
|
@@ -172,20 +171,21 @@ module Backend: M.Backend = {
|
|
|
172
171
|
switch ensureTopicArn() {
|
|
173
172
|
| Unresolved | NoTopic => ()
|
|
174
173
|
| Topic(topicArn) =>
|
|
175
|
-
Util_AlarmSpec.forKind(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
174
|
+
Util_AlarmSpec.forKind(
|
|
175
|
+
~kind,
|
|
176
|
+
~silenceWindowSeconds=silenceWindowSeconds(),
|
|
177
|
+
)->Array.forEach(spec => {
|
|
178
|
+
let _alarm = alarmFor(
|
|
179
|
+
~spec,
|
|
180
|
+
~kind,
|
|
181
|
+
~name,
|
|
182
|
+
~component,
|
|
183
|
+
~plugin,
|
|
184
|
+
~platform,
|
|
185
|
+
~logLocator,
|
|
186
|
+
~topicArn,
|
|
187
|
+
)
|
|
188
|
+
})
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
}
|
|
@@ -89,14 +89,10 @@ let make = (
|
|
|
89
89
|
|
|
90
90
|
let vpcConfig =
|
|
91
91
|
securityGroupId
|
|
92
|
-
->Pulumi.Output.apply(sgId =>
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
|
|
97
|
-
}: Lambda.Function.vpcConfig
|
|
98
|
-
)
|
|
99
|
-
)
|
|
92
|
+
->Pulumi.Output.apply((sgId): Lambda.Function.vpcConfig => {
|
|
93
|
+
Lambda.Function.subnetIds: subnetIds->Pulumi.Input.make,
|
|
94
|
+
securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
|
|
95
|
+
})
|
|
100
96
|
->Pulumi.Output.asInput
|
|
101
97
|
|
|
102
98
|
let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
|
|
@@ -111,8 +107,7 @@ let make = (
|
|
|
111
107
|
)
|
|
112
108
|
|
|
113
109
|
// IAM: GetSecretValue on each log's DB secret + SendMessage on each target queue.
|
|
114
|
-
let secretArns =
|
|
115
|
-
logs->Array.map(l => l.connectionConfig->Pulumi.Output.apply(cc => cc.secretArn))
|
|
110
|
+
let secretArns = logs->Array.map(l => l.connectionConfig->Pulumi.Output.apply(cc => cc.secretArn))
|
|
116
111
|
let queueArns = logs->Array.map(l => l.targetQueueArn)
|
|
117
112
|
let _iam =
|
|
118
113
|
(Pulumi.Output.all(secretArns), Pulumi.Output.all(queueArns), runtime.parts.lambdaRole.id)
|
|
@@ -25,8 +25,10 @@ let toEventCollectorJson = (
|
|
|
25
25
|
let item = Dict.make()
|
|
26
26
|
item->Dict.set(
|
|
27
27
|
"id",
|
|
28
|
-
DcbEventLogStorage_DynamoDb_Runtime.derivePartitionKey(
|
|
29
|
-
|
|
28
|
+
DcbEventLogStorage_DynamoDb_Runtime.derivePartitionKey(
|
|
29
|
+
~partitionTag?,
|
|
30
|
+
event.tags,
|
|
31
|
+
)->JSON.Encode.string,
|
|
30
32
|
)
|
|
31
33
|
item->Dict.set("position", event.position->JSON.Encode.string)
|
|
32
34
|
item->Dict.set("event", event.eventType->JSON.Encode.string)
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
|
|
20
20
|
Aurora / Aurora Serverless v2 is a planned second engine behind the same
|
|
21
21
|
`connectionConfig` output shape (tracked in the AWS-Postgres plan). */
|
|
22
|
-
|
|
23
22
|
/** Resolved at deploy time, serialized into the handler Lambda env, and consumed
|
|
24
23
|
at cold start to build a `PgDriver` pool. `secretArn` points at the
|
|
25
24
|
RDS-managed `{username, password}` secret; `host`/`port`/`database` come from
|
|
@@ -54,7 +53,8 @@ let connectionConfigToJson = (
|
|
|
54
53
|
("secretArn", cc.secretArn->JSON.Encode.string),
|
|
55
54
|
]
|
|
56
55
|
switch lockStrategy {
|
|
57
|
-
| Some(#AdvisoryLocks) =>
|
|
56
|
+
| Some(#AdvisoryLocks) =>
|
|
57
|
+
fields->Array.push(("lockStrategy", "AdvisoryLocks"->JSON.Encode.string))
|
|
58
58
|
| Some(#RowLocks) => fields->Array.push(("lockStrategy", "RowLocks"->JSON.Encode.string))
|
|
59
59
|
| None => ()
|
|
60
60
|
}
|
|
@@ -195,9 +195,10 @@ let make = (
|
|
|
195
195
|
// instance's so the B-phase storage adapters can order after the schema exists.
|
|
196
196
|
// The connection is passed as pre-serialized pieces to avoid a module cycle
|
|
197
197
|
// (PgMigration_Builder must not reference this module's `connectionConfig`).
|
|
198
|
-
let migrationHandlerConfig =
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
let migrationHandlerConfig =
|
|
199
|
+
connectionConfig->Pulumi.Output.apply(cc =>
|
|
200
|
+
`{"pgConnection":${cc->connectionConfigToJson->JSON.stringify}}`
|
|
201
|
+
)
|
|
201
202
|
let migrationResources = PgMigration_Builder.make(
|
|
202
203
|
~name=`${name}-pg-migrate`,
|
|
203
204
|
~handlerConfig=migrationHandlerConfig,
|
|
@@ -41,14 +41,10 @@ let make = (
|
|
|
41
41
|
|
|
42
42
|
let vpcConfig =
|
|
43
43
|
securityGroupId
|
|
44
|
-
->Pulumi.Output.apply(sgId =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
|
|
49
|
-
}: Lambda.Function.vpcConfig
|
|
50
|
-
)
|
|
51
|
-
)
|
|
44
|
+
->Pulumi.Output.apply((sgId): Lambda.Function.vpcConfig => {
|
|
45
|
+
Lambda.Function.subnetIds: subnetIds->Pulumi.Input.make,
|
|
46
|
+
securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
|
|
47
|
+
})
|
|
52
48
|
->Pulumi.Output.asInput
|
|
53
49
|
|
|
54
50
|
let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
|
|
@@ -10,15 +10,13 @@
|
|
|
10
10
|
(as a promise) to avoid a Secrets Manager round trip on every connection; a
|
|
11
11
|
password rotation surfaces as auth failures that recycle the container, after
|
|
12
12
|
which the next cold start re-fetches. */
|
|
13
|
-
|
|
14
13
|
/** RDS-managed master secret payload (`{username, password}` JSON). We only need
|
|
15
14
|
the password here — the username is carried on `connectionConfig` (deploy-time
|
|
16
15
|
known) so the pool can be constructed without first awaiting the secret. */
|
|
17
16
|
let passwordFromSecret = async (secretArn: string): string => {
|
|
18
|
-
let out =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
->AwsSdk.SecretsManager.GetSecretValueCommand.send
|
|
17
|
+
let out = await {AwsSdk.SecretsManager.GetSecretValueCommand.secretId: secretArn}
|
|
18
|
+
->AwsSdk.SecretsManager.GetSecretValueCommand.make
|
|
19
|
+
->AwsSdk.SecretsManager.GetSecretValueCommand.send
|
|
22
20
|
switch out.secretString {
|
|
23
21
|
| Some(str) =>
|
|
24
22
|
switch str->JSON.parseOrThrow->JSON.Decode.object {
|
|
@@ -29,8 +27,7 @@ let passwordFromSecret = async (secretArn: string): string => {
|
|
|
29
27
|
}
|
|
30
28
|
| None => JsError.throwWithMessage("Secrets Manager secret is not a JSON object")
|
|
31
29
|
}
|
|
32
|
-
| None =>
|
|
33
|
-
JsError.throwWithMessage(`Secrets Manager secret ${secretArn} has no SecretString`)
|
|
30
|
+
| None => JsError.throwWithMessage(`Secrets Manager secret ${secretArn} has no SecretString`)
|
|
34
31
|
}
|
|
35
32
|
}
|
|
36
33
|
|
|
@@ -41,8 +41,7 @@ type resolverEntry = {
|
|
|
41
41
|
includeIdParam: bool,
|
|
42
42
|
}
|
|
43
43
|
let entries: dict<resolverEntry> = Dict.make()
|
|
44
|
-
let register = (entry: resolverEntry): unit =>
|
|
45
|
-
entries->Dict.set(entry.readModelName, entry)
|
|
44
|
+
let register = (entry: resolverEntry): unit => entries->Dict.set(entry.readModelName, entry)
|
|
46
45
|
|
|
47
46
|
// Relay node type → read-model-name, populated per RM by QueryDbResolvers_Lambda.
|
|
48
47
|
// Baked into the env config so the entry point can serve the shared node(id).
|
|
@@ -138,14 +137,10 @@ let provision = (
|
|
|
138
137
|
// In-VPC on the DB-access security group + private subnets (reach RDS).
|
|
139
138
|
let vpcConfig =
|
|
140
139
|
selection.securityGroupId
|
|
141
|
-
->Pulumi.Output.apply(sgId =>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
|
|
146
|
-
}: Lambda.Function.vpcConfig
|
|
147
|
-
)
|
|
148
|
-
)
|
|
140
|
+
->Pulumi.Output.apply((sgId): Lambda.Function.vpcConfig => {
|
|
141
|
+
Lambda.Function.subnetIds: selection.subnetIds->Pulumi.Input.make,
|
|
142
|
+
securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
|
|
143
|
+
})
|
|
149
144
|
->Pulumi.Output.asInput
|
|
150
145
|
|
|
151
146
|
let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
|
|
@@ -239,6 +234,9 @@ let provision = (
|
|
|
239
234
|
|
|
240
235
|
// Fulfil the deferred name the Postgres storage maker handed to resolvers.
|
|
241
236
|
let _ = dataSource.name->Pulumi.Output.apply(n => resolveDataSourceName.contents(n))
|
|
242
|
-
log.info(
|
|
237
|
+
log.info(
|
|
238
|
+
~comp="PgQueryResolver_Builder",
|
|
239
|
+
`provisioned for ${handlers->Array.length->Int.toString} read model(s)`,
|
|
240
|
+
)
|
|
243
241
|
}
|
|
244
242
|
}
|
|
@@ -53,7 +53,8 @@ type payload = {
|
|
|
53
53
|
// "Owner" → "ownerId" (auth-table owner field name, matching authorizeIndexedAccess).
|
|
54
54
|
let authIdField = (group: string): string =>
|
|
55
55
|
switch group->String.get(0) {
|
|
56
|
-
| Some(c) =>
|
|
56
|
+
| Some(c) =>
|
|
57
|
+
c->String.toLowerCase ++ group->String.slice(~start=1, ~end=group->String.length) ++ "Id"
|
|
57
58
|
| None => "Id"
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -110,8 +111,7 @@ type binding = {
|
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
// -- arg helpers -------------------------------------------------------------
|
|
113
|
-
let argObj = (args: JSON.t): dict<JSON.t> =>
|
|
114
|
-
args->JSON.Decode.object->Option.getOr(Dict.make())
|
|
114
|
+
let argObj = (args: JSON.t): dict<JSON.t> => args->JSON.Decode.object->Option.getOr(Dict.make())
|
|
115
115
|
let argStr = (args: JSON.t, key: string): option<string> =>
|
|
116
116
|
args->argObj->Dict.get(key)->Option.flatMap(JSON.Decode.string)
|
|
117
117
|
let argStrs = (args: JSON.t, key: string): array<string> =>
|
|
@@ -144,7 +144,10 @@ let emptyConnection = (): JSON.t =>
|
|
|
144
144
|
// Spec-level authorization then the (optional) user interceptor — mirrors
|
|
145
145
|
// QueryDbResolvers_GraphQL.runInterceptor. Denials return the kind's empty shape
|
|
146
146
|
// (the provider-agnostic resolver behaviour), not an error.
|
|
147
|
-
let runInterceptor = async (
|
|
147
|
+
let runInterceptor = async (
|
|
148
|
+
~binding,
|
|
149
|
+
~payload,
|
|
150
|
+
): ReventlessCore.QueryDb_Callback.interceptResult => {
|
|
148
151
|
if !Reventless.Authorization.isAllowed(binding.authorization, payload.identity) {
|
|
149
152
|
Deny("Forbidden")
|
|
150
153
|
} else {
|
|
@@ -187,8 +190,7 @@ let dispatch = async (
|
|
|
187
190
|
switch payload.identity->Reventless.OwnerScope.decide(~ownerField=binding.ownerField) {
|
|
188
191
|
| Unscoped => true
|
|
189
192
|
| RefuseOwned => false
|
|
190
|
-
| ScopeTo(field, required) =>
|
|
191
|
-
item->argStr(field)->Option.mapOr(false, v => v == required)
|
|
193
|
+
| ScopeTo(field, required) => item->argStr(field)->Option.mapOr(false, v => v == required)
|
|
192
194
|
}
|
|
193
195
|
// The caller's request to see the archive, honoured only where the
|
|
194
196
|
// classification says it counts.
|
|
@@ -254,9 +256,8 @@ let dispatch = async (
|
|
|
254
256
|
found->Array.length < ids->Array.length
|
|
255
257
|
? ids->Array.filterMap(ReventlessCore.Api_Ids.alternateKey)
|
|
256
258
|
: []
|
|
257
|
-
let extra =
|
|
258
|
-
? await binding.pushdowns.byIds(~readModelName=rm, missing)
|
|
259
|
-
: []
|
|
259
|
+
let extra =
|
|
260
|
+
missing->Array.length > 0 ? await binding.pushdowns.byIds(~readModelName=rm, missing) : []
|
|
260
261
|
JSON.Encode.array(
|
|
261
262
|
Array.concat(found, extra)->Array.filter(item => ownerAllows(item) && retiredAllows(item)),
|
|
262
263
|
)
|
|
@@ -272,9 +273,8 @@ let dispatch = async (
|
|
|
272
273
|
found->Array.length < ids->Array.length
|
|
273
274
|
? ids->Array.filterMap(ReventlessCore.Api_Ids.alternateKey)
|
|
274
275
|
: []
|
|
275
|
-
let extra =
|
|
276
|
-
? await binding.pushdowns.byIds(~readModelName=rm, missing)
|
|
277
|
-
: []
|
|
276
|
+
let extra =
|
|
277
|
+
missing->Array.length > 0 ? await binding.pushdowns.byIds(~readModelName=rm, missing) : []
|
|
278
278
|
let retirementOf = (item: JSON.t) =>
|
|
279
279
|
switch binding.retiredField {
|
|
280
280
|
| None => (false, None)
|
|
@@ -386,9 +386,9 @@ let dispatch = async (
|
|
|
386
386
|
}
|
|
387
387
|
if authorized {
|
|
388
388
|
JSON.Encode.array(
|
|
389
|
-
(
|
|
390
|
-
|
|
391
|
-
),
|
|
389
|
+
(
|
|
390
|
+
await binding.pushdowns.indexLookup(~readModelName=rm, field, value)
|
|
391
|
+
)->Array.filter(item => ownerAllows(item) && retiredAllows(item)),
|
|
392
392
|
)
|
|
393
393
|
} else {
|
|
394
394
|
JSON.Encode.array([])
|
|
@@ -448,11 +448,10 @@ let dispatch = async (
|
|
|
448
448
|
// Optional target sort-key filter (source field or GraphQL arg).
|
|
449
449
|
let filtered = switch (payload.sourceSubId, binding.subIdField) {
|
|
450
450
|
| (Some({kind, name}), Some(subField)) =>
|
|
451
|
-
let subVal =
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}->Option.getOr("")
|
|
451
|
+
let subVal = switch kind {
|
|
452
|
+
| "arg" => payload.arguments->argStr(name)
|
|
453
|
+
| _ => source->argStr(name)
|
|
454
|
+
}->Option.getOr("")
|
|
456
455
|
items->Array.filter(it => it->argStr(subField)->Option.getOr("") == subVal)
|
|
457
456
|
| _ => items
|
|
458
457
|
}
|
|
@@ -555,7 +554,9 @@ let handler = async (payload: payload, _context) => {
|
|
|
555
554
|
| Some(binding) =>
|
|
556
555
|
await dispatch(~binding, ~lookupBinding=name => bindings->Dict.get(name), ~payload)
|
|
557
556
|
| None =>
|
|
558
|
-
JsError.throwWithMessage(
|
|
557
|
+
JsError.throwWithMessage(
|
|
558
|
+
"PgQueryResolver: no binding registered for read model " ++ bindingKey,
|
|
559
|
+
)
|
|
559
560
|
}
|
|
560
561
|
}
|
|
561
562
|
}
|
|
@@ -250,7 +250,8 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
250
250
|
// SDL the in-memory adapter does, so the AWS Filter / OrderBy stays in lockstep
|
|
251
251
|
// with the SDL emitted by GraphQL_FragmentGenerator at runtime.
|
|
252
252
|
let capability = switch stateSchemaOpt {
|
|
253
|
-
| Some(s) =>
|
|
253
|
+
| Some(s) =>
|
|
254
|
+
ReventlessCore.GraphQL_FragmentGenerator.deriveServerCapability(~entityName=name, s)
|
|
254
255
|
| None => ReventlessCore.GraphQL_FragmentGenerator.emptyCapability
|
|
255
256
|
}
|
|
256
257
|
let filterFieldNames = capability.filterFields->Array.map(f => f.name)
|
|
@@ -262,11 +263,10 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
262
263
|
// JS-runtime per-page sort over a full Scan (expensive in production).
|
|
263
264
|
switch stateSchemaOpt {
|
|
264
265
|
| Some(s) =>
|
|
265
|
-
let knownSortFields =
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}->Array.concat(indexes->Array.filterMap(({subIdField: ?sf}) => sf))
|
|
266
|
+
let knownSortFields = switch subIdField {
|
|
267
|
+
| Some(f) => [f]
|
|
268
|
+
| None => []
|
|
269
|
+
}->Array.concat(indexes->Array.filterMap(({subIdField: ?sf}) => sf))
|
|
270
270
|
ReventlessCore.GraphQL_FragmentGenerator.validateScanSortAlignment(
|
|
271
271
|
~schema=s,
|
|
272
272
|
~readModelName=name,
|
|
@@ -314,8 +314,7 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
314
314
|
`${name}: @owner field "${f}" keys no index on this table, so owner-scoped ` ++
|
|
315
315
|
"reads Scan the table and filter after the page is read — cost grows with the " ++
|
|
316
316
|
"table while the answer shrinks with the caller's share of it. Drop " ++
|
|
317
|
-
"`@owner({index: false})` to let the framework derive the index, or accept " ++
|
|
318
|
-
"the cost on a view that stays small.",
|
|
317
|
+
"`@owner({index: false})` to let the framework derive the index, or accept " ++ "the cost on a view that stays small.",
|
|
319
318
|
)
|
|
320
319
|
| _ => ()
|
|
321
320
|
}
|
|
@@ -347,8 +346,7 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
347
346
|
~comp="QueryDbResolvers_AppSync",
|
|
348
347
|
`${name}: @retired field "${f}" is filtered after the page is read, so pages ` ++
|
|
349
348
|
`shrink as the archive's share of the rows grows. This view is scoped on "${o}", ` ++
|
|
350
|
-
"so a scoped caller only sifts their own partition and pays in proportion to " ++
|
|
351
|
-
"their rows; it is the elevated whole-table read that pays in full.",
|
|
349
|
+
"so a scoped caller only sifts their own partition and pays in proportion to " ++ "their rows; it is the elevated whole-table read that pays in full.",
|
|
352
350
|
)
|
|
353
351
|
| (Some(f), None) =>
|
|
354
352
|
log.warn(
|
|
@@ -356,8 +354,7 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
356
354
|
`${name}: @retired field "${f}" is filtered after the page is read, so pages ` ++
|
|
357
355
|
"shrink as the archive's share of the rows grows. An @index on that field " ++
|
|
358
356
|
"would not help — the list read does not use one, and a flag makes a poor " ++
|
|
359
|
-
"partition key. Reshape the view so the rows you serve are the rows you " ++
|
|
360
|
-
"keep, or accept the cost while the archive stays small.",
|
|
357
|
+
"partition key. Reshape the view so the rows you serve are the rows you " ++ "keep, or accept the cost while the archive stays small.",
|
|
361
358
|
)
|
|
362
359
|
| (None, _) => ()
|
|
363
360
|
}
|
|
@@ -385,108 +382,109 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
385
382
|
// Derived indexes are absent from the SDL (`GraphQL_FragmentGenerator` skips
|
|
386
383
|
// them), so a resolver here would attach to a field that does not exist and
|
|
387
384
|
// fail the deploy. The list resolver above is the only thing that reads one.
|
|
388
|
-
let resolversByIndex =
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
385
|
+
let resolversByIndex =
|
|
386
|
+
indexes
|
|
387
|
+
->Array.filter(ic => !Reventless.ReadModel.isDerivedIndex(ic))
|
|
388
|
+
->Array.map(({index} as indexConfig) => {
|
|
389
|
+
// Name and key field both come from `GraphQL_FragmentGenerator`, which is
|
|
390
|
+
// where the SDL field this attaches to is derived. Deriving them here as
|
|
391
|
+
// well is how the two came to disagree: the emitted field declared `id`
|
|
392
|
+
// while this resolver read the index key, so the door could not be called.
|
|
393
|
+
let fieldName = ReventlessCore.GraphQL_FragmentGenerator.indexQueryFieldName(
|
|
394
|
+
~singleFieldName=fieldNameForSingle,
|
|
395
|
+
~index,
|
|
396
|
+
)
|
|
397
|
+
let resolverName = fieldName->String.capitalize
|
|
398
|
+
let idField = ReventlessCore.GraphQL_FragmentGenerator.indexKeyField(indexConfig)
|
|
399
|
+
switch indexConfig.authorization {
|
|
400
|
+
| None =>
|
|
401
|
+
makeQueryResolver(
|
|
402
|
+
~resolverName,
|
|
403
|
+
~field=fieldName->Pulumi.Input.make,
|
|
404
|
+
~code=switch indexConfig.subIdField {
|
|
405
|
+
| Some(sortField) =>
|
|
406
|
+
Resolver.Functions.queryByIndexSortFiltered(
|
|
407
|
+
~index,
|
|
408
|
+
~idField,
|
|
409
|
+
~sortField,
|
|
410
|
+
~ownerField?,
|
|
411
|
+
~retiredField?,
|
|
412
|
+
~retiredValues?,
|
|
413
|
+
~elevatedGroups,
|
|
414
|
+
)
|
|
415
|
+
| None =>
|
|
416
|
+
Resolver.Functions.queryByIndexFiltered(
|
|
417
|
+
~index,
|
|
418
|
+
~idField,
|
|
419
|
+
~ownerField?,
|
|
420
|
+
~retiredField?,
|
|
421
|
+
~retiredValues?,
|
|
422
|
+
~elevatedGroups,
|
|
423
|
+
)
|
|
424
|
+
},
|
|
425
|
+
)
|
|
426
|
+
| Some({tableName, group}) =>
|
|
427
|
+
let authDataSource = DataSource.makeDynamoDBDataSourceWithTableName(
|
|
428
|
+
~name=resolverName ++ "Auth",
|
|
429
|
+
~api,
|
|
430
|
+
~tableName=(
|
|
431
|
+
allQueryDbs
|
|
432
|
+
->ReventlessCore.Util.QueryDb.getLocalStorageResources(tableName)
|
|
433
|
+
->Util.DynamoDb.findResource
|
|
434
|
+
).name,
|
|
435
|
+
~serviceRole=apiRole,
|
|
436
|
+
~opts,
|
|
437
|
+
)
|
|
438
|
+
let authFunction = Function.makeJs(
|
|
439
|
+
~name=resolverName ++ "Auth",
|
|
440
|
+
~api,
|
|
441
|
+
~dataSource=authDataSource.name->Pulumi.Output.asInput,
|
|
442
|
+
~code=Resolver.Functions.authorizeIndexedAccess(~index, ~group),
|
|
443
|
+
~opts,
|
|
444
|
+
)
|
|
445
|
+
let queryFunction = Function.makeJs(
|
|
446
|
+
~name=resolverName,
|
|
447
|
+
~api,
|
|
448
|
+
~dataSource=dataSourceName,
|
|
449
|
+
// No `~ownerField`, deliberately: this index declares its own
|
|
450
|
+
// authorization, and the rows it grants a group member are by
|
|
451
|
+
// construction owned by somebody else — an order assigned to a
|
|
452
|
+
// fulfilment operator belongs to the customer who placed it. Adding the
|
|
453
|
+
// owner predicate here would return nothing and revoke the access the
|
|
454
|
+
// auth table exists to grant. Retirement still applies: an archived row
|
|
455
|
+
// is withdrawn from everyone who has not asked, whoever owns it.
|
|
456
|
+
~code=Resolver.Functions.queryByIndexFiltered(
|
|
419
457
|
~index,
|
|
420
458
|
~idField,
|
|
421
|
-
~ownerField?,
|
|
422
459
|
~retiredField?,
|
|
423
460
|
~retiredValues?,
|
|
424
461
|
~elevatedGroups,
|
|
425
|
-
)
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
// No `~ownerField`, deliberately: this index declares its own
|
|
452
|
-
// authorization, and the rows it grants a group member are by
|
|
453
|
-
// construction owned by somebody else — an order assigned to a
|
|
454
|
-
// fulfilment operator belongs to the customer who placed it. Adding the
|
|
455
|
-
// owner predicate here would return nothing and revoke the access the
|
|
456
|
-
// auth table exists to grant. Retirement still applies: an archived row
|
|
457
|
-
// is withdrawn from everyone who has not asked, whoever owns it.
|
|
458
|
-
~code=Resolver.Functions.queryByIndexFiltered(
|
|
459
|
-
~index,
|
|
460
|
-
~idField,
|
|
461
|
-
~retiredField?,
|
|
462
|
-
~retiredValues?,
|
|
463
|
-
~elevatedGroups,
|
|
464
|
-
),
|
|
465
|
-
~opts,
|
|
466
|
-
)
|
|
467
|
-
// The interceptor leads the chain, as it does in every other Query
|
|
468
|
-
// pipeline. That position means an attempt refused by the row-level index
|
|
469
|
-
// authorization below has still been counted — the same "attempts, not
|
|
470
|
-
// outcomes" bound the hook has everywhere, since it fires before the read
|
|
471
|
-
// and never learns how it ended. Putting it after the auth step would buy
|
|
472
|
-
// outcome-accurate counts and cost the property that matters more: the
|
|
473
|
-
// interceptor is the one place a read can be REFUSED, so it has to be
|
|
474
|
-
// reachable before the pipeline spends a second table read deciding
|
|
475
|
-
// whether this caller may use the index.
|
|
476
|
-
Resolver.makePipelineJsResolver(
|
|
477
|
-
~name=resolverName,
|
|
478
|
-
~api,
|
|
479
|
-
~type_="Query"->Pulumi.Input.make,
|
|
480
|
-
~field=fieldName->Pulumi.Input.make,
|
|
481
|
-
~code=Resolver.Functions.pipelinePassThrough,
|
|
482
|
-
~functions=switch interceptorFunction(~resolverName) {
|
|
483
|
-
| None => [authFunction, queryFunction]
|
|
484
|
-
| Some(interceptorFn) => [interceptorFn, authFunction, queryFunction]
|
|
485
|
-
},
|
|
486
|
-
~opts,
|
|
487
|
-
)
|
|
488
|
-
}
|
|
489
|
-
})
|
|
462
|
+
),
|
|
463
|
+
~opts,
|
|
464
|
+
)
|
|
465
|
+
// The interceptor leads the chain, as it does in every other Query
|
|
466
|
+
// pipeline. That position means an attempt refused by the row-level index
|
|
467
|
+
// authorization below has still been counted — the same "attempts, not
|
|
468
|
+
// outcomes" bound the hook has everywhere, since it fires before the read
|
|
469
|
+
// and never learns how it ended. Putting it after the auth step would buy
|
|
470
|
+
// outcome-accurate counts and cost the property that matters more: the
|
|
471
|
+
// interceptor is the one place a read can be REFUSED, so it has to be
|
|
472
|
+
// reachable before the pipeline spends a second table read deciding
|
|
473
|
+
// whether this caller may use the index.
|
|
474
|
+
Resolver.makePipelineJsResolver(
|
|
475
|
+
~name=resolverName,
|
|
476
|
+
~api,
|
|
477
|
+
~type_="Query"->Pulumi.Input.make,
|
|
478
|
+
~field=fieldName->Pulumi.Input.make,
|
|
479
|
+
~code=Resolver.Functions.pipelinePassThrough,
|
|
480
|
+
~functions=switch interceptorFunction(~resolverName) {
|
|
481
|
+
| None => [authFunction, queryFunction]
|
|
482
|
+
| Some(interceptorFn) => [interceptorFn, authFunction, queryFunction]
|
|
483
|
+
},
|
|
484
|
+
~opts,
|
|
485
|
+
)
|
|
486
|
+
}
|
|
487
|
+
})
|
|
490
488
|
|
|
491
489
|
let storageResource = (~pluginName: option<string>, ~tableName: string) =>
|
|
492
490
|
allQueryDbs
|
|
@@ -711,8 +709,13 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
711
709
|
// is independent of whether this view has a sub-id door, and adding a third
|
|
712
710
|
// dimension to that match would spell eight cases to say one thing.
|
|
713
711
|
let refsResolvers = resolverRefs->Option.mapOr([], r => [r])
|
|
714
|
-
Array.flat([
|
|
715
|
-
|
|
712
|
+
Array.flat([
|
|
713
|
+
mainResolvers,
|
|
714
|
+
refsResolvers,
|
|
715
|
+
resolversByIndex,
|
|
716
|
+
idResolvers,
|
|
717
|
+
idsResolvers,
|
|
718
|
+
])->Array.map(Util.AppSync.toResourceNative)
|
|
716
719
|
}
|
|
717
720
|
|
|
718
721
|
{resources: [], resourcesMaker}
|
|
@@ -184,13 +184,7 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
184
184
|
// Batched-by-ids — single-key projections only (mirrors the SDL).
|
|
185
185
|
let byIds = if includeIdParam && subIdField === None {
|
|
186
186
|
let byIdsField = fieldNameForAll ++ "ByIds"
|
|
187
|
-
[
|
|
188
|
-
mkResolver(
|
|
189
|
-
~resolverName=byIdsField->String.capitalize,
|
|
190
|
-
~field=byIdsField,
|
|
191
|
-
~kind="byIds",
|
|
192
|
-
),
|
|
193
|
-
]
|
|
187
|
+
[mkResolver(~resolverName=byIdsField->String.capitalize, ~field=byIdsField, ~kind="byIds")]
|
|
194
188
|
} else {
|
|
195
189
|
[]
|
|
196
190
|
}
|
|
@@ -24,7 +24,8 @@ module Selectable = {
|
|
|
24
24
|
~ttl=?,
|
|
25
25
|
~api,
|
|
26
26
|
~apiRole,
|
|
27
|
-
~owner,
|
|
27
|
+
~owner,
|
|
28
|
+
~opts,
|
|
28
29
|
) =>
|
|
29
30
|
if QueryDbBackend.isPostgresFor(name) {
|
|
30
31
|
Postgres.make(~name, ~indexes, ~subIdField?, ~ttl?, ~api, ~apiRole, ~owner, ~opts)
|
|
@@ -43,7 +44,8 @@ module SelectableStream = {
|
|
|
43
44
|
~ttl=?,
|
|
44
45
|
~api,
|
|
45
46
|
~apiRole,
|
|
46
|
-
~owner,
|
|
47
|
+
~owner,
|
|
48
|
+
~opts,
|
|
47
49
|
) =>
|
|
48
50
|
if QueryDbBackend.isPostgresFor(name) {
|
|
49
51
|
QueryDbBackend.postgresStreamRegistry->Set.add(name)
|