@reventlessdev/reventless-aws 3.0.0-alpha.174 → 3.0.0-alpha.176

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.
Files changed (71) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +10 -10
  3. package/src/Platform.res +115 -31
  4. package/src/Platform.res.mjs +217 -44
  5. package/src/adapter/DcbEventLog/DcbBackend.res +2 -2
  6. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +4 -1
  7. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs +1 -1
  8. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res +14 -4
  9. package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs +19 -14
  10. package/src/adapter/EventLog/EventLogBackend.res +82 -0
  11. package/src/adapter/EventLog/EventLogBackend.res.mjs +53 -0
  12. package/src/adapter/EventLog/EventLogStorage.res +15 -0
  13. package/src/adapter/EventLog/EventLogStorage.res.mjs +20 -1
  14. package/src/adapter/EventLog/EventLogStorage_Postgres.res +37 -0
  15. package/src/adapter/EventLog/EventLogStorage_Postgres.res.mjs +21 -0
  16. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +26 -11
  17. package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +15 -6
  18. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +78 -7
  19. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs +30 -2
  20. package/src/adapter/Postgres/PgProjectionFeed.res +100 -0
  21. package/src/adapter/Postgres/PgProjectionFeed.res.mjs +54 -0
  22. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +192 -0
  23. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +141 -0
  24. package/src/adapter/QueryDb/QueryDbBackend.res +52 -0
  25. package/src/adapter/QueryDb/QueryDbBackend.res.mjs +44 -0
  26. package/src/adapter/QueryDb/QueryDbResolvers.res +48 -0
  27. package/src/adapter/QueryDb/QueryDbResolvers.res.mjs +17 -1
  28. package/src/adapter/QueryDb/QueryDbStorage.res +44 -0
  29. package/src/adapter/QueryDb/QueryDbStorage.res.mjs +34 -1
  30. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +43 -0
  31. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res.mjs +21 -0
  32. package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res +19 -0
  33. package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res.mjs +15 -0
  34. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +12 -1
  35. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +2 -1
  36. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +83 -3
  37. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +46 -3
  38. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +83 -3
  39. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +48 -3
  40. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +130 -5
  41. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +66 -6
  42. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +15 -10
  43. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +32 -15
  44. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +26 -12
  45. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +138 -8
  46. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +76 -11
  47. package/src/components/Aggregate_Builder_Single.res +12 -5
  48. package/src/components/Aggregate_Builder_Single.res.mjs +12 -6
  49. package/src/components/Aggregate_Builder_Single_Async.res +12 -5
  50. package/src/components/Aggregate_Builder_Single_Async.res.mjs +12 -6
  51. package/src/components/ReadModel_Builder_NoResolver.res.mjs +1 -1
  52. package/src/components/ReadModel_Builder_NoResolver_Stream.res +13 -4
  53. package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +14 -9
  54. package/src/components/ReadModel_Builder_Single.res +14 -5
  55. package/src/components/ReadModel_Builder_Single.res.mjs +15 -12
  56. package/src/components/ReadModel_Builder_Single_Stream.res +14 -5
  57. package/src/components/ReadModel_Builder_Single_Stream.res.mjs +15 -12
  58. package/src/components/StateViewSlice_Builder.res +10 -4
  59. package/src/components/StateViewSlice_Builder.res.mjs +8 -10
  60. package/src/components/StateViewSlice_Builder_Stream.res +10 -4
  61. package/src/components/StateViewSlice_Builder_Stream.res.mjs +8 -10
  62. package/src/plugin/runtime/PluginRuntime_Builder.res +12 -0
  63. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +4 -0
  64. package/tests/EventCollectorChannelStreamRuntimeTest.res +55 -0
  65. package/tests/EventCollectorChannelStreamRuntimeTest.res.mjs +78 -0
  66. package/tests/PgChangeFeedRelay_IntegrationTest.res +259 -0
  67. package/tests/PgChangeFeedRelay_IntegrationTest.res.mjs +238 -0
  68. package/tests/PgPipeline_IntegrationTest.res +208 -0
  69. package/tests/PgPipeline_IntegrationTest.res.mjs +260 -0
  70. package/tests/PgQueryResolver_LambdaTest.res +206 -0
  71. package/tests/PgQueryResolver_LambdaTest.res.mjs +259 -0
