@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
|
@@ -51,10 +51,7 @@ let describedPool = () =>
|
|
|
51
51
|
),
|
|
52
52
|
])->JSON.Encode.object,
|
|
53
53
|
),
|
|
54
|
-
(
|
|
55
|
-
"UserPoolTags",
|
|
56
|
-
Dict.fromArray([("CostCentre", str("identity"))])->JSON.Encode.object,
|
|
57
|
-
),
|
|
54
|
+
("UserPoolTags", Dict.fromArray([("CostCentre", str("identity"))])->JSON.Encode.object),
|
|
58
55
|
(
|
|
59
56
|
"LambdaConfig",
|
|
60
57
|
Dict.fromArray([
|
|
@@ -73,8 +70,7 @@ let merged = (~preTokenGenerationArn) =>
|
|
|
73
70
|
|
|
74
71
|
let triggerArn = "arn:aws:lambda:eu-west-1:1:function:ActiveRoleTrigger"
|
|
75
72
|
|
|
76
|
-
let lambdaConfigOf = input =>
|
|
77
|
-
input->Dict.get("LambdaConfig")->Option.flatMap(JSON.Decode.object)
|
|
73
|
+
let lambdaConfigOf = input => input->Dict.get("LambdaConfig")->Option.flatMap(JSON.Decode.object)
|
|
78
74
|
|
|
79
75
|
/** The `PreTokenGenerationConfig` an attach is expected to write: our ARN, at the
|
|
80
76
|
only version the trigger handler implements. */
|
|
@@ -159,10 +155,16 @@ describe("Auth_ActiveRolePoolAttachment.mergedUpdateInput — shaping for the AP
|
|
|
159
155
|
|
|
160
156
|
testSync("read-only fields UpdateUserPool rejects are not sent", () =>
|
|
161
157
|
expect(
|
|
162
|
-
[
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
158
|
+
[
|
|
159
|
+
"Id",
|
|
160
|
+
"Arn",
|
|
161
|
+
"Status",
|
|
162
|
+
"CreationDate",
|
|
163
|
+
"LastModifiedDate",
|
|
164
|
+
"SchemaAttributes",
|
|
165
|
+
"UsernameAttributes",
|
|
166
|
+
"EstimatedNumberOfUsers",
|
|
167
|
+
]->Array.filter(k => input->Dict.get(k)->Option.isSome),
|
|
166
168
|
)->toEqual([])
|
|
167
169
|
)
|
|
168
170
|
})
|
|
@@ -211,98 +213,102 @@ describe("Auth_ActiveRolePoolAttachment.mergedUpdateInput — a pool with nothin
|
|
|
211
213
|
// in two fields, and `UpdateUserPool` rejects the call when both are present
|
|
212
214
|
// naming different functions. Nothing in this suite carried a
|
|
213
215
|
// `PreTokenGenerationConfig` before, which is exactly why it shipped.
|
|
214
|
-
describe(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Dict.fromArray([
|
|
219
|
-
("Name", str("CustomerPool")),
|
|
220
|
-
(
|
|
221
|
-
"LambdaConfig",
|
|
222
|
-
Dict.fromArray([
|
|
223
|
-
("PreSignUp", str("arn:aws:lambda:eu-west-1:1:function:TheirPreSignUp")),
|
|
224
|
-
("PreTokenGeneration", str(theirArn)),
|
|
225
|
-
(
|
|
226
|
-
"PreTokenGenerationConfig",
|
|
227
|
-
Dict.fromArray([
|
|
228
|
-
("LambdaArn", str(theirArn)),
|
|
229
|
-
("LambdaVersion", str(version)),
|
|
230
|
-
])->JSON.Encode.object,
|
|
231
|
-
),
|
|
232
|
-
])->JSON.Encode.object,
|
|
233
|
-
),
|
|
234
|
-
])
|
|
235
|
-
|
|
236
|
-
let attachOnto = (~version) =>
|
|
237
|
-
Attachment.mergedUpdateInput(
|
|
238
|
-
~described=describedWithConfig(~version),
|
|
239
|
-
~userPoolId="eu-west-1_abc123",
|
|
240
|
-
~preTokenGenerationArn=Some(triggerArn),
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
// The property AWS enforces, asserted directly rather than through its error
|
|
244
|
-
// text: the two fields must name the same function.
|
|
245
|
-
testSync("both fields name our trigger, and the same one", () => {
|
|
246
|
-
let config = lambdaConfigOf(attachOnto(~version="V1_0"))
|
|
247
|
-
expect((
|
|
248
|
-
config->Option.flatMap(c => c->Dict.get("PreTokenGeneration")),
|
|
249
|
-
config
|
|
250
|
-
->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig"))
|
|
251
|
-
->Option.flatMap(JSON.Decode.object)
|
|
252
|
-
->Option.flatMap(c => c->Dict.get("LambdaArn")),
|
|
253
|
-
))->toEqual((Some(str(triggerArn)), Some(str(triggerArn))))
|
|
254
|
-
})
|
|
216
|
+
describe(
|
|
217
|
+
"Auth_ActiveRolePoolAttachment.mergedUpdateInput — a pool already on the V2 field",
|
|
218
|
+
() => {
|
|
219
|
+
let theirArn = "arn:aws:lambda:eu-west-1:1:function:TheirOwnPreToken"
|
|
255
220
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
221
|
+
let describedWithConfig = (~version) =>
|
|
222
|
+
Dict.fromArray([
|
|
223
|
+
("Name", str("CustomerPool")),
|
|
224
|
+
(
|
|
225
|
+
"LambdaConfig",
|
|
226
|
+
Dict.fromArray([
|
|
227
|
+
("PreSignUp", str("arn:aws:lambda:eu-west-1:1:function:TheirPreSignUp")),
|
|
228
|
+
("PreTokenGeneration", str(theirArn)),
|
|
229
|
+
(
|
|
230
|
+
"PreTokenGenerationConfig",
|
|
231
|
+
Dict.fromArray([
|
|
232
|
+
("LambdaArn", str(theirArn)),
|
|
233
|
+
("LambdaVersion", str(version)),
|
|
234
|
+
])->JSON.Encode.object,
|
|
235
|
+
),
|
|
236
|
+
])->JSON.Encode.object,
|
|
237
|
+
),
|
|
238
|
+
])
|
|
272
239
|
|
|
273
|
-
|
|
274
|
-
let config = lambdaConfigOf(
|
|
240
|
+
let attachOnto = (~version) =>
|
|
275
241
|
Attachment.mergedUpdateInput(
|
|
276
|
-
~described=describedWithConfig(~version
|
|
242
|
+
~described=describedWithConfig(~version),
|
|
277
243
|
~userPoolId="eu-west-1_abc123",
|
|
278
|
-
~preTokenGenerationArn=
|
|
279
|
-
)
|
|
244
|
+
~preTokenGenerationArn=Some(triggerArn),
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
// The property AWS enforces, asserted directly rather than through its error
|
|
248
|
+
// text: the two fields must name the same function.
|
|
249
|
+
testSync("both fields name our trigger, and the same one", () => {
|
|
250
|
+
let config = lambdaConfigOf(attachOnto(~version="V1_0"))
|
|
251
|
+
expect((
|
|
252
|
+
config->Option.flatMap(c => c->Dict.get("PreTokenGeneration")),
|
|
253
|
+
config
|
|
254
|
+
->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig"))
|
|
255
|
+
->Option.flatMap(JSON.Decode.object)
|
|
256
|
+
->Option.flatMap(c => c->Dict.get("LambdaArn")),
|
|
257
|
+
))->toEqual((Some(str(triggerArn)), Some(str(triggerArn))))
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
// A pool left on V2_0 does not fail — the handler answers in V1_0 shape and
|
|
261
|
+
// Cognito ignores it, so tokens mint un-narrowed and nothing reports it.
|
|
262
|
+
testSync("a pool that arrived on V2_0 is pinned back to the version we implement", () =>
|
|
263
|
+
expect(
|
|
264
|
+
lambdaConfigOf(attachOnto(~version="V2_0"))
|
|
265
|
+
->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig"))
|
|
266
|
+
->Option.flatMap(JSON.Decode.object)
|
|
267
|
+
->Option.flatMap(c => c->Dict.get("LambdaVersion")),
|
|
268
|
+
)->toEqual(Some(str("V1_0")))
|
|
280
269
|
)
|
|
281
|
-
expect((
|
|
282
|
-
config->Option.flatMap(c => c->Dict.get("PreTokenGeneration")),
|
|
283
|
-
config->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig")),
|
|
284
|
-
))->toEqual((None, None))
|
|
285
|
-
})
|
|
286
270
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
testSync("feeding the merge its own output back changes nothing", () => {
|
|
292
|
-
let once = attachOnto(~version="V1_0")
|
|
293
|
-
let twice = Attachment.mergedUpdateInput(
|
|
294
|
-
~described=once,
|
|
295
|
-
~userPoolId="eu-west-1_abc123",
|
|
296
|
-
~preTokenGenerationArn=Some(triggerArn),
|
|
271
|
+
testSync("the customer's other triggers still survive", () =>
|
|
272
|
+
expect(
|
|
273
|
+
lambdaConfigOf(attachOnto(~version="V1_0"))->Option.flatMap(c => c->Dict.get("PreSignUp")),
|
|
274
|
+
)->toEqual(Some(str("arn:aws:lambda:eu-west-1:1:function:TheirPreSignUp")))
|
|
297
275
|
)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
276
|
+
|
|
277
|
+
testSync("detaching removes both fields, not just the legacy one", () => {
|
|
278
|
+
let config = lambdaConfigOf(
|
|
279
|
+
Attachment.mergedUpdateInput(
|
|
280
|
+
~described=describedWithConfig(~version="V1_0"),
|
|
281
|
+
~userPoolId="eu-west-1_abc123",
|
|
282
|
+
~preTokenGenerationArn=None,
|
|
283
|
+
),
|
|
284
|
+
)
|
|
285
|
+
expect((
|
|
286
|
+
config->Option.flatMap(c => c->Dict.get("PreTokenGeneration")),
|
|
287
|
+
config->Option.flatMap(c => c->Dict.get("PreTokenGenerationConfig")),
|
|
288
|
+
))->toEqual((None, None))
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
// The fixpoint property, and the one that would have caught this: AWS
|
|
292
|
+
// materialises whichever field the merge did not write, so the merge has to
|
|
293
|
+
// accept its own output unchanged or a later deploy fails with nothing altered
|
|
294
|
+
// but the service normalising its own record.
|
|
295
|
+
testSync("feeding the merge its own output back changes nothing", () => {
|
|
296
|
+
let once = attachOnto(~version="V1_0")
|
|
297
|
+
let twice = Attachment.mergedUpdateInput(
|
|
298
|
+
~described=once,
|
|
299
|
+
~userPoolId="eu-west-1_abc123",
|
|
300
|
+
~preTokenGenerationArn=Some(triggerArn),
|
|
301
|
+
)
|
|
302
|
+
expect(lambdaConfigOf(twice))->toEqual(lambdaConfigOf(once))
|
|
303
|
+
})
|
|
304
|
+
},
|
|
305
|
+
)
|
|
301
306
|
|
|
302
307
|
describe("Auth_ActiveRolePoolAttachment.attachedTrigger", () => {
|
|
303
308
|
testSync("reports the trigger a described pool carries", () =>
|
|
304
|
-
expect(
|
|
305
|
-
|
|
309
|
+
expect(
|
|
310
|
+
Attachment.attachedTrigger(~described=merged(~preTokenGenerationArn=Some(triggerArn))),
|
|
311
|
+
)->toEqual(Some(triggerArn))
|
|
306
312
|
)
|
|
307
313
|
|
|
308
314
|
testSync("reports none when the pool carries no trigger", () =>
|
|
@@ -317,7 +323,9 @@ describe("Auth_ActiveRolePoolAttachment.attachedTrigger", () => {
|
|
|
317
323
|
~described=Dict.fromArray([
|
|
318
324
|
(
|
|
319
325
|
"LambdaConfig",
|
|
320
|
-
Dict.fromArray([
|
|
326
|
+
Dict.fromArray([
|
|
327
|
+
("PreTokenGenerationConfig", v1Config(triggerArn)),
|
|
328
|
+
])->JSON.Encode.object,
|
|
321
329
|
),
|
|
322
330
|
]),
|
|
323
331
|
),
|
|
@@ -439,9 +447,7 @@ describe("Auth_ActiveRolePoolAttachment.probeEvent", () => {
|
|
|
439
447
|
|
|
440
448
|
// Both halves of the key must miss every real row, not just the subject.
|
|
441
449
|
testSync("the probe's subject and client cannot collide with a real row", () =>
|
|
442
|
-
expect(
|
|
443
|
-
Attachment.probeSubject == Attachment.probeClientId,
|
|
444
|
-
)->toBe(false)
|
|
450
|
+
expect(Attachment.probeSubject == Attachment.probeClientId)->toBe(false)
|
|
445
451
|
)
|
|
446
452
|
})
|
|
447
453
|
|
|
@@ -470,8 +476,9 @@ describe("Auth_ActiveRolePoolAttachment.attachedTriggerArn", () => {
|
|
|
470
476
|
])
|
|
471
477
|
|
|
472
478
|
testSync("an empty pool holds nothing", () =>
|
|
473
|
-
expect(
|
|
474
|
-
|
|
479
|
+
expect(
|
|
480
|
+
Attachment.attachedTriggerArn(~described=Dict.fromArray([("Name", str("Bare"))])),
|
|
481
|
+
)->toEqual(None)
|
|
475
482
|
)
|
|
476
483
|
|
|
477
484
|
testSync("reports the trigger a pool carries", () =>
|
|
@@ -539,8 +546,9 @@ describe("Auth_ActiveRolePoolAttachment.classifySlot", () => {
|
|
|
539
546
|
// halves. Unreachable by configuration now that the store is derived — reachable
|
|
540
547
|
// by version skew, while an older release is still on its stack-scoped table.
|
|
541
548
|
testSync("another deployment's trigger on a different store is the defect", () =>
|
|
542
|
-
expect(
|
|
543
|
-
|
|
549
|
+
expect(
|
|
550
|
+
classify(~attachedArn=Some(theirs), ~attachedStore=Some("ActiveRoleStore-829c96f")),
|
|
551
|
+
)->toEqual(Attachment.DifferentStore({arn: theirs, theirStore: "ActiveRoleStore-829c96f"}))
|
|
544
552
|
)
|
|
545
553
|
|
|
546
554
|
// Reading the store rather than matching a name is what makes this a check on
|
|
@@ -568,13 +576,12 @@ describe("Auth_ActiveRolePoolAttachment.refusalFor", () => {
|
|
|
568
576
|
// Both stores named, because an operator cannot act on this without knowing
|
|
569
577
|
// which two are in disagreement.
|
|
570
578
|
testSync("a disagreeing store is refused, naming both stores", () => {
|
|
571
|
-
let message =
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
)->Option.getOr("")
|
|
579
|
+
let message = refusal(
|
|
580
|
+
Attachment.DifferentStore({
|
|
581
|
+
arn: "arn:aws:lambda:eu-west-1:1:function:Other",
|
|
582
|
+
theirStore: "ActiveRoleStore-829c96f",
|
|
583
|
+
}),
|
|
584
|
+
)->Option.getOr("")
|
|
578
585
|
expect((
|
|
579
586
|
message->String.includes(store),
|
|
580
587
|
message->String.includes("ActiveRoleStore-829c96f"),
|
|
@@ -18,8 +18,9 @@ describe("Auth_ActiveRoleStore_Ops.mayActAs — the conformance table", () => {
|
|
|
18
18
|
// path, exercised against the handler rather than the predicate.
|
|
19
19
|
| None => ()
|
|
20
20
|
| Some(role) =>
|
|
21
|
-
testSync(
|
|
22
|
-
|
|
21
|
+
testSync(
|
|
22
|
+
label,
|
|
23
|
+
() => expect(Ops.mayActAs(~membership, ~requested=role))->toBe(expected->Option.isSome),
|
|
23
24
|
)
|
|
24
25
|
}
|
|
25
26
|
)
|
|
@@ -48,7 +49,9 @@ describe("Auth_ActiveRoleStore_Ops.mayActAs — narrowing only", () => {
|
|
|
48
49
|
describe("Auth_ActiveRoleStore_Ops.cognitoLookupName", () => {
|
|
49
50
|
testSync("the username is what addresses Cognito when the authorizer sent one", () =>
|
|
50
51
|
expect(
|
|
51
|
-
Ops.cognitoLookupName(
|
|
52
|
+
Ops.cognitoLookupName(
|
|
53
|
+
~identity={sub: "d265d464-2091-706a-e5f9-3afafe7be29c", username: Value("merch")},
|
|
54
|
+
),
|
|
52
55
|
)->toEqual(Some("merch"))
|
|
53
56
|
)
|
|
54
57
|
|
|
@@ -85,10 +88,9 @@ describe("Auth_ActiveRoleStore.chooseStore", () => {
|
|
|
85
88
|
)
|
|
86
89
|
|
|
87
90
|
testSync("a stack given a provider reads that provider's derived store", () =>
|
|
88
|
-
expect(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
)
|
|
91
|
+
expect(
|
|
92
|
+
Auth_ActiveRoleStore.chooseStore(~identityProviderId=Some("eu-west-1_CQTwafSeX")),
|
|
93
|
+
)->toEqual(Auth_ActiveRoleStore.ProviderScoped("ReventlessActiveRoleStore-eu-west-1_CQTwafSeX"))
|
|
92
94
|
)
|
|
93
95
|
|
|
94
96
|
// 🚨 The defect, expressed as the property that now prevents it. Two platform
|
|
@@ -97,8 +99,9 @@ describe("Auth_ActiveRoleStore.chooseStore", () => {
|
|
|
97
99
|
// doing nothing. There is no configuration either could carry that would make
|
|
98
100
|
// them disagree.
|
|
99
101
|
testSync("two deployments on one provider cannot choose different stores", () =>
|
|
100
|
-
expect(Auth_ActiveRoleStore.chooseStore(~identityProviderId=Some("eu-west-1_Shared")))
|
|
101
|
-
|
|
102
|
+
expect(Auth_ActiveRoleStore.chooseStore(~identityProviderId=Some("eu-west-1_Shared")))->toEqual(
|
|
103
|
+
Auth_ActiveRoleStore.chooseStore(~identityProviderId=Some("eu-west-1_Shared")),
|
|
104
|
+
)
|
|
102
105
|
)
|
|
103
106
|
})
|
|
104
107
|
|
|
@@ -73,7 +73,8 @@ describe("Auth_ActiveRoleStore_Schema.keySchemaRefusal", () => {
|
|
|
73
73
|
)
|
|
74
74
|
|
|
75
75
|
testSync("the refusal names the table, what it found, and what is needed", () => {
|
|
76
|
-
let message =
|
|
76
|
+
let message =
|
|
77
|
+
Schema.keySchemaRefusal(~tableName=store, ~actual=[("id", "HASH")])->Option.getOr("")
|
|
77
78
|
expect((
|
|
78
79
|
message->String.includes(store),
|
|
79
80
|
message->String.includes("id:HASH"),
|
|
@@ -18,14 +18,13 @@ let eventFor = (
|
|
|
18
18
|
groupConfiguration: {
|
|
19
19
|
groupsToOverride: membership,
|
|
20
20
|
iamRolesToOverride: iamRoles,
|
|
21
|
-
preferredRole: ?preferredRole->Option.map(Nullable.make),
|
|
21
|
+
preferredRole: ?(preferredRole->Option.map(Nullable.make)),
|
|
22
22
|
},
|
|
23
23
|
},
|
|
24
24
|
userName: "carol",
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
let overrideOf = (e: Trigger.event) =>
|
|
28
|
-
e.response->Option.flatMap(r => r.claimsOverrideDetails)
|
|
27
|
+
let overrideOf = (e: Trigger.event) => e.response->Option.flatMap(r => r.claimsOverrideDetails)
|
|
29
28
|
|
|
30
29
|
let groupsOf = (e: Trigger.event) =>
|
|
31
30
|
overrideOf(e)->Option.flatMap(c => c.groupOverrideDetails)->Option.map(g => g.groupsToOverride)
|
|
@@ -51,18 +50,20 @@ let claimOf = (e: Trigger.event, key) =>
|
|
|
51
50
|
// the same token. That an absent override really is absent (rather than an empty
|
|
52
51
|
// one, which is not the same thing) is asserted separately below.
|
|
53
52
|
|
|
54
|
-
let effectiveGroups = (e: Trigger.event, ~membership) =>
|
|
55
|
-
groupsOf(e)->Option.getOr(membership)
|
|
53
|
+
let effectiveGroups = (e: Trigger.event, ~membership) => groupsOf(e)->Option.getOr(membership)
|
|
56
54
|
|
|
57
55
|
describe("the conformance table, minted by the trigger", () => {
|
|
58
56
|
Contract.conformanceCases->Array.forEach(({label, membership, requested, expected}) =>
|
|
59
|
-
testSync(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
57
|
+
testSync(
|
|
58
|
+
label,
|
|
59
|
+
() => {
|
|
60
|
+
let event = Trigger.respond(
|
|
61
|
+
~event=eventFor(~membership),
|
|
62
|
+
~decision=Trigger.decide(~membership, ~storedRole=requested),
|
|
63
|
+
)
|
|
64
|
+
expect(effectiveGroups(event, ~membership))->toEqual(expected->Option.getOr(membership))
|
|
65
|
+
},
|
|
66
|
+
)
|
|
66
67
|
)
|
|
67
68
|
})
|
|
68
69
|
|
|
@@ -76,9 +77,9 @@ describe("Auth_ActiveRoleTrigger_Ops.decide", () => {
|
|
|
76
77
|
)
|
|
77
78
|
|
|
78
79
|
testSync("a held role narrows", () =>
|
|
79
|
-
expect(
|
|
80
|
-
Trigger.
|
|
81
|
-
)
|
|
80
|
+
expect(Trigger.decide(~membership=["Admin", "Shopper"], ~storedRole=Some("Shopper")))->toEqual(
|
|
81
|
+
Trigger.Narrow({role: "Shopper", membership: ["Admin", "Shopper"]}),
|
|
82
|
+
)
|
|
82
83
|
)
|
|
83
84
|
|
|
84
85
|
// The case the local path never had to answer: the row outlives the membership.
|
|
@@ -152,7 +153,10 @@ describe("Auth_ActiveRoleTrigger_Ops.respond", () => {
|
|
|
152
153
|
expect((
|
|
153
154
|
details->Option.map(d => d.iamRolesToOverride),
|
|
154
155
|
details->Option.flatMap(d => d.preferredRole),
|
|
155
|
-
))->toEqual((
|
|
156
|
+
))->toEqual((
|
|
157
|
+
Some(["arn:aws:iam::1:role/Shopper"]),
|
|
158
|
+
Some(Nullable.make("arn:aws:iam::1:role/Shopper")),
|
|
159
|
+
))
|
|
156
160
|
})
|
|
157
161
|
|
|
158
162
|
testSync("a stale mint carries them through too", () => {
|
|
@@ -41,9 +41,7 @@ describe("Auth_Cognito.authenticate", () => {
|
|
|
41
41
|
})
|
|
42
42
|
|
|
43
43
|
testAsync("non-Bearer Authorization header → Anonymous", async () => {
|
|
44
|
-
let r = await Auth_Cognito.authenticate(
|
|
45
|
-
buildContext([("authorization", "Basic dXNlcjpwYXNz")]),
|
|
46
|
-
)
|
|
44
|
+
let r = await Auth_Cognito.authenticate(buildContext([("authorization", "Basic dXNlcjpwYXNz")]))
|
|
47
45
|
switch r {
|
|
48
46
|
| Anonymous => ()
|
|
49
47
|
| _ => JsError.throwWithMessage("expected Anonymous")
|
|
@@ -51,9 +49,7 @@ describe("Auth_Cognito.authenticate", () => {
|
|
|
51
49
|
})
|
|
52
50
|
|
|
53
51
|
testAsync("malformed Bearer token → AuthError", async () => {
|
|
54
|
-
let r = await Auth_Cognito.authenticate(
|
|
55
|
-
buildContext([("authorization", "Bearer not-a-jwt")]),
|
|
56
|
-
)
|
|
52
|
+
let r = await Auth_Cognito.authenticate(buildContext([("authorization", "Bearer not-a-jwt")]))
|
|
57
53
|
switch r {
|
|
58
54
|
| AuthError(_) => ()
|
|
59
55
|
| _ => JsError.throwWithMessage("expected AuthError")
|
|
@@ -64,15 +60,10 @@ describe("Auth_Cognito.authenticate", () => {
|
|
|
64
60
|
let claims = Dict.fromArray([
|
|
65
61
|
("sub", JSON.Encode.string("c3741234-aaaa-bbbb-cccc-ddddeeee1234")),
|
|
66
62
|
("cognito:username", JSON.Encode.string("alice@example.com")),
|
|
67
|
-
(
|
|
68
|
-
"cognito:groups",
|
|
69
|
-
["Admin", "User"]->Array.map(JSON.Encode.string)->JSON.Encode.array,
|
|
70
|
-
),
|
|
63
|
+
("cognito:groups", ["Admin", "User"]->Array.map(JSON.Encode.string)->JSON.Encode.array),
|
|
71
64
|
])
|
|
72
65
|
let token = buildJwt(claims)
|
|
73
|
-
let r = await Auth_Cognito.authenticate(
|
|
74
|
-
buildContext([("authorization", "Bearer " ++ token)]),
|
|
75
|
-
)
|
|
66
|
+
let r = await Auth_Cognito.authenticate(buildContext([("authorization", "Bearer " ++ token)]))
|
|
76
67
|
switch r {
|
|
77
68
|
| Authenticated(identity) =>
|
|
78
69
|
expect(identity.userId)->toBe("c3741234-aaaa-bbbb-cccc-ddddeeee1234")
|
|
@@ -88,9 +79,7 @@ describe("Auth_Cognito.authenticate", () => {
|
|
|
88
79
|
("cognito:username", JSON.Encode.string("bob")),
|
|
89
80
|
])
|
|
90
81
|
let token = buildJwt(claims)
|
|
91
|
-
let r = await Auth_Cognito.authenticate(
|
|
92
|
-
buildContext([("authorization", "Bearer " ++ token)]),
|
|
93
|
-
)
|
|
82
|
+
let r = await Auth_Cognito.authenticate(buildContext([("authorization", "Bearer " ++ token)]))
|
|
94
83
|
switch r {
|
|
95
84
|
| Authenticated(identity) =>
|
|
96
85
|
expect(identity.groups)->toEqual([])
|
|
@@ -100,13 +89,9 @@ describe("Auth_Cognito.authenticate", () => {
|
|
|
100
89
|
})
|
|
101
90
|
|
|
102
91
|
testAsync("header lookup is case-insensitive (Authorization)", async () => {
|
|
103
|
-
let claims = Dict.fromArray([
|
|
104
|
-
("sub", JSON.Encode.string("user-mixed-case")),
|
|
105
|
-
])
|
|
92
|
+
let claims = Dict.fromArray([("sub", JSON.Encode.string("user-mixed-case"))])
|
|
106
93
|
let token = buildJwt(claims)
|
|
107
|
-
let r = await Auth_Cognito.authenticate(
|
|
108
|
-
buildContext([("Authorization", "Bearer " ++ token)]),
|
|
109
|
-
)
|
|
94
|
+
let r = await Auth_Cognito.authenticate(buildContext([("Authorization", "Bearer " ++ token)]))
|
|
110
95
|
switch r {
|
|
111
96
|
| Authenticated(identity) => expect(identity.userId)->toBe("user-mixed-case")
|
|
112
97
|
| _ => JsError.throwWithMessage("expected Authenticated")
|
|
@@ -124,31 +109,31 @@ describe("Auth_Cognito.fromAppSyncIdentity", () => {
|
|
|
124
109
|
}
|
|
125
110
|
})
|
|
126
111
|
|
|
127
|
-
testSync(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
(
|
|
131
|
-
"cognito:
|
|
132
|
-
["Admin"]->Array.map(JSON.Encode.string)->JSON.Encode.array,
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
claims,
|
|
139
|
-
}
|
|
140
|
-
switch Auth_Cognito.fromAppSyncIdentity(Some(id)) {
|
|
141
|
-
| Authenticated(identity) =>
|
|
142
|
-
expect(identity.userId)->toBe("c3741234-aaaa")
|
|
143
|
-
expect(identity.username)->toBe("alice@example.com")
|
|
144
|
-
expect(identity.groups)->toEqual(["Admin"])
|
|
145
|
-
switch identity.provider {
|
|
146
|
-
| Cognito => ()
|
|
147
|
-
| _ => JsError.throwWithMessage("expected Cognito provider")
|
|
112
|
+
testSync(
|
|
113
|
+
"Cognito identity: sub + claims.cognito:groups → Authenticated with Cognito provider",
|
|
114
|
+
() => {
|
|
115
|
+
let claims = Dict.fromArray([
|
|
116
|
+
("cognito:username", JSON.Encode.string("alice@example.com")),
|
|
117
|
+
("cognito:groups", ["Admin"]->Array.map(JSON.Encode.string)->JSON.Encode.array),
|
|
118
|
+
])
|
|
119
|
+
let id: Auth_Cognito.appSyncIdentity = {
|
|
120
|
+
sub: "c3741234-aaaa",
|
|
121
|
+
username: "alice@example.com",
|
|
122
|
+
claims,
|
|
148
123
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
124
|
+
switch Auth_Cognito.fromAppSyncIdentity(Some(id)) {
|
|
125
|
+
| Authenticated(identity) =>
|
|
126
|
+
expect(identity.userId)->toBe("c3741234-aaaa")
|
|
127
|
+
expect(identity.username)->toBe("alice@example.com")
|
|
128
|
+
expect(identity.groups)->toEqual(["Admin"])
|
|
129
|
+
switch identity.provider {
|
|
130
|
+
| Cognito => ()
|
|
131
|
+
| _ => JsError.throwWithMessage("expected Cognito provider")
|
|
132
|
+
}
|
|
133
|
+
| _ => JsError.throwWithMessage("expected Authenticated")
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
)
|
|
152
137
|
|
|
153
138
|
testSync("Cognito identity: missing claims.cognito:groups → empty groups", () => {
|
|
154
139
|
let id: Auth_Cognito.appSyncIdentity = {
|