@reventlessdev/reventless-aws 3.0.0-alpha.344 → 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 +8 -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 +46 -42
- 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 +15 -3
- 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
|
@@ -32,7 +32,10 @@ let stored = (eventType, tags, data): DcbEventLog_Adapter.rawStoredEvent => {
|
|
|
32
32
|
let capturingSendBatch = sink => async jsons => sink := sink.contents->Array.concat(jsons)
|
|
33
33
|
|
|
34
34
|
let idOf = json =>
|
|
35
|
-
json
|
|
35
|
+
json
|
|
36
|
+
->JSON.Decode.object
|
|
37
|
+
->Option.flatMap(o => o->Dict.get("id"))
|
|
38
|
+
->Option.flatMap(JSON.Decode.string)
|
|
36
39
|
|
|
37
40
|
switch NodeProcess.env->Dict.get("PG_URL") {
|
|
38
41
|
| None =>
|
|
@@ -63,7 +66,11 @@ switch NodeProcess.env->Dict.get("PG_URL") {
|
|
|
63
66
|
testPromise("relays each appended DCB event as an {id, meta, event} body", async () => {
|
|
64
67
|
let (_n, ops, _s) = makeLog("relay-basic")
|
|
65
68
|
let mk = i =>
|
|
66
|
-
stored(
|
|
69
|
+
stored(
|
|
70
|
+
"OrderPlaced",
|
|
71
|
+
[{key: "orderId", value: "o-1"}],
|
|
72
|
+
jsonObj([("i", JSON.Encode.int(i))]),
|
|
73
|
+
)
|
|
67
74
|
let _ = await ops.append([mk(0), mk(1), mk(2)])
|
|
68
75
|
|
|
69
76
|
let sink = ref([])
|
|
@@ -81,109 +88,134 @@ switch NodeProcess.env->Dict.get("PG_URL") {
|
|
|
81
88
|
let first = sink.contents->Array.getUnsafe(0)
|
|
82
89
|
expect(idOf(first))->toEqual(Some("orderId:o-1"))
|
|
83
90
|
let obj = first->JSON.Decode.object->Option.getOrThrow
|
|
84
|
-
expect(
|
|
91
|
+
expect(
|
|
92
|
+
obj->Dict.get("event")->Option.getOrThrow->JSON.stringify->String.includes("OrderPlaced"),
|
|
93
|
+
)->toBe(true)
|
|
85
94
|
let metaObj = obj->Dict.get("meta")->Option.flatMap(JSON.Decode.object)->Option.getOrThrow
|
|
86
95
|
expect(metaObj->Dict.get("service"))->toEqual(Some(JSON.Encode.string("relay-it")))
|
|
87
96
|
})
|
|
88
97
|
|
|
89
|
-
testPromise(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
testPromise(
|
|
99
|
+
"checkpoints — a second drain from the same subscriber sees nothing new",
|
|
100
|
+
async () => {
|
|
101
|
+
let (_n, ops, _s) = makeLog("relay-checkpoint")
|
|
102
|
+
let mk = i =>
|
|
103
|
+
stored("Fed", [{key: "orderId", value: "f"}], jsonObj([("i", JSON.Encode.int(i))]))
|
|
104
|
+
let _ = await ops.append([mk(0), mk(1)])
|
|
93
105
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
106
|
+
let sink = ref([])
|
|
107
|
+
let first = await PgChangeFeedRelay_Runtime.relayWithPool(
|
|
108
|
+
~pool,
|
|
109
|
+
~logName="relay-checkpoint",
|
|
110
|
+
~subscriber="relay-it",
|
|
111
|
+
~sendBatch=capturingSendBatch(sink),
|
|
112
|
+
)
|
|
113
|
+
expect(first)->toBe(2)
|
|
102
114
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
let second = await PgChangeFeedRelay_Runtime.relayWithPool(
|
|
116
|
+
~pool,
|
|
117
|
+
~logName="relay-checkpoint",
|
|
118
|
+
~subscriber="relay-it",
|
|
119
|
+
~sendBatch=capturingSendBatch(sink),
|
|
120
|
+
)
|
|
121
|
+
expect(second)->toBe(0)
|
|
122
|
+
// No extra bodies were emitted on the second pass.
|
|
123
|
+
expect(sink.contents->Array.length)->toBe(2)
|
|
124
|
+
},
|
|
125
|
+
)
|
|
113
126
|
|
|
114
|
-
testPromise(
|
|
115
|
-
|
|
116
|
-
|
|
127
|
+
testPromise(
|
|
128
|
+
"classic: relays each appended event_log event as an {id, meta, event} body",
|
|
129
|
+
async () => {
|
|
130
|
+
let (_n, ops, _s) = ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
|
|
117
131
|
~pool,
|
|
118
132
|
~name="ClassicRelayBasicEventLog",
|
|
119
133
|
~opts=(),
|
|
120
134
|
)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
// The flat on-disk item the classic append stores verbatim — same shape the
|
|
136
|
+
// DynamoDB path puts (id, position, event, data, decomposed meta).
|
|
137
|
+
let flatItem = (~id, ~seq, ~eventType, ~data) =>
|
|
138
|
+
[
|
|
139
|
+
("id", JSON.Encode.string(id)),
|
|
140
|
+
("position", JSON.Encode.string(seq->Int.toString->String.padStart(9, "0"))),
|
|
141
|
+
("event", JSON.Encode.string(eventType)),
|
|
142
|
+
("data", data),
|
|
143
|
+
]
|
|
144
|
+
->Array.concat(
|
|
145
|
+
ReventlessCore.Message.generateMeta(
|
|
146
|
+
~service="relay-it",
|
|
147
|
+
)->ReventlessCore.Message.decomposeMeta,
|
|
148
|
+
)
|
|
149
|
+
->Dict.fromArray
|
|
150
|
+
->JSON.Encode.object
|
|
151
|
+
let _ = await ops.append(
|
|
152
|
+
0,
|
|
153
|
+
"agg-1",
|
|
154
|
+
[
|
|
155
|
+
flatItem(
|
|
156
|
+
~id="agg-1",
|
|
157
|
+
~seq=0,
|
|
158
|
+
~eventType="NameUpdated",
|
|
159
|
+
~data=jsonObj([("n", JSON.Encode.int(0))]),
|
|
160
|
+
),
|
|
161
|
+
flatItem(
|
|
162
|
+
~id="agg-1",
|
|
163
|
+
~seq=1,
|
|
164
|
+
~eventType="NameUpdated",
|
|
165
|
+
~data=jsonObj([("n", JSON.Encode.int(1))]),
|
|
166
|
+
),
|
|
167
|
+
],
|
|
132
168
|
)
|
|
133
|
-
->Dict.fromArray
|
|
134
|
-
->JSON.Encode.object
|
|
135
|
-
let _ = await ops.append(
|
|
136
|
-
0,
|
|
137
|
-
"agg-1",
|
|
138
|
-
[
|
|
139
|
-
flatItem(~id="agg-1", ~seq=0, ~eventType="NameUpdated", ~data=jsonObj([("n", JSON.Encode.int(0))])),
|
|
140
|
-
flatItem(~id="agg-1", ~seq=1, ~eventType="NameUpdated", ~data=jsonObj([("n", JSON.Encode.int(1))])),
|
|
141
|
-
],
|
|
142
|
-
)
|
|
143
169
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
170
|
+
let sink = ref([])
|
|
171
|
+
let processed = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
|
|
172
|
+
~pool,
|
|
173
|
+
~logName="ClassicRelayBasicEventLog",
|
|
174
|
+
~subscriber="relay-it:ClassicRelayBasicEventLog",
|
|
175
|
+
~sendBatch=capturingSendBatch(sink),
|
|
176
|
+
)
|
|
151
177
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
178
|
+
expect(processed)->toBe(2)
|
|
179
|
+
expect(sink.contents->Array.length)->toBe(2)
|
|
180
|
+
let first = sink.contents->Array.getUnsafe(0)
|
|
181
|
+
expect(idOf(first))->toEqual(Some("agg-1"))
|
|
182
|
+
let obj = first->JSON.Decode.object->Option.getOrThrow
|
|
183
|
+
expect(
|
|
184
|
+
obj->Dict.get("event")->Option.getOrThrow->JSON.stringify->String.includes("NameUpdated"),
|
|
185
|
+
)->toBe(true)
|
|
186
|
+
let metaObj = obj->Dict.get("meta")->Option.flatMap(JSON.Decode.object)->Option.getOrThrow
|
|
187
|
+
expect(metaObj->Dict.get("service"))->toEqual(Some(JSON.Encode.string("relay-it")))
|
|
162
188
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
189
|
+
// Checkpointed: a second drain from the same subscriber sees nothing new.
|
|
190
|
+
let second = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
|
|
191
|
+
~pool,
|
|
192
|
+
~logName="ClassicRelayBasicEventLog",
|
|
193
|
+
~subscriber="relay-it:ClassicRelayBasicEventLog",
|
|
194
|
+
~sendBatch=capturingSendBatch(sink),
|
|
195
|
+
)
|
|
196
|
+
expect(second)->toBe(0)
|
|
197
|
+
expect(sink.contents->Array.length)->toBe(2)
|
|
198
|
+
},
|
|
199
|
+
)
|
|
173
200
|
|
|
174
201
|
testPromise("classic: per-log subscribers keep checkpoints isolated across logs", async () => {
|
|
175
202
|
// event_log_subscription keys by subscriber alone. With a SHARED subscriber,
|
|
176
203
|
// draining log A after log B's events were appended would save a cursor past
|
|
177
204
|
// B's rows and silently skip them — the clobber the per-log subscriber fixes.
|
|
178
205
|
let mkOps = name => {
|
|
179
|
-
let (_n, ops, _s) =
|
|
180
|
-
|
|
206
|
+
let (_n, ops, _s) = ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
|
|
207
|
+
~pool,
|
|
208
|
+
~name,
|
|
209
|
+
~opts=(),
|
|
210
|
+
)
|
|
181
211
|
ops
|
|
182
212
|
}
|
|
183
213
|
let opsB = mkOps("ClassicRelayIsoBEventLog")
|
|
184
214
|
let opsA = mkOps("ClassicRelayIsoAEventLog")
|
|
185
215
|
let meta = () =>
|
|
186
|
-
ReventlessCore.Message.generateMeta(
|
|
216
|
+
ReventlessCore.Message.generateMeta(
|
|
217
|
+
~service="relay-it",
|
|
218
|
+
)->ReventlessCore.Message.decomposeMeta
|
|
187
219
|
let item = (id, eventType) =>
|
|
188
220
|
[
|
|
189
221
|
("id", JSON.Encode.string(id)),
|
|
@@ -221,20 +253,18 @@ switch NodeProcess.env->Dict.get("PG_URL") {
|
|
|
221
253
|
})
|
|
222
254
|
|
|
223
255
|
testPromise("partitionTag pins the id to its tag on a multi-tag event", async () => {
|
|
224
|
-
let (_n, ops, _s) =
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
jsonObj([("n", JSON.Encode.int(1))]),
|
|
237
|
-
)
|
|
256
|
+
let (_n, ops, _s) = ReventlessPostgres.DcbEventLogStorage_Postgres.makeStorage(
|
|
257
|
+
~pool,
|
|
258
|
+
~name="relay-pt",
|
|
259
|
+
~indexes=[],
|
|
260
|
+
~partitionTag=DcbTag.Simple({key: "customerId"}),
|
|
261
|
+
~opts,
|
|
262
|
+
)
|
|
263
|
+
let ev = stored(
|
|
264
|
+
"OrderPlaced",
|
|
265
|
+
[{key: "orderId", value: "o-9"}, {key: "customerId", value: "c-9"}],
|
|
266
|
+
jsonObj([("n", JSON.Encode.int(1))]),
|
|
267
|
+
)
|
|
238
268
|
let _ = await ops.append([ev])
|
|
239
269
|
|
|
240
270
|
// The relay receives the sury-encoded partition tag exactly as the builder emits it.
|
|
@@ -30,8 +30,7 @@ describe("PgChangeFeedRelay_Runtime.toEventCollectorJson", () => {
|
|
|
30
30
|
expect(eventJson->JSON.stringify->String.includes("c-1"))->toBe(true)
|
|
31
31
|
|
|
32
32
|
// meta round-trips the producing service
|
|
33
|
-
let metaObj =
|
|
34
|
-
obj->Dict.get("meta")->Option.flatMap(JSON.Decode.object)->Option.getOrThrow
|
|
33
|
+
let metaObj = obj->Dict.get("meta")->Option.flatMap(JSON.Decode.object)->Option.getOrThrow
|
|
35
34
|
expect(metaObj->Dict.get("service"))->toEqual(Some(JSON.Encode.string("Orders")))
|
|
36
35
|
})
|
|
37
36
|
|
|
@@ -52,7 +51,10 @@ describe("PgChangeFeedRelay_Runtime partitionTag (B2.3d)", () => {
|
|
|
52
51
|
testSync("a Simple partitionTag selects its tag for the id, not just the first", () => {
|
|
53
52
|
let pt = Reventless.DcbTag.Simple({key: "customerId"})
|
|
54
53
|
let json =
|
|
55
|
-
PgChangeFeedRelay_Runtime.toEventCollectorJson(
|
|
54
|
+
PgChangeFeedRelay_Runtime.toEventCollectorJson(
|
|
55
|
+
multiTagEvent,
|
|
56
|
+
~partitionTag=pt,
|
|
57
|
+
)->Option.getOrThrow
|
|
56
58
|
let obj = json->JSON.Decode.object->Option.getOrThrow
|
|
57
59
|
// Without partitionTag the id would be "orderId:o-1" (first tag); the tag
|
|
58
60
|
// pins it to "customerId:c-9".
|
|
@@ -64,12 +66,13 @@ describe("PgChangeFeedRelay_Runtime partitionTag (B2.3d)", () => {
|
|
|
64
66
|
// and PgChangeFeedRelay_Runtime.relay parses it back the same way. Guard the
|
|
65
67
|
// round-trip so a schema change can't silently break the id derivation.
|
|
66
68
|
let simple = Reventless.DcbTag.Simple({key: "courseId"})
|
|
67
|
-
let composite =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
let composite = Reventless.DcbTag.Composite({keys: ["studentId", "courseId"], seps: [":"]})
|
|
70
|
+
[simple, composite]->Array.forEach(
|
|
71
|
+
pt => {
|
|
72
|
+
let wire = pt->Reventless.Util_Sury.toJson(Reventless.DcbTag.derivedPartitionTagSchema)
|
|
73
|
+
let back = wire->Reventless.Util_Sury.fromJson(Reventless.DcbTag.derivedPartitionTagSchema)
|
|
74
|
+
expect(back)->toEqual(pt)
|
|
75
|
+
},
|
|
76
|
+
)
|
|
74
77
|
})
|
|
75
78
|
})
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
open JestGlobals
|
|
16
16
|
open ReventlessCore
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
// --- Fixtures: one classic source, one read model, one projection mapping ---
|
|
20
19
|
|
|
21
20
|
module OrderSource = {
|
|
@@ -132,78 +131,93 @@ switch NodeProcess.env->Dict.get("PG_URL") {
|
|
|
132
131
|
let _ = pool->ReventlessPostgres.PgDriver.endPool
|
|
133
132
|
})
|
|
134
133
|
|
|
135
|
-
describe(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
describe(
|
|
135
|
+
"Postgres pipeline (append → relay → feed decode → projection → qdb row)",
|
|
136
|
+
() => {
|
|
137
|
+
testPromise("projects a classic event end-to-end into the Postgres QueryDb", async () => {
|
|
138
|
+
let (_n, elOps, _s) = ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
|
|
139
139
|
~pool,
|
|
140
140
|
~name="PipelineOrderEventLog",
|
|
141
141
|
~opts=(),
|
|
142
142
|
)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
~sendBatch=async jsons => sink := sink.contents->Array.concat(jsons),
|
|
156
|
-
)
|
|
157
|
-
expect(relayed)->toBe(1)
|
|
158
|
-
|
|
159
|
-
// Feed-queue delivery → entry-point decode → real projection callback.
|
|
160
|
-
let _ = await EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(
|
|
161
|
-
Callback.handleJsonEvents,
|
|
162
|
-
asSqsEvent(sink.contents),
|
|
163
|
-
(),
|
|
164
|
-
)->Effect.runPromise
|
|
165
|
-
|
|
166
|
-
// The projected row is in the Postgres QueryDb.
|
|
167
|
-
switch await qdbOps.load("o-1") {
|
|
168
|
-
| Ok(rows) =>
|
|
169
|
-
expect(rows->Array.length)->toBe(1)
|
|
170
|
-
let row = rows->Array.getUnsafe(0)->JSON.Decode.object->Option.getOrThrow
|
|
171
|
-
expect(row->Dict.get("product"))->toEqual(Some(JSON.Encode.string("boat")))
|
|
172
|
-
expect(row->Dict.get("id"))->toEqual(Some(JSON.Encode.string("o-1")))
|
|
173
|
-
| Error(_) => expect("load failed")->toBe("Ok")
|
|
174
|
-
}
|
|
175
|
-
})
|
|
143
|
+
let _ = await elOps.append(
|
|
144
|
+
0,
|
|
145
|
+
"o-1",
|
|
146
|
+
[
|
|
147
|
+
flatItem(
|
|
148
|
+
~id="o-1",
|
|
149
|
+
~seq=0,
|
|
150
|
+
~service=OrderSource.name,
|
|
151
|
+
~event=OrderPlaced({product: "boat"}),
|
|
152
|
+
),
|
|
153
|
+
],
|
|
154
|
+
)
|
|
176
155
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
156
|
+
// Relay drains event_log and emits the EventCollector bodies.
|
|
157
|
+
let sink = ref([])
|
|
158
|
+
let relayed = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
|
|
159
|
+
~pool,
|
|
160
|
+
~logName="PipelineOrderEventLog",
|
|
161
|
+
~subscriber="aws-rm-feed:PipelineOrderEventLog",
|
|
162
|
+
~sendBatch=async jsons => sink := sink.contents->Array.concat(jsons),
|
|
163
|
+
)
|
|
164
|
+
expect(relayed)->toBe(1)
|
|
165
|
+
|
|
166
|
+
// Feed-queue delivery → entry-point decode → real projection callback.
|
|
167
|
+
let _ = await EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(
|
|
168
|
+
Callback.handleJsonEvents,
|
|
169
|
+
asSqsEvent(sink.contents),
|
|
170
|
+
(),
|
|
171
|
+
)->Effect.runPromise
|
|
172
|
+
|
|
173
|
+
// The projected row is in the Postgres QueryDb.
|
|
174
|
+
switch await qdbOps.load("o-1") {
|
|
175
|
+
| Ok(rows) =>
|
|
176
|
+
expect(rows->Array.length)->toBe(1)
|
|
177
|
+
let row = rows->Array.getUnsafe(0)->JSON.Decode.object->Option.getOrThrow
|
|
178
|
+
expect(row->Dict.get("product"))->toEqual(Some(JSON.Encode.string("boat")))
|
|
179
|
+
expect(row->Dict.get("id"))->toEqual(Some(JSON.Encode.string("o-1")))
|
|
180
|
+
| Error(_) => expect("load failed")->toBe("Ok")
|
|
181
|
+
}
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
testPromise("meta.service mismatch is a silent no-op (dispatch filter)", async () => {
|
|
185
|
+
let (_n, elOps, _s) = ReventlessPostgres.EventLogStorage_Postgres.makeStorage(
|
|
180
186
|
~pool,
|
|
181
187
|
~name="PipelineWrongServiceEventLog",
|
|
182
188
|
~opts=(),
|
|
183
189
|
)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
190
|
+
let _ = await elOps.append(
|
|
191
|
+
0,
|
|
192
|
+
"o-2",
|
|
193
|
+
[
|
|
194
|
+
flatItem(
|
|
195
|
+
~id="o-2",
|
|
196
|
+
~seq=0,
|
|
197
|
+
~service="NotTheSource",
|
|
198
|
+
~event=OrderPlaced({product: "x"}),
|
|
199
|
+
),
|
|
200
|
+
],
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
let sink = ref([])
|
|
204
|
+
let _ = await PgChangeFeedRelay_Runtime.relayClassicWithPool(
|
|
205
|
+
~pool,
|
|
206
|
+
~logName="PipelineWrongServiceEventLog",
|
|
207
|
+
~subscriber="aws-rm-feed:PipelineWrongServiceEventLog",
|
|
208
|
+
~sendBatch=async jsons => sink := sink.contents->Array.concat(jsons),
|
|
209
|
+
)
|
|
210
|
+
let _ = await EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(
|
|
211
|
+
Callback.handleJsonEvents,
|
|
212
|
+
asSqsEvent(sink.contents),
|
|
213
|
+
(),
|
|
214
|
+
)->Effect.runPromise
|
|
215
|
+
|
|
216
|
+
switch await qdbOps.load("o-2") {
|
|
217
|
+
| Ok(rows) => expect(rows->Array.length)->toBe(0)
|
|
218
|
+
| Error(_) => expect("load failed")->toBe("Ok")
|
|
219
|
+
}
|
|
220
|
+
})
|
|
221
|
+
},
|
|
222
|
+
)
|
|
209
223
|
}
|
|
@@ -92,8 +92,7 @@ switch NodeProcess.env->Dict.get("PG_URL") {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
switch await Ops.load("c-1"->CustomersSpec.Id.makeFromString) {
|
|
95
|
-
| Ok([{geolocation: Located({lat, lng})}]) =>
|
|
96
|
-
expect((lat, lng))->toEqual((48.2082, 16.3738))
|
|
95
|
+
| Ok([{geolocation: Located({lat, lng})}]) => expect((lat, lng))->toEqual((48.2082, 16.3738))
|
|
97
96
|
| _ => expect("round trip")->toBe("Located with its point intact")
|
|
98
97
|
}
|
|
99
98
|
})
|
|
@@ -15,16 +15,12 @@ describe("PgQueryResolverEntryPoint_Ops.parseResolverConfig", () => {
|
|
|
15
15
|
})
|
|
16
16
|
|
|
17
17
|
testSync("null pgConnection decodes to None", () => {
|
|
18
|
-
let config = PgQueryResolverEntryPoint_Ops.parseResolverConfig(
|
|
19
|
-
`{"pgConnection":null,"handlers":[]}`,
|
|
20
|
-
)
|
|
18
|
+
let config = PgQueryResolverEntryPoint_Ops.parseResolverConfig(`{"pgConnection":null,"handlers":[]}`)
|
|
21
19
|
expect(config.pgConnection->Option.isNone)->toBe(true)
|
|
22
20
|
})
|
|
23
21
|
|
|
24
22
|
testSync("decodes connection, handlers, and node types", () => {
|
|
25
|
-
let config = PgQueryResolverEntryPoint_Ops.parseResolverConfig(
|
|
26
|
-
`{"pgConnection":{"host":"db.local","port":5432,"database":"app","username":"master","secretArn":"arn:secret"},"handlers":[{"readModelName":"Products","specModule":"@x/p/src/ReadModel/Products.res.mjs","labelField":"name","includeIdParam":true},{"readModelName":"Orders","specModule":"@x/p/src/ReadModel/Orders.res.mjs","labelField":"id","includeIdParam":false}],"nodeTypes":{"Product":"Products"}}`,
|
|
27
|
-
)
|
|
23
|
+
let config = PgQueryResolverEntryPoint_Ops.parseResolverConfig(`{"pgConnection":{"host":"db.local","port":5432,"database":"app","username":"master","secretArn":"arn:secret"},"handlers":[{"readModelName":"Products","specModule":"@x/p/src/ReadModel/Products.res.mjs","labelField":"name","includeIdParam":true},{"readModelName":"Orders","specModule":"@x/p/src/ReadModel/Orders.res.mjs","labelField":"id","includeIdParam":false}],"nodeTypes":{"Product":"Products"}}`)
|
|
28
24
|
expect(config.pgConnection->Option.map(cc => cc.host))->toEqual(Some("db.local"))
|
|
29
25
|
expect(config.handlers->Array.length)->toBe(2)
|
|
30
26
|
let first = config.handlers->Array.getUnsafe(0)
|