@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
|
@@ -44,9 +44,7 @@ describe("Runtime.collectQueryTags", () => {
|
|
|
44
44
|
})
|
|
45
45
|
|
|
46
46
|
testSync("collects tags from a single queryItem", () => {
|
|
47
|
-
let q: Reventless.DcbTag.query = [
|
|
48
|
-
{tags: [tag("orderId", "o1"), tag("customerId", "c1")]},
|
|
49
|
-
]
|
|
47
|
+
let q: Reventless.DcbTag.query = [{tags: [tag("orderId", "o1"), tag("customerId", "c1")]}]
|
|
50
48
|
let result = Runtime.collectQueryTags(q)
|
|
51
49
|
expect(result->Array.length)->toBe(2)
|
|
52
50
|
})
|
|
@@ -62,10 +60,7 @@ describe("Runtime.collectQueryTags", () => {
|
|
|
62
60
|
})
|
|
63
61
|
|
|
64
62
|
testSync("ignores queryItems without tags but keeps others", () => {
|
|
65
|
-
let q: Reventless.DcbTag.query = [
|
|
66
|
-
{eventTypes: ["Foo"]},
|
|
67
|
-
{tags: [tag("orderId", "o1")]},
|
|
68
|
-
]
|
|
63
|
+
let q: Reventless.DcbTag.query = [{eventTypes: ["Foo"]}, {tags: [tag("orderId", "o1")]}]
|
|
69
64
|
let result = Runtime.collectQueryTags(q)
|
|
70
65
|
expect(result->Array.length)->toBe(1)
|
|
71
66
|
})
|
|
@@ -139,8 +134,7 @@ describe("Runtime.buildConditionalFenceUpdate", () => {
|
|
|
139
134
|
~newPosition="100",
|
|
140
135
|
~after=Some("50"),
|
|
141
136
|
)
|
|
142
|
-
let referenced =
|
|
143
|
-
update.expressionAttributeNames->Option.getOr(Dict.make())->Dict.valuesToArray
|
|
137
|
+
let referenced = update.expressionAttributeNames->Option.getOr(Dict.make())->Dict.valuesToArray
|
|
144
138
|
expect(referenced->Array.includes("pos#ProductPriceChanged"))->toBe(false)
|
|
145
139
|
})
|
|
146
140
|
|
|
@@ -207,29 +201,17 @@ describe("Runtime.buildQueryByPartitionKeyInput", () => {
|
|
|
207
201
|
})
|
|
208
202
|
|
|
209
203
|
testSync("omits consistentRead when ~strongConsistency=false", () => {
|
|
210
|
-
let input = Runtime.buildQueryByPartitionKeyInput(
|
|
211
|
-
table,
|
|
212
|
-
"orderId:o1",
|
|
213
|
-
~strongConsistency=false,
|
|
214
|
-
)
|
|
204
|
+
let input = Runtime.buildQueryByPartitionKeyInput(table, "orderId:o1", ~strongConsistency=false)
|
|
215
205
|
expect(input.consistentRead)->toEqual(None)
|
|
216
206
|
})
|
|
217
207
|
|
|
218
208
|
testSync("sets consistentRead=true when ~strongConsistency=true", () => {
|
|
219
|
-
let input = Runtime.buildQueryByPartitionKeyInput(
|
|
220
|
-
table,
|
|
221
|
-
"orderId:o1",
|
|
222
|
-
~strongConsistency=true,
|
|
223
|
-
)
|
|
209
|
+
let input = Runtime.buildQueryByPartitionKeyInput(table, "orderId:o1", ~strongConsistency=true)
|
|
224
210
|
expect(input.consistentRead)->toEqual(Some(true))
|
|
225
211
|
})
|
|
226
212
|
|
|
227
213
|
testSync("does not target a GSI (uses base table)", () => {
|
|
228
|
-
let input = Runtime.buildQueryByPartitionKeyInput(
|
|
229
|
-
table,
|
|
230
|
-
"orderId:o1",
|
|
231
|
-
~strongConsistency=true,
|
|
232
|
-
)
|
|
214
|
+
let input = Runtime.buildQueryByPartitionKeyInput(table, "orderId:o1", ~strongConsistency=true)
|
|
233
215
|
expect(input.indexName)->toEqual(None)
|
|
234
216
|
})
|
|
235
217
|
|
|
@@ -333,7 +315,10 @@ describe("Runtime.buildConditionalTransactItems — fence-scope = read-scope", (
|
|
|
333
315
|
}
|
|
334
316
|
|
|
335
317
|
// Locate the fence transact item (Update or ConditionCheck) targeting `fenceId`.
|
|
336
|
-
let findFence = (
|
|
318
|
+
let findFence = (
|
|
319
|
+
items: array<AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.transactWriteItem>,
|
|
320
|
+
fenceId,
|
|
321
|
+
) =>
|
|
337
322
|
items->Array.find(it => {
|
|
338
323
|
let idOf = key => key->Dict.get("id") == Some(fenceId->JSON.Encode.string)
|
|
339
324
|
switch (it.update, it.conditionCheck) {
|
|
@@ -342,38 +327,62 @@ describe("Runtime.buildConditionalTransactItems — fence-scope = read-scope", (
|
|
|
342
327
|
| _ => false
|
|
343
328
|
}
|
|
344
329
|
})
|
|
345
|
-
let isUpdate = it =>
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
330
|
+
let isUpdate = it =>
|
|
331
|
+
it
|
|
332
|
+
->Option.flatMap(i => i.AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.update)
|
|
333
|
+
->Option.isSome
|
|
334
|
+
let isCheck = it =>
|
|
335
|
+
it
|
|
336
|
+
->Option.flatMap(i => i.AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.conditionCheck)
|
|
337
|
+
->Option.isSome
|
|
338
|
+
|
|
339
|
+
describe(
|
|
340
|
+
"PlaceOrder shape: OrderPlaced partitioned by orderId, reads orderId + productId",
|
|
341
|
+
() => {
|
|
342
|
+
let orderPlaced = event(
|
|
343
|
+
"OrderPlaced",
|
|
344
|
+
[tag("orderId", "o1"), tag("customerId", "c1"), tag("productId", "p5")],
|
|
345
|
+
)
|
|
346
|
+
let partitionTag = Some(Reventless.DcbTag.Simple({key: "orderId"}))
|
|
347
|
+
let cond: Reventless.DcbTag.appendCondition = {
|
|
348
|
+
query: [
|
|
349
|
+
{tags: [tag("orderId", "o1")], eventTypes: ["OrderPlaced"]},
|
|
350
|
+
{tags: [tag("productId", "p5")], eventTypes: ["OrderPlaced", "CatalogProductSynced"]},
|
|
351
|
+
],
|
|
352
|
+
after: "50",
|
|
353
|
+
}
|
|
354
|
+
let items = Runtime.buildConditionalTransactItems(
|
|
355
|
+
table,
|
|
356
|
+
[orderPlaced],
|
|
357
|
+
cond,
|
|
358
|
+
"100",
|
|
359
|
+
~partitionTag?,
|
|
360
|
+
)
|
|
362
361
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
362
|
+
testSync(
|
|
363
|
+
"partition tag (orderId) is a conditional Update that bumps the fence",
|
|
364
|
+
() => {
|
|
365
|
+
expect(isUpdate(findFence(items, "fence#orderId:o1")))->toBe(true)
|
|
366
|
+
},
|
|
367
|
+
)
|
|
366
368
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
369
|
+
testSync(
|
|
370
|
+
"non-partition read tag (productId) is a ConditionCheck, not a bump",
|
|
371
|
+
() => {
|
|
372
|
+
let it = findFence(items, "fence#productId:p5")
|
|
373
|
+
expect(isCheck(it))->toBe(true)
|
|
374
|
+
expect(isUpdate(it))->toBe(false)
|
|
375
|
+
},
|
|
376
|
+
)
|
|
372
377
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
378
|
+
testSync(
|
|
379
|
+
"secondary event tag (customerId) is not fenced at all",
|
|
380
|
+
() => {
|
|
381
|
+
expect(findFence(items, "fence#customerId:c1")->Option.isSome)->toBe(false)
|
|
382
|
+
},
|
|
383
|
+
)
|
|
384
|
+
},
|
|
385
|
+
)
|
|
377
386
|
|
|
378
387
|
describe("composite (multi-tag) read keeps check+bump on all its tags", () => {
|
|
379
388
|
// RecordProductDemand-style: one multi-tag clause => composite GSI read.
|
|
@@ -381,19 +390,28 @@ describe("Runtime.buildConditionalTransactItems — fence-scope = read-scope", (
|
|
|
381
390
|
let partitionTag = Some(Reventless.DcbTag.Simple({key: "productId"}))
|
|
382
391
|
let cond: Reventless.DcbTag.appendCondition = {
|
|
383
392
|
query: [
|
|
384
|
-
{
|
|
393
|
+
{
|
|
394
|
+
tags: [tag("productId", "p1"), tag("orderId", "o1")],
|
|
395
|
+
eventTypes: ["ProductDemandRecorded"],
|
|
396
|
+
},
|
|
385
397
|
],
|
|
386
398
|
after: "50",
|
|
387
399
|
}
|
|
388
400
|
let items = Runtime.buildConditionalTransactItems(table, [demand], cond, "100", ~partitionTag?)
|
|
389
401
|
|
|
390
|
-
testSync(
|
|
391
|
-
|
|
392
|
-
|
|
402
|
+
testSync(
|
|
403
|
+
"partition tag (productId) is a conditional Update",
|
|
404
|
+
() => {
|
|
405
|
+
expect(isUpdate(findFence(items, "fence#productId:p1")))->toBe(true)
|
|
406
|
+
},
|
|
407
|
+
)
|
|
393
408
|
|
|
394
|
-
testSync(
|
|
395
|
-
|
|
396
|
-
|
|
409
|
+
testSync(
|
|
410
|
+
"other composite tag (orderId) is also a conditional Update — OCC preserved",
|
|
411
|
+
() => {
|
|
412
|
+
expect(isUpdate(findFence(items, "fence#orderId:o1")))->toBe(true)
|
|
413
|
+
},
|
|
414
|
+
)
|
|
397
415
|
})
|
|
398
416
|
|
|
399
417
|
// Cross-partition tag: fence bumped by every carrier (Issue 13 / Phase 7).
|
|
@@ -412,119 +430,171 @@ describe("Runtime.buildConditionalTransactItems — fence-scope = read-scope", (
|
|
|
412
430
|
after: "50",
|
|
413
431
|
}
|
|
414
432
|
|
|
415
|
-
testSync(
|
|
433
|
+
testSync(
|
|
434
|
+
"with studentId cross-partition, its fence is a conditional Update (bump)",
|
|
435
|
+
() => {
|
|
436
|
+
let items = Runtime.buildConditionalTransactItems(
|
|
437
|
+
table,
|
|
438
|
+
[subscribed],
|
|
439
|
+
cond,
|
|
440
|
+
"100",
|
|
441
|
+
~partitionTag?,
|
|
442
|
+
~crossPartitionTagKeys=["studentId"],
|
|
443
|
+
)
|
|
444
|
+
// courseId (partition) bumps as usual; studentId (cross-partition secondary) also bumps.
|
|
445
|
+
expect(isUpdate(findFence(items, "fence#courseId:C1")))->toBe(true)
|
|
446
|
+
expect(isUpdate(findFence(items, "fence#studentId:S1")))->toBe(true)
|
|
447
|
+
},
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
testSync(
|
|
451
|
+
"without the cross-partition flag, studentId is only a read-only ConditionCheck",
|
|
452
|
+
() => {
|
|
453
|
+
let items = Runtime.buildConditionalTransactItems(
|
|
454
|
+
table,
|
|
455
|
+
[subscribed],
|
|
456
|
+
cond,
|
|
457
|
+
"100",
|
|
458
|
+
~partitionTag?,
|
|
459
|
+
)
|
|
460
|
+
let it = findFence(items, "fence#studentId:S1")
|
|
461
|
+
expect(isCheck(it))->toBe(true)
|
|
462
|
+
expect(isUpdate(it))->toBe(false)
|
|
463
|
+
},
|
|
464
|
+
)
|
|
465
|
+
})
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
describe(
|
|
469
|
+
"Runtime.buildConditionalTransactItems — composite partition fences on ONE composite key",
|
|
470
|
+
() => {
|
|
471
|
+
// SyncResource-style: @compositePartitionTag over {environment, platformName,
|
|
472
|
+
// pluginName}. The read is one exact `tag_composite` match, so the fence must be
|
|
473
|
+
// a SINGLE composite-key fence — not one per member. Per-member fencing made the
|
|
474
|
+
// low-cardinality prefix (`environment`, `platformName`) hot under a deploy
|
|
475
|
+
// fan-out. Plan: docs/plans/done/dcb-hot-tag-fence-contention.md.
|
|
476
|
+
let event = (eventType, tags): ReventlessCore.DcbEventLog_Adapter.rawStoredEvent => {
|
|
477
|
+
eventType,
|
|
478
|
+
data: JSON.Object(Dict.make()),
|
|
479
|
+
tags,
|
|
480
|
+
meta: testMeta(),
|
|
481
|
+
}
|
|
482
|
+
let findFence = (
|
|
483
|
+
items: array<AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.transactWriteItem>,
|
|
484
|
+
fenceId,
|
|
485
|
+
) =>
|
|
486
|
+
items->Array.find(it => {
|
|
487
|
+
let idOf = key => key->Dict.get("id") == Some(fenceId->JSON.Encode.string)
|
|
488
|
+
switch (it.update, it.conditionCheck) {
|
|
489
|
+
| (Some(u), _) => idOf(u.key)
|
|
490
|
+
| (_, Some(c)) => idOf(c.key)
|
|
491
|
+
| _ => false
|
|
492
|
+
}
|
|
493
|
+
})
|
|
494
|
+
let isUpdate = it =>
|
|
495
|
+
it
|
|
496
|
+
->Option.flatMap(i => i.AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.update)
|
|
497
|
+
->Option.isSome
|
|
498
|
+
let fenceIds = (
|
|
499
|
+
items: array<AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.transactWriteItem>,
|
|
500
|
+
) =>
|
|
501
|
+
items
|
|
502
|
+
->Array.filterMap(it => {
|
|
503
|
+
let idOf = key => key->Dict.get("id")->Option.flatMap(JSON.Decode.string)
|
|
504
|
+
switch (it.update, it.conditionCheck) {
|
|
505
|
+
| (Some(u), _) => idOf(u.key)
|
|
506
|
+
| (_, Some(c)) => idOf(c.key)
|
|
507
|
+
| _ => None
|
|
508
|
+
}
|
|
509
|
+
})
|
|
510
|
+
->Array.filter(s => s->String.startsWith("fence#"))
|
|
511
|
+
|
|
512
|
+
let spec: Reventless.DcbTag.compositePartitionSpec = {
|
|
513
|
+
keys: ["environment", "platformName", "pluginName"],
|
|
514
|
+
seps: ["/", "/"],
|
|
515
|
+
}
|
|
516
|
+
let partitionTag = Some(Reventless.DcbTag.Composite(spec))
|
|
517
|
+
let members = [
|
|
518
|
+
tag("environment", "prod"),
|
|
519
|
+
tag("platformName", "plat"),
|
|
520
|
+
tag("pluginName", "plug"),
|
|
521
|
+
]
|
|
522
|
+
let compositeFence = "fence#__dcb_composite__:prod/plat/plug"
|
|
523
|
+
|
|
524
|
+
describe("at after=Some (entity exists)", () => {
|
|
525
|
+
let resource = event("ResourceAdded", members)
|
|
526
|
+
let cond: Reventless.DcbTag.appendCondition = {
|
|
527
|
+
query: [{tags: members, eventTypes: ["ResourceAdded"]}],
|
|
528
|
+
after: "50",
|
|
529
|
+
}
|
|
416
530
|
let items = Runtime.buildConditionalTransactItems(
|
|
417
531
|
table,
|
|
418
|
-
[
|
|
532
|
+
[resource],
|
|
419
533
|
cond,
|
|
420
534
|
"100",
|
|
421
535
|
~partitionTag?,
|
|
422
|
-
~crossPartitionTagKeys=["studentId"],
|
|
423
536
|
)
|
|
424
|
-
// courseId (partition) bumps as usual; studentId (cross-partition secondary) also bumps.
|
|
425
|
-
expect(isUpdate(findFence(items, "fence#courseId:C1")))->toBe(true)
|
|
426
|
-
expect(isUpdate(findFence(items, "fence#studentId:S1")))->toBe(true)
|
|
427
|
-
})
|
|
428
|
-
|
|
429
|
-
testSync("without the cross-partition flag, studentId is only a read-only ConditionCheck", () => {
|
|
430
|
-
let items = Runtime.buildConditionalTransactItems(table, [subscribed], cond, "100", ~partitionTag?)
|
|
431
|
-
let it = findFence(items, "fence#studentId:S1")
|
|
432
|
-
expect(isCheck(it))->toBe(true)
|
|
433
|
-
expect(isUpdate(it))->toBe(false)
|
|
434
|
-
})
|
|
435
|
-
})
|
|
436
|
-
})
|
|
437
537
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
let event = (eventType, tags): ReventlessCore.DcbEventLog_Adapter.rawStoredEvent => {
|
|
445
|
-
eventType,
|
|
446
|
-
data: JSON.Object(Dict.make()),
|
|
447
|
-
tags,
|
|
448
|
-
meta: testMeta(),
|
|
449
|
-
}
|
|
450
|
-
let findFence = (
|
|
451
|
-
items: array<AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.transactWriteItem>,
|
|
452
|
-
fenceId,
|
|
453
|
-
) =>
|
|
454
|
-
items->Array.find(it => {
|
|
455
|
-
let idOf = key => key->Dict.get("id") == Some(fenceId->JSON.Encode.string)
|
|
456
|
-
switch (it.update, it.conditionCheck) {
|
|
457
|
-
| (Some(u), _) => idOf(u.key)
|
|
458
|
-
| (_, Some(c)) => idOf(c.key)
|
|
459
|
-
| _ => false
|
|
460
|
-
}
|
|
461
|
-
})
|
|
462
|
-
let isUpdate = it =>
|
|
463
|
-
it->Option.flatMap(i => i.AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.update)->Option.isSome
|
|
464
|
-
let fenceIds = (items: array<AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.transactWriteItem>) =>
|
|
465
|
-
items
|
|
466
|
-
->Array.filterMap(it => {
|
|
467
|
-
let idOf = key => key->Dict.get("id")->Option.flatMap(JSON.Decode.string)
|
|
468
|
-
switch (it.update, it.conditionCheck) {
|
|
469
|
-
| (Some(u), _) => idOf(u.key)
|
|
470
|
-
| (_, Some(c)) => idOf(c.key)
|
|
471
|
-
| _ => None
|
|
472
|
-
}
|
|
473
|
-
})
|
|
474
|
-
->Array.filter(s => s->String.startsWith("fence#"))
|
|
475
|
-
|
|
476
|
-
let spec: Reventless.DcbTag.compositePartitionSpec = {
|
|
477
|
-
keys: ["environment", "platformName", "pluginName"],
|
|
478
|
-
seps: ["/", "/"],
|
|
479
|
-
}
|
|
480
|
-
let partitionTag = Some(Reventless.DcbTag.Composite(spec))
|
|
481
|
-
let members = [tag("environment", "prod"), tag("platformName", "plat"), tag("pluginName", "plug")]
|
|
482
|
-
let compositeFence = "fence#__dcb_composite__:prod/plat/plug"
|
|
483
|
-
|
|
484
|
-
describe("at after=Some (entity exists)", () => {
|
|
485
|
-
let resource = event("ResourceAdded", members)
|
|
486
|
-
let cond: Reventless.DcbTag.appendCondition = {
|
|
487
|
-
query: [{tags: members, eventTypes: ["ResourceAdded"]}],
|
|
488
|
-
after: "50",
|
|
489
|
-
}
|
|
490
|
-
let items = Runtime.buildConditionalTransactItems(table, [resource], cond, "100", ~partitionTag?)
|
|
538
|
+
testSync(
|
|
539
|
+
"the whole composite key is a single conditional Update",
|
|
540
|
+
() => {
|
|
541
|
+
expect(isUpdate(findFence(items, compositeFence)))->toBe(true)
|
|
542
|
+
},
|
|
543
|
+
)
|
|
491
544
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
545
|
+
testSync(
|
|
546
|
+
"no per-member fence is emitted (the hot-fence regression)",
|
|
547
|
+
() => {
|
|
548
|
+
expect(findFence(items, "fence#environment:prod")->Option.isSome)->toBe(false)
|
|
549
|
+
expect(findFence(items, "fence#platformName:plat")->Option.isSome)->toBe(false)
|
|
550
|
+
expect(findFence(items, "fence#pluginName:plug")->Option.isSome)->toBe(false)
|
|
551
|
+
},
|
|
552
|
+
)
|
|
495
553
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
554
|
+
testSync(
|
|
555
|
+
"exactly one fence item total (the composite fence, no members)",
|
|
556
|
+
() => {
|
|
557
|
+
expect(fenceIds(items))->toEqual([compositeFence])
|
|
558
|
+
},
|
|
559
|
+
)
|
|
500
560
|
})
|
|
501
561
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
562
|
+
describe("at after=None (folded create guard)", () => {
|
|
563
|
+
let resource = event("ResourceAdded", members)
|
|
564
|
+
let cond: Reventless.DcbTag.appendCondition = {
|
|
565
|
+
query: [{tags: members, eventTypes: ["ResourceAdded"]}],
|
|
566
|
+
}
|
|
567
|
+
let items = Runtime.buildConditionalTransactItems(
|
|
568
|
+
table,
|
|
569
|
+
[resource],
|
|
570
|
+
cond,
|
|
571
|
+
"100",
|
|
572
|
+
~partitionTag?,
|
|
573
|
+
)
|
|
506
574
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
expect(u.expressionAttributeValues->Option.flatMap(v => v->Dict.get(":after")))->toEqual(None)
|
|
521
|
-
})
|
|
575
|
+
testSync(
|
|
576
|
+
"the composite fence is a create-guard Update gated on attribute_not_exists",
|
|
577
|
+
() => {
|
|
578
|
+
let u =
|
|
579
|
+
findFence(items, compositeFence)
|
|
580
|
+
->Option.flatMap(i => i.AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.update)
|
|
581
|
+
->Option.getOrThrow
|
|
582
|
+
expect(u.conditionExpression)->toEqual(Some("attribute_not_exists(#c0)"))
|
|
583
|
+
expect(u.expressionAttributeValues->Option.flatMap(v => v->Dict.get(":after")))->toEqual(
|
|
584
|
+
None,
|
|
585
|
+
)
|
|
586
|
+
},
|
|
587
|
+
)
|
|
522
588
|
|
|
523
|
-
|
|
524
|
-
|
|
589
|
+
testSync(
|
|
590
|
+
"still exactly one composite fence (no per-member create guards)",
|
|
591
|
+
() => {
|
|
592
|
+
expect(fenceIds(items))->toEqual([compositeFence])
|
|
593
|
+
},
|
|
594
|
+
)
|
|
525
595
|
})
|
|
526
|
-
}
|
|
527
|
-
|
|
596
|
+
},
|
|
597
|
+
)
|
|
528
598
|
|
|
529
599
|
describe("Runtime.toItem — tag_composite is keyed on the event's entity tags", () => {
|
|
530
600
|
// Guards the composite write/read key alignment a composite-partition slice's OCC
|
|
@@ -597,13 +667,16 @@ describe("Runtime.toItem — an empty tag value skips its GSI attribute", () =>
|
|
|
597
667
|
->Dict.get("tags")
|
|
598
668
|
->Option.flatMap(JSON.Decode.array)
|
|
599
669
|
->Option.getOrThrow
|
|
600
|
-
->Array.filterMap(
|
|
601
|
-
t
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
670
|
+
->Array.filterMap(
|
|
671
|
+
t =>
|
|
672
|
+
t
|
|
673
|
+
->JSON.Decode.object
|
|
674
|
+
->Option.map(
|
|
675
|
+
o => (
|
|
676
|
+
o->Dict.get("key")->Option.flatMap(JSON.Decode.string)->Option.getOr(""),
|
|
677
|
+
o->Dict.get("value")->Option.flatMap(JSON.Decode.string)->Option.getOr("!missing"),
|
|
678
|
+
),
|
|
679
|
+
),
|
|
607
680
|
)
|
|
608
681
|
expect(payloadTags)->toEqual([("environment", "prod"), ("componentName", "")])
|
|
609
682
|
})
|
|
@@ -630,7 +703,9 @@ describe("Runtime.buildConditionalTransactItems — folded create guard (after=N
|
|
|
630
703
|
)
|
|
631
704
|
|
|
632
705
|
// True if any item targets a legacy `create#…` sentinel row (must be none now).
|
|
633
|
-
let hasCreateRow = (
|
|
706
|
+
let hasCreateRow = (
|
|
707
|
+
items: array<AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.transactWriteItem>,
|
|
708
|
+
) =>
|
|
634
709
|
items->Array.some(it =>
|
|
635
710
|
switch it.AwsSdk.DynamoDb.DocumentClient.TransactWriteCommand.update {
|
|
636
711
|
| Some(u) =>
|
|
@@ -653,23 +728,34 @@ describe("Runtime.buildConditionalTransactItems — folded create guard (after=N
|
|
|
653
728
|
}
|
|
654
729
|
let items = Runtime.buildConditionalTransactItems(table, [added], cond, "100", ~partitionTag?)
|
|
655
730
|
|
|
656
|
-
testSync(
|
|
657
|
-
|
|
658
|
-
|
|
731
|
+
testSync(
|
|
732
|
+
"no separate create# row is emitted (guard folded into the fence)",
|
|
733
|
+
() => {
|
|
734
|
+
expect(hasCreateRow(items))->toBe(false)
|
|
735
|
+
},
|
|
736
|
+
)
|
|
659
737
|
|
|
660
|
-
testSync(
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
738
|
+
testSync(
|
|
739
|
+
"the partition fence is a conditional Update gated on attribute_not_exists",
|
|
740
|
+
() => {
|
|
741
|
+
let u = fenceUpdate(items, "fence#productId:p1")->Option.getOrThrow
|
|
742
|
+
expect(u.conditionExpression)->toEqual(Some("attribute_not_exists(#c0)"))
|
|
743
|
+
expect(u.updateExpression)->toBe("SET #p0 = :new")
|
|
744
|
+
expect(u.expressionAttributeValues->Option.flatMap(v => v->Dict.get(":after")))->toEqual(
|
|
745
|
+
None,
|
|
746
|
+
)
|
|
747
|
+
},
|
|
748
|
+
)
|
|
666
749
|
|
|
667
|
-
testSync(
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
750
|
+
testSync(
|
|
751
|
+
"the guard references only the produced/consumed type",
|
|
752
|
+
() => {
|
|
753
|
+
let u = fenceUpdate(items, "fence#productId:p1")->Option.getOrThrow
|
|
754
|
+
let referenced = referencedAttrs(u)
|
|
755
|
+
expect(referenced->Array.includes("pos#ProductAdded"))->toBe(true)
|
|
756
|
+
expect(referenced->Array.includes("pos#ProductPriceChanged"))->toBe(false)
|
|
757
|
+
},
|
|
758
|
+
)
|
|
673
759
|
})
|
|
674
760
|
|
|
675
761
|
describe("produced-not-consumed (the double-create gate — analysis con #1)", () => {
|
|
@@ -682,45 +768,55 @@ describe("Runtime.buildConditionalTransactItems — folded create guard (after=N
|
|
|
682
768
|
}
|
|
683
769
|
let items = Runtime.buildConditionalTransactItems(table, [started], cond, "100", ~partitionTag?)
|
|
684
770
|
|
|
685
|
-
testSync(
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
771
|
+
testSync(
|
|
772
|
+
"the produced type is in the guard even though it is not consumed",
|
|
773
|
+
() => {
|
|
774
|
+
let u = fenceUpdate(items, "fence#orderId:o1")->Option.getOrThrow
|
|
775
|
+
let referenced = referencedAttrs(u)
|
|
776
|
+
// guard set = consumed ∪ produced = {OrderDrafted, OrderStarted}
|
|
777
|
+
expect(referenced->Array.includes("pos#OrderStarted"))->toBe(true)
|
|
778
|
+
expect(referenced->Array.includes("pos#OrderDrafted"))->toBe(true)
|
|
779
|
+
},
|
|
780
|
+
)
|
|
692
781
|
|
|
693
|
-
testSync(
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
782
|
+
testSync(
|
|
783
|
+
"the bump advances the produced type",
|
|
784
|
+
() => {
|
|
785
|
+
let u = fenceUpdate(items, "fence#orderId:o1")->Option.getOrThrow
|
|
786
|
+
expect(u.updateExpression)->toBe("SET #p0 = :new")
|
|
787
|
+
expect(u.expressionAttributeNames->Option.flatMap(n => n->Dict.get("#p0")))->toEqual(
|
|
788
|
+
Some("pos#OrderStarted"),
|
|
789
|
+
)
|
|
790
|
+
},
|
|
791
|
+
)
|
|
700
792
|
})
|
|
701
793
|
|
|
702
794
|
describe("at after=Some (entity exists)", () => {
|
|
703
795
|
let changed = event("ProductNameChanged", [tag("productId", "p1")])
|
|
704
796
|
let partitionTag = Some(Reventless.DcbTag.Simple({key: "productId"}))
|
|
705
797
|
let cond: Reventless.DcbTag.appendCondition = {
|
|
706
|
-
query: [
|
|
707
|
-
{tags: [tag("productId", "p1")], eventTypes: ["ProductAdded", "ProductNameChanged"]},
|
|
708
|
-
],
|
|
798
|
+
query: [{tags: [tag("productId", "p1")], eventTypes: ["ProductAdded", "ProductNameChanged"]}],
|
|
709
799
|
after: "50",
|
|
710
800
|
}
|
|
711
801
|
let items = Runtime.buildConditionalTransactItems(table, [changed], cond, "100", ~partitionTag?)
|
|
712
802
|
|
|
713
|
-
testSync(
|
|
714
|
-
|
|
715
|
-
|
|
803
|
+
testSync(
|
|
804
|
+
"emits no create# row",
|
|
805
|
+
() => {
|
|
806
|
+
expect(hasCreateRow(items))->toBe(false)
|
|
807
|
+
},
|
|
808
|
+
)
|
|
716
809
|
|
|
717
|
-
testSync(
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
810
|
+
testSync(
|
|
811
|
+
"the fence check covers consumed types but not unrelated ones (Issue 4 fix)",
|
|
812
|
+
() => {
|
|
813
|
+
let u = fenceUpdate(items, "fence#productId:p1")->Option.getOrThrow
|
|
814
|
+
let referenced = referencedAttrs(u)
|
|
815
|
+
expect(referenced->Array.includes("pos#ProductAdded"))->toBe(true)
|
|
816
|
+
expect(referenced->Array.includes("pos#ProductNameChanged"))->toBe(true)
|
|
817
|
+
expect(referenced->Array.includes("pos#ProductPriceChanged"))->toBe(false)
|
|
818
|
+
},
|
|
819
|
+
)
|
|
724
820
|
})
|
|
725
821
|
})
|
|
726
822
|
|