@@ -16,8 +16,8 @@ module Make = (
16
16
  Spec,
17
17
  Mappings,
18
18
  RuntimeEnvironment,
19
- QueryDbStorage.DynamoDb,
20
- QueryDbResolvers.AppSync,
19
+ QueryDbStorage.Selectable,
20
+ QueryDbResolvers.Selectable,
21
21
  EventCollectorChannel,
22
22
  EventCollectorRuntimeBuilder,
23
23
  )
@@ -31,15 +31,24 @@ module Make = (
31
31
  let make = (~api, ~apiRole, ~allEventTopics, ~opts=?): component => {
32
32
  let readModel = Inner.make(~api, ~apiRole, ~allEventTopics, ~opts?)
33
33
 
34
- let queryDbOutputs = (readModel->Inner.outputs).queryDb
35
- let tableResource = queryDbOutputs.resources->Array.getUnsafe(0)
36
- let queryDbTableName = tableResource.name
34
+ // On the Postgres backend there is no DynamoDB table (resources is empty)
35
+ // the read-model spec name is the stable `qdb_<name>` discriminator, both
36
+ // here and in the entry point's Postgres branch.
37
+ let pgBacked = QueryDbBackend.isPostgresFor(Spec.name)
38
+ let queryDbTableName = if pgBacked {
39
+ Pulumi.Output.make(Spec.name)
40
+ } else {
41
+ let queryDbOutputs = (readModel->Inner.outputs).queryDb
42
+ let tableResource = queryDbOutputs.resources->Array.getUnsafe(0)
43
+ tableResource.name
44
+ }
37
45
 
38
46
  EventCollectorRuntimeBuilder.registerReadModel(
39
47
  ~readModelName=Spec.name,
40
48
  ~specModulePath=Util_Bundle.getModuleSpecifier(Spec.moduleUrl),
41
49
  ~mappingsModulePath=Util_Bundle.getModuleSpecifier(Mappings.moduleUrl),
42
50
  ~queryDbTableName,
51
+ ~pgBacked,
43
52
  )
44
53
 
45
54
  readModel
@@ -1,9 +1,11 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Pulumi from "@pulumi/pulumi";
3
4
  import * as Util_Bundle$ReventlessAws from "../util/Util_Bundle.res.mjs";
5
+ import * as QueryDbBackend$ReventlessAws from "../adapter/QueryDb/QueryDbBackend.res.mjs";
6
+ import * as QueryDbStorage$ReventlessAws from "../adapter/QueryDb/QueryDbStorage.res.mjs";
7
+ import * as QueryDbResolvers$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers.res.mjs";
4
8
  import * as ReadModel_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/ReadModel/ReadModel_Builder.res.mjs";
5
- import * as QueryDbStorage_DynamoDb$ReventlessAws from "../adapter/QueryDb/QueryDbStorage_DynamoDb.res.mjs";
6
- import * as QueryDbResolvers_AppSync$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs";
7
9
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "../adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
8
10
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
9
11
  import * as EventCollectorRuntime_Builder_Single$ReventlessAws from "../adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs";
@@ -16,11 +18,7 @@ function Make(Spec) {
16
18
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
17
19
  asEventHandler: prim => prim,
18
20
  asEffectHandler: prim => prim
19
- })({
20
- make: QueryDbStorage_DynamoDb$ReventlessAws.make
21
- })({
22
- make: QueryDbResolvers_AppSync$ReventlessAws.make
23
- })({
21
+ })(QueryDbStorage$ReventlessAws.Selectable)(QueryDbResolvers$ReventlessAws.Selectable)({
24
22
  make: EventCollectorChannel_DynamoDbStream$ReventlessAws.make,
25
23
  connect: EventCollectorChannel_DynamoDbStream$ReventlessAws.connect
26
24
  })({
@@ -33,10 +31,15 @@ function Make(Spec) {
33
31
  });
34
32
  let make = (api, apiRole, allEventTopics, opts) => {
35
33
  let readModel = Inner.make(api, apiRole, allEventTopics, opts);
36
- let queryDbOutputs = Inner.outputs(readModel).queryDb;
37
- let tableResource = queryDbOutputs.resources[0];
38
- let queryDbTableName = tableResource.name;
39
- EventCollectorRuntime_Builder_Single$ReventlessAws.registerReadModel(Inner.Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Inner.Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Mappings.moduleUrl), queryDbTableName);
34
+ let pgBacked = QueryDbBackend$ReventlessAws.isPostgresFor(Inner.Spec.name);
35
+ let queryDbTableName;
36
+ if (pgBacked) {
37
+ queryDbTableName = Pulumi.output(Inner.Spec.name);
38
+ } else {
39
+ let queryDbOutputs = Inner.outputs(readModel).queryDb;
40
+ queryDbTableName = queryDbOutputs.resources[0].name;
41
+ }
42
+ EventCollectorRuntime_Builder_Single$ReventlessAws.registerReadModel(Inner.Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Inner.Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Mappings.moduleUrl), queryDbTableName, pgBacked);
40
43
  return readModel;
41
44
  };
