@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
@@ -129,18 +129,44 @@ let buildLambda = (
129
129
  ~handlerOutputs: array<Pulumi.Output.t<handlerEntry>>,
130
130
  ~packageDirs,
131
131
  ~channelSpecs,
132
+ ~feedQueue: option<PulumiAws.SQS.Queue.t>=None,
132
133
  ~memorySize=1024,
133
134
  ~timeout=30,
134
135
  ) => {
135
136
  let opts = {Pulumi.ComponentResource.parent: parent}
137
+
138
+ // B3.1: on a Postgres-backed platform every slice view table lives in
139
+ // Postgres (slices are never admin-exempt) — one shared `pgConnection` is
140
+ // hoisted to the top level of the compressed config, like base/sourceUrn.
141
+ let qdbSelection = QueryDbBackend.get()
142
+ let pgConnectionFragment = switch qdbSelection {
143
+ | Some(sel) =>
144
+ sel.connectionConfig->Pulumi.Output.apply(cc => {
145
+ let pgConnectionJson =
146
+ [
147
+ ("host", cc.host->JSON.Encode.string),
148
+ ("port", cc.port->Int.toFloat->JSON.Encode.float),
149
+ ("database", cc.database->JSON.Encode.string),
150
+ ("username", cc.username->JSON.Encode.string),
151
+ ("secretArn", cc.secretArn->JSON.Encode.string),
152
+ ]
153
+ ->Dict.fromArray
154
+ ->JSON.Encode.object
155
+ ->JSON.stringify
156
+ `,"pgConnection":${pgConnectionJson}`
157
+ })
158
+ | None => Pulumi.Output.make("")
159
+ }
160
+
136
161
  // Compress HANDLER_CONFIG to stay under AWS Lambda's 4KB env-var ceiling:
137
162
  // hoist the common module-path prefix (`base`) and the shared DCB stream
138
163
  // `sourceUrn` (identical across a plugin's slices) out of the per-handler
139
164
  // entries, and use short keys (s/p/q/u). StateViewSliceEntryPoint re-expands
140
165
  // these. Durable fix (externalize to S3/SSM) tracked in docs/plans.
141
166
  let handlerConfigOutput =
142
- Pulumi.Output.all(handlerOutputs)
143
- ->Pulumi.Output.apply(handlers => {
167
+ (Pulumi.Output.all(handlerOutputs), pgConnectionFragment)
168
+ ->Pulumi.Output.all2
169
+ ->Pulumi.Output.apply(((handlers, pgFragment)) => {
144
170
  let paths = []
145
171
  handlers->Array.forEach(h => {
146
172
  paths->Array.push(h.specModule)->ignore
@@ -175,7 +201,7 @@ let buildLambda = (
175
201
  ->Array.join(",")
176
202
  let baseJson = base->JSON.stringifyAny->Option.getOr(`""`)
177
203
  let urnJson = sharedUrn->JSON.stringifyAny->Option.getOr(`""`)
178
- `{"v":2,"base":${baseJson},"sourceUrn":${urnJson},"handlers":[${entries}]}`
204
+ `{"v":2,"base":${baseJson},"sourceUrn":${urnJson},"handlers":[${entries}]${pgFragment}}`
179
205
  })
180
206
 
181
207
  let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
@@ -201,6 +227,25 @@ let buildLambda = (
201
227
  ~packageDirs,
202
228
  )
203
229
 
230
+ // Postgres-backed view tables → the projection Lambda writes to RDS, so it
231
+ // must land in-VPC on the DB-access security group + private subnets.
232
+ let vpcConfig = switch qdbSelection {
233
+ | Some(sel) =>
234
+ Some(
235
+ sel.securityGroupId
236
+ ->Pulumi.Output.apply(sgId =>
237
+ (
238
+ {
239
+ PulumiAws.Lambda.Function.subnetIds: sel.subnetIds->Pulumi.Input.make,
240
+ securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
241
+ }: PulumiAws.Lambda.Function.vpcConfig
242
+ )
243
+ )
244
+ ->Pulumi.Output.asInput,
245
+ )
246
+ | None => None
247
+ }
248
+
204
249
  let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
205
250
  ~name="AllStateViewSlices",
206
251
  ~code,
@@ -208,15 +253,58 @@ let buildLambda = (
208
253
  ~envVars,
209
254
  ~memorySize,
210
255
  ~timeout,
256
+ ~vpcConfig=?vpcConfig,
211
257
  ~opts,
212
258
  )
213
259
 
260
+ // Grant the Postgres-backed projection Lambda read access to the RDS-managed
261
+ // master secret so PgRuntime can resolve the DB password at cold start.
262
+ switch qdbSelection {
263
+ | Some(sel) =>
264
+ let _ = sel.connectionConfig->Pulumi.Output.apply(cc => {
265
+ open PulumiAws.PolicyDocument
266
+ PulumiAws.IAM.RolePolicy.make(
267
+ ~name="AllStateViewSlices-pgSecret",
268
+ ~args={
269
+ policy: PulumiAws.PolicyDocument.make(
270
+ ~id="AllStateViewSlices-pgSecretPolicy",
271
+ ~statements=[
272
+ {
273
+ sid: "AllowGetPgSecret",
274
+ effect: Allow,
275
+ actions: Action("secretsmanager:GetSecretValue"),
276
+ resources: Resource(cc.secretArn),
277
+ },
278
+ ],
279
+ )
280
+ ->PulumiAws.PolicyDocument.toJsonString
281
+ ->Pulumi.Input.make,
282
+ role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
283
+ },
284
+ )
285
+ })
286
+ | None => ()
287
+ }
288
+
214
289
  let _connectResources = EventCollectorChannel.connect(
215
290
  ~name="AllStateViewSlices",
216
291
  ~channelSpecs,
217
292
  ~runtime,
218
293
  ~opts,
219
294
  )
295
+
296
+ // B3.0: wire the Postgres feed queue into the Lambda (ESM + receive IAM).
297
+ switch feedQueue {
298
+ | Some(queue) =>
299
+ PgProjectionFeed.connect(
300
+ ~name="AllStateViewSlicesFeed",
301
+ ~queue,
302
+ ~lambda=runtime.parts.lambda,
303
+ ~lambdaRole=runtime.parts.lambdaRole,
304
+ ~opts=opts->ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions,
305
+ )
306
+ | None => ()
307
+ }
220
308
  }
221
309
 
222
310
  let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component) =>
@@ -236,6 +324,25 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
236
324
  ~opts={Pulumi.ComponentResource.parent: parent},
237
325
  )
238
326
 
327
+ // B3.0: a Postgres-backed DCB log has no stream resource — provision the
328
+ // SQS feed queue the change-feed relay fans DCB events into. Its ARN is
329
+ // the handlers' sourceUrn dispatch key.
330
+ let feedQueue = DcbBackend.isPostgres()
331
+ ? Some(
332
+ PgProjectionFeed.makeQueue(
333
+ ~name="AllStateViewSlicesFeed",
334
+ ~scope="aws-svs-feed",
335
+ ~includeClassic=false,
336
+ ~includeDcb=true,
337
+ ~opts={Pulumi.CustomResourceOptions.parent: parent},
338
+ ),
339
+ )
340
+ : None
341
+ let feedArnOutput = switch feedQueue {
342
+ | Some(queue) => queue.arn
343
+ | None => Pulumi.Output.make("")
344
+ }
345
+
239
346
  let handlerOutputs: array<Pulumi.Output.t<handlerEntry>> = []
240
347
  let packageDirs: dict<string> = Dict.make()
241
348
  let allQueryDbResources: array<ReventlessInfra.Adapter.resource> = []
@@ -248,8 +355,11 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
248
355
  packageDirs->Dict.set(projectionPkg, Util_Bundle.resolvePackageRoot(projectionPkg))
249
356
 
250
357
  let etResources: array<ReventlessInfra.Adapter.resource> = dcbOutputs.eventTopic.resources
251
- let sourceUrn = etResources->Array.getUnsafe(0)
252
- let sourceUrn = sourceUrn.urn
358
+ // No stream resource (Postgres DCB) → dispatch on the feed queue ARN.
359
+ let sourceUrn = switch etResources->Array.get(0) {
360
+ | Some(resource) => resource.urn
361
+ | None => feedArnOutput
362
+ }
253
363
 
254
364
  let handlerJson =
255
365
  Pulumi.Output.all2((info.queryDbTableName, sourceUrn))
@@ -270,6 +380,7 @@ let finishWithDcbEventLog = (dcbEventLog: ReventlessCore.DcbEventLog.component)
270
380
  ~handlerOutputs,
271
381
  ~packageDirs,
272
382
  ~channelSpecs=[{channel: channel, eventTopics, resources: allQueryDbResources}],
383
+ ~feedQueue,
273
384
  )
274
385
  | None =>
275
386
  log.warn(
@@ -300,6 +411,23 @@ let finish = () =>
300
411
  let handlerOutputs: array<Pulumi.Output.t<handlerEntry>> = []
301
412
  let packageDirs: dict<string> = Dict.make()
302
413
 
414
+ // B3.0: Postgres-backed source logs have no stream — feed queue instead.
415
+ let feedQueue = DcbBackend.isPostgres()
416
+ ? Some(
417
+ PgProjectionFeed.makeQueue(
418
+ ~name="AllStateViewSlicesFeed",
419
+ ~scope="aws-svs-feed",
420
+ ~includeClassic=false,
421
+ ~includeDcb=true,
422
+ ~opts={Pulumi.CustomResourceOptions.parent: parent},
423
+ ),
424
+ )
425
+ : None
426
+ let feedArnOutput = switch feedQueue {
427
+ | Some(queue) => queue.arn
428
+ | None => Pulumi.Output.make("")
429
+ }
430
+
303
431
  storedSpecs->Array.forEach(spec => {
304
432
  switch sliceInfos->Dict.get(spec.componentName) {
305
433
  | Some(info) =>
@@ -309,13 +437,14 @@ let finish = () =>
309
437
  packageDirs->Dict.set(projectionPkg, Util_Bundle.resolvePackageRoot(projectionPkg))
310
438
 
311
439
  let handlerJson =
312
- Pulumi.Output.all2((info.queryDbTableName, spec.sourceUrns))
313
- ->Pulumi.Output.apply(((tableName, urns)) => {
440
+ Pulumi.Output.all3((info.queryDbTableName, spec.sourceUrns, feedArnOutput))
441
+ ->Pulumi.Output.apply(((tableName, urns, feedArn)) => {
314
442
  let entry: handlerEntry = {
315
443
  specModule: info.specModulePath,
316
444
  projectionModule: info.projectionModulePath,
317
445
  queryDbTableName: tableName,
318
- sourceUrn: urns->Array.getUnsafe(0),
446
+ // No stream resource (Postgres) → dispatch on the feed queue ARN.
447
+ sourceUrn: urns->Array.get(0)->Option.getOr(feedArn),
319
448
  }
320
449
  entry
321
450
  })
@@ -333,6 +462,7 @@ let finish = () =>
333
462
  ~handlerOutputs,
334
463
  ~packageDirs,
335
464
  ~channelSpecs=storedSpecs->Array.map(({channelSpec}) => channelSpec),
465
+ ~feedQueue,
336
466
  ~memorySize=maxMemorySize,
337
467
  ~timeout=maxTimeout,
338
468
  )
@@ -1,14 +1,21 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as Aws from "@pulumi/aws";
3
4
  import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
4
5
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
6
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
6
7
  import * as Pulumi from "@pulumi/pulumi";
7
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
8
9
  import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
10
+ import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
9
11
  import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
10
12
  import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
13
+ import * as DcbBackend$ReventlessAws from "../DcbEventLog/DcbBackend.res.mjs";
14
+ import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
11
15
  import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
16
+ import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
17
+ import * as QueryDbBackend$ReventlessAws from "../QueryDb/QueryDbBackend.res.mjs";
18
+ import * as PgProjectionFeed$ReventlessAws from "../Postgres/PgProjectionFeed.res.mjs";
12
19
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
13
20
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
14
21
 
@@ -81,14 +88,45 @@ let finished = {
81
88
  contents: false
82
89
  };
83
90
 
84
- function buildLambda(parent, handlerOutputs, packageDirs, channelSpecs, memorySizeOpt, timeoutOpt) {
91
+ function buildLambda(parent, handlerOutputs, packageDirs, channelSpecs, feedQueueOpt, memorySizeOpt, timeoutOpt) {
92
+ let feedQueue = feedQueueOpt !== undefined ? Primitive_option.valFromOption(feedQueueOpt) : undefined;
85
93
  let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : 1024;
86
94
  let timeout = timeoutOpt !== undefined ? timeoutOpt : 30;
87
95
  let opts_parent = parent;
88
96
  let opts = {
89
97
  parent: opts_parent
90
98
  };
91
- let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => {
99
+ let qdbSelection = QueryDbBackend$ReventlessAws.get();
100
+ let pgConnectionFragment = qdbSelection !== undefined ? qdbSelection.connectionConfig.apply(cc => {
101
+ let pgConnectionJson = JSON.stringify(Object.fromEntries([
102
+ [
103
+ "host",
104
+ cc.host
105
+ ],
106
+ [
107
+ "port",
108
+ cc.port
109
+ ],
110
+ [
111
+ "database",
112
+ cc.database
113
+ ],
114
+ [
115
+ "username",
116
+ cc.username
117
+ ],
118
+ [
119
+ "secretArn",
120
+ cc.secretArn
121
+ ]
122
+ ]));
123
+ return `,"pgConnection":` + pgConnectionJson;
124
+ }) : Pulumi.output("");
125
+ let handlerConfigOutput = Pulumi.all([
126
+ Pulumi.all(handlerOutputs),
127
+ pgConnectionFragment
128
+ ]).apply(param => {
129
+ let handlers = param[0];
92
130
  let paths = [];
93
131
  handlers.forEach(h => {
94
132
  paths.push(h.specModule);
@@ -110,15 +148,33 @@ function buildLambda(parent, handlerOutputs, packageDirs, channelSpecs, memorySi
110
148
  }).join(",");
111
149
  let baseJson = Stdlib_Option.getOr(JSON.stringify(base), `""`);
112
150
  let urnJson = Stdlib_Option.getOr(JSON.stringify(sharedUrn), `""`);
113
- return `{"v":2,"base":` + baseJson + `,"sourceUrn":` + urnJson + `,"handlers":[` + entries + `]}`;
151
+ return `{"v":2,"base":` + baseJson + `,"sourceUrn":` + urnJson + `,"handlers":[` + entries + `]` + param[1] + `}`;
114
152
  });
115
153
  let envVars = {};
116
154
  envVars["HANDLER_CONFIG"] = handlerConfigOutput;
117
155
  packageDirs["@reventlessdev/reventless-aws"] = Util_Bundle$ReventlessAws.resolvePackageRoot("@reventlessdev/reventless-aws");
118
156
  packageDirs["@reventlessdev/reventless-core"] = Util_Bundle$ReventlessAws.resolvePackageRoot("@reventlessdev/reventless-core");
119
157
  let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/StateViewSliceEntryPoint.mjs", packageDirs, undefined);
120
- let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllStateViewSlices", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts);
158
+ let vpcConfig = qdbSelection !== undefined ? qdbSelection.securityGroupId.apply(sgId => ({
159
+ subnetIds: qdbSelection.subnetIds,
160
+ securityGroupIds: [sgId]
161
+ })) : undefined;
162
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllStateViewSlices", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, vpcConfig, opts);
163
+ if (qdbSelection !== undefined) {
164
+ qdbSelection.connectionConfig.apply(cc => new (Aws.iam.RolePolicy)("AllStateViewSlices-pgSecret", {
165
+ policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, "AllStateViewSlices-pgSecretPolicy", [{
166
+ Sid: "AllowGetPgSecret",
167
+ Effect: "Allow",
168
+ Action: "secretsmanager:GetSecretValue",
169
+ Resource: cc.secretArn
170
+ }])),
171
+ role: runtime.parts.lambdaRole.id
172
+ }));
173
+ }
121
174
  EventCollectorChannel_DynamoDbStream$ReventlessAws.connect("AllStateViewSlices", channelSpecs, runtime, opts);
175
+ if (feedQueue !== undefined) {
176
+ return PgProjectionFeed$ReventlessAws.connect("AllStateViewSlicesFeed", feedQueue, runtime.parts.lambda, runtime.parts.lambdaRole, Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions(opts));
177
+ }
122
178
  }
123
179
 
124
180
  function finishWithDcbEventLog(dcbEventLog) {
@@ -138,6 +194,10 @@ function finishWithDcbEventLog(dcbEventLog) {
138
194
  let channel = EventCollectorChannel_DynamoDbStream$ReventlessAws.make("AllStateViewSlices", eventTopics, {
139
195
  parent: parent
140
196
  });
197
+ let feedQueue = DcbBackend$ReventlessAws.isPostgres() ? PgProjectionFeed$ReventlessAws.makeQueue("AllStateViewSlicesFeed", "aws-svs-feed", false, true, {
198
+ parent: parent
199
+ }) : undefined;
200
+ let feedArnOutput = feedQueue !== undefined ? feedQueue.arn : Pulumi.output("");
141
201
  let handlerOutputs = [];
142
202
  let packageDirs = {};
143
203
  let allQueryDbResources = [];
@@ -150,11 +210,11 @@ function finishWithDcbEventLog(dcbEventLog) {
150
210
  let projectionPkg = Util_Bundle$ReventlessAws.extractPackageName(info.projectionModulePath);
151
211
  packageDirs[projectionPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(projectionPkg);
152
212
  let etResources = dcbOutputs.eventTopic.resources;
153
- let sourceUrn = etResources[0];
154
- let sourceUrn$1 = sourceUrn.urn;
213
+ let resource = etResources[0];
214
+ let sourceUrn = resource !== undefined ? resource.urn : feedArnOutput;
155
215
  let handlerJson = Pulumi.all([
156
216
  info.queryDbTableName,
157
- sourceUrn$1
217
+ sourceUrn
158
218
  ]).apply(param => ({
159
219
  specModule: info.specModulePath,
160
220
  projectionModule: info.projectionModulePath,
@@ -167,7 +227,7 @@ function finishWithDcbEventLog(dcbEventLog) {
167
227
  channel: channel,
168
228
  eventTopics: eventTopics,
169
229
  resources: allQueryDbResources
170
- }], undefined, undefined);
230
+ }], Primitive_option.some(feedQueue), undefined, undefined);
171
231
  } else {
172
232
  log.warn("StateViewSliceRuntime_Builder_Single", undefined, "finishWithDcbEventLog: DCB EventLog has no parent");
173
233
  }
@@ -192,6 +252,10 @@ function finish() {
192
252
  if (parent !== undefined) {
193
253
  let handlerOutputs = [];
194
254
  let packageDirs = {};
255
+ let feedQueue = DcbBackend$ReventlessAws.isPostgres() ? PgProjectionFeed$ReventlessAws.makeQueue("AllStateViewSlicesFeed", "aws-svs-feed", false, true, {
256
+ parent: parent
257
+ }) : undefined;
258
+ let feedArnOutput = feedQueue !== undefined ? feedQueue.arn : Pulumi.output("");
195
259
  storedSpecs.forEach(spec => {
196
260
  let info = sliceInfos[spec.componentName];
197
261
  if (info === undefined) {
@@ -203,16 +267,17 @@ function finish() {
203
267
  packageDirs[projectionPkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(projectionPkg);
204
268
  let handlerJson = Pulumi.all([
205
269
  info.queryDbTableName,
206
- spec.sourceUrns
270
+ spec.sourceUrns,
271
+ feedArnOutput
207
272
  ]).apply(param => ({
208
273
  specModule: info.specModulePath,
209
274
  projectionModule: info.projectionModulePath,
210
275
  queryDbTableName: param[0],
211
- sourceUrn: param[1][0]
276
+ sourceUrn: Stdlib_Option.getOr(param[1][0], param[2])
212
277
  }));
213
278
  handlerOutputs.push(handlerJson);
214
279
  });
215
- buildLambda(parent, handlerOutputs, packageDirs, storedSpecs.map(param => param.channelSpec), match[0], match[1]);
280
+ buildLambda(parent, handlerOutputs, packageDirs, storedSpecs.map(param => param.channelSpec), Primitive_option.some(feedQueue), match[0], match[1]);
216
281
  } else {
217
282
  log.warn("StateViewSliceRuntime_Builder_Single", undefined, `finish: grandParent not set`);
218
283
  }
@@ -21,7 +21,7 @@ module Make = (
21
21
  RuntimeEnvironment,
22
22
  CommandGeneratorResolvers,
23
23
  CommandTopicChannel,
24
- EventLogStorage.DynamoDbStream,
24
+ EventLogStorage.Selectable,
25
25
  EventTopicPublisher.DynamoDbStream,
26
26
  EventCollectorChannel,
27
27
  AggregateRuntimeBuilder,
@@ -38,10 +38,17 @@ module Make = (
38
38
  let aggregate = Inner.make(~api, ~opts?)
39
39
 
40
40
  // Extract the event log table name from the aggregate's outputs.
41
- // EventLog.outputs.resources[0] is the DynamoDB table resource.
42
- let eventLogOutputs = (aggregate->Inner.outputs).eventLog
43
- let tableResource = eventLogOutputs.resources->Array.getUnsafe(0)
44
- let eventLogTableName = tableResource.name
41
+ // EventLog.outputs.resources[0] is the DynamoDB table resource. On the
42
+ // Postgres backend there is no table (resources is empty) — the stable
43
+ // `event_log.log_name` (`<Aggregate>EventLog`) stands in for it, both here
44
+ // and in the entry point's Postgres branch.
45
+ let eventLogTableName = if EventLogBackend.isPostgres() {
46
+ Pulumi.Output.make(Spec.name ++ "EventLog")
47
+ } else {
48
+ let eventLogOutputs = (aggregate->Inner.outputs).eventLog
49
+ let tableResource = eventLogOutputs.resources->Array.getUnsafe(0)
50
+ tableResource.name
51
+ }
45
52
 
46
53
  AggregateRuntimeBuilder.registerAggregate(
47
54
  ~aggregateName=Spec.name,
@@ -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 Util_Bundle$ReventlessAws from "../util/Util_Bundle.res.mjs";
5
+ import * as EventLogBackend$ReventlessAws from "../adapter/EventLog/EventLogBackend.res.mjs";
6
+ import * as EventLogStorage$ReventlessAws from "../adapter/EventLog/EventLogStorage.res.mjs";
4
7
  import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
5
8
  import * as Aggregate_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/Aggregate/Aggregate_Builder.res.mjs";
6
9
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "../adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
7
10
  import * as CommandTopicChannel_SQS_Sync$ReventlessAws from "../adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs";
8
- import * as EventLogStorage_DynamoDbStream$ReventlessAws from "../adapter/EventLog/EventLogStorage_DynamoDbStream.res.mjs";
9
11
  import * as AggregateRuntime_Builder_Single$ReventlessAws from "../adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs";
10
12
  import * as CommandGeneratorResolvers_AppSync$ReventlessAws from "../adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res.mjs";
11
13
  import * as EventTopicPublisher_DynamoDbStream$ReventlessAws from "../adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res.mjs";
@@ -24,7 +26,7 @@ function Make(Spec) {
24
26
  asEffectHandler: prim => prim
25
27
  })(CommandGeneratorResolvers_AppSync$ReventlessAws)({
26
28
  make: CommandTopicChannel_SQS_Sync$ReventlessAws.make
27
- })(EventLogStorage_DynamoDbStream$ReventlessAws)(EventTopicPublisher_DynamoDbStream$ReventlessAws)({
29
+ })(EventLogStorage$ReventlessAws.Selectable)(EventTopicPublisher_DynamoDbStream$ReventlessAws)({
28
30
  make: EventCollectorChannel_DynamoDbStream$ReventlessAws.make,
29
31
  connect: EventCollectorChannel_DynamoDbStream$ReventlessAws.connect
30
32
  })({
@@ -42,9 +44,13 @@ function Make(Spec) {
42
44
  })(Plugin_Helpers$ReventlessCore.NoopHooksConfig);
43
45
  let make = (api, opts) => {
44
46
  let aggregate = Inner.make(api, opts);
45
- let eventLogOutputs = Inner.outputs(aggregate).eventLog;
46
- let tableResource = eventLogOutputs.resources[0];
47
- let eventLogTableName = tableResource.name;
47
+ let eventLogTableName;
48
+ if (EventLogBackend$ReventlessAws.isPostgres()) {
49
+ eventLogTableName = Pulumi.output(Spec.name + "EventLog");
50
+ } else {
51
+ let eventLogOutputs = Inner.outputs(aggregate).eventLog;
52
+ eventLogTableName = eventLogOutputs.resources[0].name;
53
+ }
48
54
  AggregateRuntime_Builder_Single$ReventlessAws.registerAggregate(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Behavior.moduleUrl), eventLogTableName);
49
55
  return aggregate;
50
56
  };
@@ -77,4 +83,4 @@ export {
77
83
  AggregateRuntimeBuilder,
78
84
  Make,
79
85
  }
80
- /* Util_Bundle-ReventlessAws Not a pure module */
86
+ /* @pulumi/pulumi Not a pure module */
@@ -21,7 +21,7 @@ module Make = (
21
21
  RuntimeEnvironment,
22
22
  CommandGeneratorResolvers,
23
23
  CommandTopicChannel,
24
- EventLogStorage.DynamoDbStream,
24
+ EventLogStorage.Selectable,
25
25
  EventTopicPublisher.DynamoDbStream,
26
26
  EventCollectorChannel,
27
27
  AggregateRuntimeBuilder,
@@ -38,10 +38,17 @@ module Make = (
38
38
  let aggregate = Inner.make(~api, ~opts?)
39
39
 
40
40
  // Extract the event log table name from the aggregate's outputs.
41
- // EventLog.outputs.resources[0] is the DynamoDB table resource.
42
- let eventLogOutputs = (aggregate->Inner.outputs).eventLog
43
- let tableResource = eventLogOutputs.resources->Array.getUnsafe(0)
44
- let eventLogTableName = tableResource.name
41
+ // EventLog.outputs.resources[0] is the DynamoDB table resource. On the
42
+ // Postgres backend there is no table (resources is empty) — the stable
43
+ // `event_log.log_name` (`<Aggregate>EventLog`) stands in for it, both here
44
+ // and in the entry point's Postgres branch.
45
+ let eventLogTableName = if EventLogBackend.isPostgres() {
46
+ Pulumi.Output.make(Spec.name ++ "EventLog")
47
+ } else {
48
+ let eventLogOutputs = (aggregate->Inner.outputs).eventLog
49
+ let tableResource = eventLogOutputs.resources->Array.getUnsafe(0)
50
+ tableResource.name
51
+ }
45
52
 
46
53
  AggregateRuntimeBuilder.registerAggregate(
47
54
  ~aggregateName=Spec.name,
@@ -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 Util_Bundle$ReventlessAws from "../util/Util_Bundle.res.mjs";
5
+ import * as EventLogBackend$ReventlessAws from "../adapter/EventLog/EventLogBackend.res.mjs";
6
+ import * as EventLogStorage$ReventlessAws from "../adapter/EventLog/EventLogStorage.res.mjs";
4
7
  import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
5
8
  import * as Aggregate_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/Aggregate/Aggregate_Builder.res.mjs";
6
9
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "../adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
7
10
  import * as CommandTopicChannel_SQS_Async$ReventlessAws from "../adapter/CommandTopic/CommandTopicChannel_SQS_Async.res.mjs";
8
- import * as EventLogStorage_DynamoDbStream$ReventlessAws from "../adapter/EventLog/EventLogStorage_DynamoDbStream.res.mjs";
9
11
  import * as CommandGeneratorResolvers_AppSync$ReventlessAws from "../adapter/CommandGenerator/CommandGeneratorResolvers_AppSync.res.mjs";
10
12
  import * as EventTopicPublisher_DynamoDbStream$ReventlessAws from "../adapter/EventTopic/EventTopicPublisher_DynamoDbStream.res.mjs";
11
13
  import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
@@ -24,7 +26,7 @@ function Make(Spec) {
24
26
  asEffectHandler: prim => prim
25
27
  })(CommandGeneratorResolvers_AppSync$ReventlessAws)({
26
28
  make: CommandTopicChannel_SQS_Async$ReventlessAws.make
27
- })(EventLogStorage_DynamoDbStream$ReventlessAws)(EventTopicPublisher_DynamoDbStream$ReventlessAws)({
29
+ })(EventLogStorage$ReventlessAws.Selectable)(EventTopicPublisher_DynamoDbStream$ReventlessAws)({
28
30
  make: EventCollectorChannel_DynamoDbStream$ReventlessAws.make,
29
31
  connect: EventCollectorChannel_DynamoDbStream$ReventlessAws.connect
30
32
  })({
@@ -42,9 +44,13 @@ function Make(Spec) {
42
44
  })(Plugin_Helpers$ReventlessCore.NoopHooksConfig);
43
45
  let make = (api, opts) => {
44
46
  let aggregate = Inner.make(api, opts);
45
- let eventLogOutputs = Inner.outputs(aggregate).eventLog;
46
- let tableResource = eventLogOutputs.resources[0];
47
- let eventLogTableName = tableResource.name;
47
+ let eventLogTableName;
48
+ if (EventLogBackend$ReventlessAws.isPostgres()) {
49
+ eventLogTableName = Pulumi.output(Spec.name + "EventLog");
50
+ } else {
51
+ let eventLogOutputs = Inner.outputs(aggregate).eventLog;
52
+ eventLogTableName = eventLogOutputs.resources[0].name;
53
+ }
48
54
  AggregateRuntime_Builder_Single_Async$ReventlessAws.registerAggregate(Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Behavior.moduleUrl), eventLogTableName);
49
55
  return aggregate;
50
56
  };
@@ -77,4 +83,4 @@ export {
77
83
  AggregateRuntimeBuilder,
78
84
  Make,
79
85
  }
80
- /* Util_Bundle-ReventlessAws Not a pure module */
86
+ /* @pulumi/pulumi Not a pure module */
@@ -34,7 +34,7 @@ function Make(Spec) {
34
34
  let queryDbOutputs = Inner.outputs(readModel).queryDb;
35
35
  let tableResource = queryDbOutputs.resources[0];
36
36
  let queryDbTableName = tableResource.name;
37
- EventCollectorRuntime_Builder_Single$ReventlessAws.registerReadModel(Inner.Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Inner.Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Mappings.moduleUrl), queryDbTableName);
37
+ EventCollectorRuntime_Builder_Single$ReventlessAws.registerReadModel(Inner.Spec.name, Util_Bundle$ReventlessAws.getModuleSpecifier(Inner.Spec.moduleUrl), Util_Bundle$ReventlessAws.getModuleSpecifier(Mappings.moduleUrl), queryDbTableName, undefined);
38
38
  return readModel;
39
39
  };
40
40
  let finish = () => EventCollectorRuntime_Builder_Single$ReventlessAws.finish();
@@ -24,7 +24,7 @@ module Make = (
24
24
  Spec,
25
25
  Mappings,
26
26
  RuntimeEnvironment,
27
- QueryDbStorage_DynamoDbStream,
27
+ QueryDbStorage.SelectableStream,
28
28
  QueryDbResolvers_AppSync_NoOp,
29
29
  EventCollectorChannel,
30
30
  EventCollectorRuntimeBuilder,
@@ -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";
4
7
  import * as ReadModel_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/ReadModel/ReadModel_Builder.res.mjs";
5
8
  import * as RuntimeEnvironment_Lambda$ReventlessAws from "../adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
6
9
  import * as QueryDbResolvers_AppSync_NoOp$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers_AppSync_NoOp.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,9 +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
- })(QueryDbResolvers_AppSync_NoOp$ReventlessAws)({
21
+ })(QueryDbStorage$ReventlessAws.SelectableStream)(QueryDbResolvers_AppSync_NoOp$ReventlessAws)({
22
22
  make: EventCollectorChannel_DynamoDbStream$ReventlessAws.make,
23
23
  connect: EventCollectorChannel_DynamoDbStream$ReventlessAws.connect
24
24
  })({
@@ -31,10 +31,15 @@ function Make(Spec) {
31
31
  });
32
32
  let make = (api, apiRole, allEventTopics, opts) => {
33
33
  let readModel = Inner.make(api, apiRole, allEventTopics, opts);
34
- let queryDbOutputs = Inner.outputs(readModel).queryDb;
35
- let tableResource = queryDbOutputs.resources[0];
36
- let queryDbTableName = tableResource.name;
37
- 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);
38
43
  return readModel;
39
44
  };
40
45
  let finish = () => EventCollectorRuntime_Builder_Single$ReventlessAws.finish();
@@ -62,4 +67,4 @@ export {
62
67
  EventCollectorRuntimeBuilder,
63
68
  Make,
64
69
  }
65
- /* Util_Bundle-ReventlessAws Not a pure module */
70
+ /* @pulumi/pulumi Not a pure module */