42
45
  let finish = () => EventCollectorRuntime_Builder_Single$ReventlessAws.finish();
@@ -64,4 +67,4 @@ export {
64
67
  EventCollectorRuntimeBuilder,
65
68
  Make,
66
69
  }
67
- /* Util_Bundle-ReventlessAws Not a pure module */
70
+ /* @pulumi/pulumi Not a pure module */
@@ -24,8 +24,8 @@ module Make = (
24
24
  Spec,
25
25
  Mappings,
26
26
  RuntimeEnvironment,
27
- QueryDbStorage_DynamoDbStream,
28
- QueryDbResolvers.AppSync,
27
+ QueryDbStorage.SelectableStream,
28
+ QueryDbResolvers.Selectable,
29
29
  EventCollectorChannel,
30
30
  EventCollectorRuntimeBuilder,
31
31
  )
@@ -39,15 +39,24 @@ module Make = (
39
39
  let make = (~api, ~apiRole, ~allEventTopics, ~opts=?): component => {
40
40
  let readModel = Inner.make(~api, ~apiRole, ~allEventTopics, ~opts?)
41
41
 
42
- let queryDbOutputs = (readModel->Inner.outputs).queryDb
43
- let tableResource = queryDbOutputs.resources->Array.getUnsafe(0)
44
- let queryDbTableName = tableResource.name
42
+ // On the Postgres backend there is no DynamoDB table (resources is empty)
43
+ // the read-model spec name is the stable `qdb_<name>` discriminator, both
44
+ // here and in the entry point's Postgres branch.
45
+ let pgBacked = QueryDbBackend.isPostgresFor(Spec.name)
46
+ let queryDbTableName = if pgBacked {
47
+ Pulumi.Output.make(Spec.name)
48
+ } else {
49
+ let queryDbOutputs = (readModel->Inner.outputs).queryDb
50
+ let tableResource = queryDbOutputs.resources->Array.getUnsafe(0)
51
+ tableResource.name
52
+ }
45
53
 
46
54
  EventCollectorRuntimeBuilder.registerReadModel(
47
55
  ~readModelName=Spec.name,
48
56
  ~specModulePath=Util_Bundle.getModuleSpecifier(Spec.moduleUrl),
49
57
  ~mappingsModulePath=Util_Bundle.getModuleSpecifier(Mappings.moduleUrl),
50
58
  ~queryDbTableName,
59
+ ~pgBacked,
51
60
  )
52
61
 
53
62
  readModel
@@ -1,10 +1,12 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Pulumi from "@pulumi/pulumi";
3
4
  import * as Util_Bundle$ReventlessAws from "../util/Util_Bundle.res.mjs";
5
+ import * as QueryDbBackend$ReventlessAws from "../adapter/QueryDb/QueryDbBackend.res.mjs";
6
+ import * as QueryDbStorage$ReventlessAws from "../adapter/QueryDb/QueryDbStorage.res.mjs";
7
+ import * as QueryDbResolvers$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers.res.mjs";
4
8
  import * as ReadModel_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/ReadModel/ReadModel_Builder.res.mjs";
5
- import * as QueryDbResolvers_AppSync$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs";
6
9
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "../adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
7
- import * as QueryDbStorage_DynamoDbStream$ReventlessAws from "../adapter/QueryDb/QueryDbStorage_DynamoDbStream.res.mjs";
8
10
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
9
11
  import * as EventCollectorRuntime_Builder_Single$ReventlessAws from "../adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs";
10
12
 
@@ -16,11 +18,7 @@ function Make(Spec) {
16
18
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
17
19
  asEventHandler: prim => prim,
18
20
  asEffectHandler: prim => prim
19
- })({
20
- make: QueryDbStorage_DynamoDbStream$ReventlessAws.make
21
- })({
22
- make: QueryDbResolvers_AppSync$ReventlessAws.make
23
- })({
21
+ })(QueryDbStorage$ReventlessAws.SelectableStream)(QueryDbResolvers$ReventlessAws.Selectable)({
24
22
  make: EventCollectorChannel_DynamoDbStream$ReventlessAws.make,
25
23
  connect: EventCollectorChannel_DynamoDbStream$ReventlessAws.connect
26
24
  })({
@@ -33,10 +31,15 @@ function Make(Spec) {
33
31
  });
34
32
  let make = (api, apiRole, allEventTopics, opts) => {
35
33
  let readModel = Inner.make(api, apiRole, allEventTopics, opts);
36
- let queryDbOutputs = Inner.outputs(readModel).queryDb;
37
- let tableResource = queryDbOutputs.resources[0];
38
- let queryDbTableName = tableResource.name;
39
- EventCollectorRuntime_Builder_Single$ReventlessAws.registerReadModel(Inner.Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Inner.Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Mappings.moduleUrl), queryDbTableName);
34
+ let pgBacked = QueryDbBackend$ReventlessAws.isPostgresFor(Inner.Spec.name);
35
+ let queryDbTableName;
36
+ if (pgBacked) {
37
+ queryDbTableName = Pulumi.output(Inner.Spec.name);
38
+ } else {
39
+ let queryDbOutputs = Inner.outputs(readModel).queryDb;
40
+ queryDbTableName = queryDbOutputs.resources[0].name;
41
+ }
42
+ EventCollectorRuntime_Builder_Single$ReventlessAws.registerReadModel(Inner.Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Inner.Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Mappings.moduleUrl), queryDbTableName, pgBacked);
40
43
  return readModel;
41
44
  };
42
45
  let finish = () => EventCollectorRuntime_Builder_Single$ReventlessAws.finish();
@@ -64,4 +67,4 @@ export {
64
67
  EventCollectorRuntimeBuilder,
65
68
  Make,
66
69
  }
67
- /* Util_Bundle-ReventlessAws Not a pure module */
70
+ /* @pulumi/pulumi Not a pure module */
@@ -11,8 +11,8 @@ module Make = (Api: {
11
11
  }) => {
12
12
  module Inner = ReventlessCore.StateViewSlice_Builder.Make(
13
13
  RuntimeEnvironment,
14
- QueryDbStorage.DynamoDb,
15
- QueryDbResolvers.AppSync,
14
+ QueryDbStorage.Selectable,
15
+ QueryDbResolvers.Selectable,
16
16
  EventCollectorChannel,
17
17
  EventCollectorRuntimeBuilder,
18
18
  Api,
@@ -32,12 +32,18 @@ module Make = (Api: {
32
32
  let make = (~dcbEventLog, ~opts=?): component => {
33
33
  let sv = InnerMake.make(~dcbEventLog, ~opts?)
34
34
  let queryDbOutputs = (sv->ReventlessCore.Component.outputs).queryDb
35
- let tableResource = queryDbOutputs.resources->Array.getUnsafe(0)
35
+ // On the Postgres backend there is no DynamoDB table (resources is
36
+ // empty) — the slice spec name is the stable `qdb_<name>` discriminator.
37
+ let queryDbTableName = if QueryDbBackend.isPostgresFor(Spec.name) {
38
+ Pulumi.Output.make(Spec.name)
39
+ } else {
40
+ (queryDbOutputs.resources->Array.getUnsafe(0)).name
41
+ }
36
42
  EventCollectorRuntimeBuilder.registerStateViewSlice(
37
43
  ~name=Spec.name,
38
44
  ~specModulePath=Util_Bundle.getModuleSpecifier(Spec.moduleUrl),
39
45
  ~projectionModulePath=Util_Bundle.getModuleSpecifier(Projection.moduleUrl),
40
- ~queryDbTableName=tableResource.name,
46
+ ~queryDbTableName,
41
47
  ~queryDbResources=queryDbOutputs.resources,
42
48
  )
43
49
  sv
@@ -1,10 +1,12 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Pulumi from "@pulumi/pulumi";
3
4
  import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
4
5
  import * as Util_Bundle$ReventlessAws from "../util/Util_Bundle.res.mjs";
5
- import * as QueryDbStorage_DynamoDb$ReventlessAws from "../adapter/QueryDb/QueryDbStorage_DynamoDb.res.mjs";
6
+ import * as QueryDbBackend$ReventlessAws from "../adapter/QueryDb/QueryDbBackend.res.mjs";
7
+ import * as QueryDbStorage$ReventlessAws from "../adapter/QueryDb/QueryDbStorage.res.mjs";
8
+ import * as QueryDbResolvers$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers.res.mjs";
6
9
  import * as StateViewSlice_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs";
7
- import * as QueryDbResolvers_AppSync$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs";
8
10
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "../adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
9
11
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
10
12
  import * as StateViewSliceRuntime_Builder_Single$ReventlessAws from "../adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs";
@@ -16,11 +18,7 @@ function Make(Api) {
16
18
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
17
19
  asEventHandler: prim => prim,
18
20
  asEffectHandler: prim => prim
19
- })({
20
- make: QueryDbStorage_DynamoDb$ReventlessAws.make
21
- })({
22
- make: QueryDbResolvers_AppSync$ReventlessAws.make
23
- })({
21
+ })(QueryDbStorage$ReventlessAws.Selectable)(QueryDbResolvers$ReventlessAws.Selectable)({
24
22
  make: EventCollectorChannel_DynamoDbStream$ReventlessAws.make,
25
23
  connect: EventCollectorChannel_DynamoDbStream$ReventlessAws.connect
26
24
  })({
@@ -36,8 +34,8 @@ function Make(Api) {
36
34
  let make = (dcbEventLog, opts) => {
37
35
  let sv = InnerMake.make(dcbEventLog, opts);
38
36
  let queryDbOutputs = Component$ReventlessCore.outputs(sv).queryDb;
39
- let tableResource = queryDbOutputs.resources[0];
40
- StateViewSliceRuntime_Builder_Single$ReventlessAws.registerStateViewSlice(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Projection.moduleUrl), tableResource.name, queryDbOutputs.resources);
37
+ let queryDbTableName = QueryDbBackend$ReventlessAws.isPostgresFor(Spec.name) ? Pulumi.output(Spec.name) : queryDbOutputs.resources[0].name;
38
+ StateViewSliceRuntime_Builder_Single$ReventlessAws.registerStateViewSlice(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Projection.moduleUrl), queryDbTableName, queryDbOutputs.resources);
41
39
  return sv;
42
40
  };
43
41
  return {
@@ -65,4 +63,4 @@ export {
65
63
  EventCollectorRuntimeBuilder,
66
64
  Make,
67
65
  }
68
- /* Component-ReventlessCore Not a pure module */
66
+ /* @pulumi/pulumi Not a pure module */
@@ -15,8 +15,8 @@ module Make = (Api: {
15
15
  }) => {
16
16
  module Inner = ReventlessCore.StateViewSlice_Builder.Make(
17
17
  RuntimeEnvironment,
18
- QueryDbStorage_DynamoDbStream,
19
- QueryDbResolvers.AppSync,
18
+ QueryDbStorage.SelectableStream,
19
+ QueryDbResolvers.Selectable,
20
20
  EventCollectorChannel,
21
21
  EventCollectorRuntimeBuilder,
22
22
  Api,
@@ -36,12 +36,18 @@ module Make = (Api: {
36
36
  let make = (~dcbEventLog, ~opts=?): component => {
37
37
  let sv = InnerMake.make(~dcbEventLog, ~opts?)
38
38
  let queryDbOutputs = (sv->ReventlessCore.Component.outputs).queryDb
39
- let tableResource = queryDbOutputs.resources->Array.getUnsafe(0)
39
+ // On the Postgres backend there is no DynamoDB table (resources is
40
+ // empty) — the slice spec name is the stable `qdb_<name>` discriminator.
41
+ let queryDbTableName = if QueryDbBackend.isPostgresFor(Spec.name) {
42
+ Pulumi.Output.make(Spec.name)
43
+ } else {
44
+ (queryDbOutputs.resources->Array.getUnsafe(0)).name
45
+ }
40
46
  EventCollectorRuntimeBuilder.registerStateViewSlice(
41
47
  ~name=Spec.name,
42
48
  ~specModulePath=Util_Bundle.getModuleSpecifier(Spec.moduleUrl),
43
49
  ~projectionModulePath=Util_Bundle.getModuleSpecifier(Projection.moduleUrl),
44
- ~queryDbTableName=tableResource.name,
50
+ ~queryDbTableName,
45
51
  ~queryDbResources=queryDbOutputs.resources,
46
52
  )
47
53
  sv
@@ -1,11 +1,13 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Pulumi from "@pulumi/pulumi";
3
4
  import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
4
5
  import * as Util_Bundle$ReventlessAws from "../util/Util_Bundle.res.mjs";
6
+ import * as QueryDbBackend$ReventlessAws from "../adapter/QueryDb/QueryDbBackend.res.mjs";
7
+ import * as QueryDbStorage$ReventlessAws from "../adapter/QueryDb/QueryDbStorage.res.mjs";
8
+ import * as QueryDbResolvers$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers.res.mjs";
5
9
  import * as StateViewSlice_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs";
6
- import * as QueryDbResolvers_AppSync$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs";
7
10
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "../adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
8
- import * as QueryDbStorage_DynamoDbStream$ReventlessAws from "../adapter/QueryDb/QueryDbStorage_DynamoDbStream.res.mjs";
9
11
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
10
12
  import * as StateViewSliceRuntime_Builder_Single$ReventlessAws from "../adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs";
11
13
 
@@ -16,11 +18,7 @@ function Make(Api) {
16
18
  extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
17
19
  asEventHandler: prim => prim,
18
20
  asEffectHandler: prim => prim
19
- })({
20
- make: QueryDbStorage_DynamoDbStream$ReventlessAws.make
21
- })({
22
- make: QueryDbResolvers_AppSync$ReventlessAws.make
23
- })({
21
+ })(QueryDbStorage$ReventlessAws.SelectableStream)(QueryDbResolvers$ReventlessAws.Selectable)({
24
22
  make: EventCollectorChannel_DynamoDbStream$ReventlessAws.make,
25
23
  connect: EventCollectorChannel_DynamoDbStream$ReventlessAws.connect
26
24
  })({
@@ -36,8 +34,8 @@ function Make(Api) {
36
34
  let make = (dcbEventLog, opts) => {
37
35
  let sv = InnerMake.make(dcbEventLog, opts);
38
36
  let queryDbOutputs = Component$ReventlessCore.outputs(sv).queryDb;
39
- let tableResource = queryDbOutputs.resources[0];
40
- StateViewSliceRuntime_Builder_Single$ReventlessAws.registerStateViewSlice(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Projection.moduleUrl), tableResource.name, queryDbOutputs.resources);
37
+ let queryDbTableName = QueryDbBackend$ReventlessAws.isPostgresFor(Spec.name) ? Pulumi.output(Spec.name) : queryDbOutputs.resources[0].name;
38
+ StateViewSliceRuntime_Builder_Single$ReventlessAws.registerStateViewSlice(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Projection.moduleUrl), queryDbTableName, queryDbOutputs.resources);
41
39
  return sv;
42
40
  };
43
41
  return {
@@ -65,4 +63,4 @@ export {
65
63
  EventCollectorRuntimeBuilder,
66
64
  Make,
67
65
  }
68
- /* Component-ReventlessCore Not a pure module */
66
+ /* @pulumi/pulumi Not a pure module */
@@ -216,6 +216,18 @@ module Make = (
216
216
  }
217
217
  }
218
218
 
219
+ // Classic Postgres logs: `~eventTopics` is keyed by aggregate name, so each key
220
+ // attaches this plugin's EventCollector queue to exactly the aggregates whose
221
+ // event-log DynamoDB stream the collector would have subscribed on the DynamoDB
222
+ // path (admin's extension-point filtering included).
223
+ if EventLogBackend.isPostgres() {
224
+ eventTopics
225
+ ->Dict.keysToArray
226
+ ->Array.forEach(aggregateName =>
227
+ EventLogBackend.attachCollectorQueue(~aggregateName, ~url=queue.id, ~arn=queue.arn)
228
+ )
229
+ }
230
+
219
231
  // Pull the per-EventCollector context registered by Plugin_Helpers.connect
220
232
  // (plugins) or synthesise an admin one if none registered.
221
233
  let context: ReventlessCore.Plugin_Helpers.eventCollectorContext =
@@ -14,6 +14,7 @@ import * as PluginSpec$ReventlessCore from "@reventlessdev/reventless-core/src/p
14
14
  import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
15
15
  import * as ComponentType$ReventlessCore from "@reventlessdev/reventless-core/src/ComponentType.res.mjs";
16
16
  import * as EventCollector$ReventlessCore from "@reventlessdev/reventless-core/src/components/EventCollector/EventCollector.res.mjs";
17
+ import * as EventLogBackend$ReventlessAws from "../../adapter/EventLog/EventLogBackend.res.mjs";
17
18
  import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
18
19
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "../../adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
19
20
  import * as StateChangeSliceRuntime_Builder_Single$ReventlessAws from "../../adapter/Runtime/StateChangeSliceRuntime_Builder_Single.res.mjs";
@@ -174,6 +175,9 @@ function Make(EventCollectorChannel) {
174
175
  DcbBackend$ReventlessAws.attachCollectorQueue(pluginName + "DcbEventLog", queue.id, queue.arn);
175
176
  }
176
177
  }
178
+ if (EventLogBackend$ReventlessAws.isPostgres()) {
179
+ Object.keys(eventTopics).forEach(aggregateName => EventLogBackend$ReventlessAws.attachCollectorQueue(aggregateName, queue.id, queue.arn));
180
+ }
177
181
  let ctx = Plugin_Helpers$ReventlessCore.eventCollectorContextRef.contents[name];
178
182
  let context = ctx !== undefined ? ctx : synthesizeAdminContext();
179
183
  let envVars = {};
@@ -0,0 +1,55 @@
1
+ // B3.0: the ReadModel/StateViewSlice record decoder must accept BOTH DynamoDB
2
+ // stream records (DynamoDB backend) and SQS records (Postgres backend — bodies
3
+ // injected by the PgChangeFeedRelay via the projection feed queue), in one batch.
4
+
5
+ open JestGlobals
6
+
7
+ let mkEvent = (records): PulumiAws.Lambda.CallbackFunction.event => Obj.magic({"Records": records})
8
+
9
+ let sqsRecord = body =>
10
+ {
11
+ "eventSource": "aws:sqs",
12
+ "eventSourceARN": "arn:aws:sqs:eu-west-1:1:AllReadModelsFeed",
13
+ "body": body,
14
+ }
15
+
16
+ let run = event => {
17
+ let seen: array<JSON.t> = []
18
+ let handler: ReventlessCore.EventCollector.jsonEventsHandler = stream =>
19
+ stream
20
+ ->Stream.runForEach(json => Effect.succeed(seen->Array.push(json)->ignore))
21
+ ->Effect.map(_ => ())
22
+ EventCollectorChannel_DynamoDbStream_Runtime.handleStreamEvent(handler, event, ())
23
+ ->Effect.runPromise
24
+ ->Promise.thenResolve(_ => seen)
25
+ }
26
+
27
+ describe("handleStreamEvent (ReadModel/SVS record decoder)", () => {
28
+ test("parses SQS feed-queue records into their JSON bodies", async () => {
29
+ let seen = await run(
30
+ mkEvent([
31
+ sqsRecord(`{"id":"agg-1","meta":{"service":"Product"},"event":{"type":"Added","data":{}}}`),
32
+ sqsRecord(`{"id":"agg-2","meta":{"service":"Product"},"event":{"type":"Added","data":{}}}`),
33
+ ]),
34
+ )
35
+ expect(seen->Array.length)->toBe(2)
36
+ let id0 =
37
+ seen
38
+ ->Array.getUnsafe(0)
39
+ ->JSON.Decode.object
40
+ ->Option.flatMap(o => o->Dict.get("id"))
41
+ ->Option.flatMap(JSON.Decode.string)
42
+ expect(id0)->toEqual(Some("agg-1"))
43
+ })
44
+
45
+ test("skips malformed SQS bodies and unknown sources without failing the batch", async () => {
46
+ let seen = await run(
47
+ mkEvent([
48
+ sqsRecord("not-json"),
49
+ {"eventSource": "aws:kinesis", "eventSourceARN": "arn:x", "body": "{}"},
50
+ sqsRecord(`{"ok":true}`),
51
+ ]),
52
+ )
53
+ expect(seen->Array.length)->toBe(1)
54
+ })
55
+ })
@@ -0,0 +1,78 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Stream from "effect/Stream";
7
+ import * as EventCollectorChannel_DynamoDbStream_Runtime$ReventlessAws from "../src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs";
8
+
9
+ function mkEvent(records) {
10
+ return {
11
+ Records: records
12
+ };
13
+ }
14
+
15
+ function sqsRecord(body) {
16
+ return {
17
+ eventSource: "aws:sqs",
18
+ eventSourceARN: "arn:aws:sqs:eu-west-1:1:AllReadModelsFeed",
19
+ body: body
20
+ };
21
+ }
22
+
23
+ function run(event) {
24
+ let seen = [];
25
+ let handler = stream => Effect.map(Stream.runForEach(stream, json => Effect.succeed((seen.push(json), undefined))), () => {});
26
+ return Effect.runPromise(EventCollectorChannel_DynamoDbStream_Runtime$ReventlessAws.handleStreamEvent(handler, event, undefined)).then(() => seen);
27
+ }
28
+
29
+ globalThis.describe("handleStreamEvent (ReadModel/SVS record decoder)", () => {
30
+ globalThis.test("parses SQS feed-queue records into their JSON bodies", async () => {
31
+ let seen = await run({
32
+ Records: [
33
+ {
34
+ eventSource: "aws:sqs",
35
+ eventSourceARN: "arn:aws:sqs:eu-west-1:1:AllReadModelsFeed",
36
+ body: `{"id":"agg-1","meta":{"service":"Product"},"event":{"type":"Added","data":{}}}`
37
+ },
38
+ {
39
+ eventSource: "aws:sqs",
40
+ eventSourceARN: "arn:aws:sqs:eu-west-1:1:AllReadModelsFeed",
41
+ body: `{"id":"agg-2","meta":{"service":"Product"},"event":{"type":"Added","data":{}}}`
42
+ }
43
+ ]
44
+ });
45
+ globalThis.expect(seen.length).toBe(2);
46
+ let id0 = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(seen[0]), o => o["id"]), Stdlib_JSON.Decode.string);
47
+ globalThis.expect(id0).toEqual("agg-1");
48
+ });
49
+ globalThis.test("skips malformed SQS bodies and unknown sources without failing the batch", async () => {
50
+ let seen = await run({
51
+ Records: [
52
+ {
53
+ eventSource: "aws:sqs",
54
+ eventSourceARN: "arn:aws:sqs:eu-west-1:1:AllReadModelsFeed",
55
+ body: "not-json"
56
+ },
57
+ {
58
+ eventSource: "aws:kinesis",
59
+ eventSourceARN: "arn:x",
60
+ body: "{}"
61
+ },
62
+ {
63
+ eventSource: "aws:sqs",
64
+ eventSourceARN: "arn:aws:sqs:eu-west-1:1:AllReadModelsFeed",
65
+ body: `{"ok":true}`
66
+ }
67
+ ]
68
+ });
69
+ globalThis.expect(seen.length).toBe(1);
70
+ });
71
+ });
72
+
73
+ export {
74
+ mkEvent,
75
+ sqsRecord,
76
+ run,
77
+ }
78
+ /* Not a pure module */