@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.
- package/CHANGELOG.md +21 -0
- package/package.json +10 -10
- package/src/Platform.res +115 -31
- package/src/Platform.res.mjs +217 -44
- package/src/adapter/DcbEventLog/DcbBackend.res +2 -2
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res +4 -1
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs +1 -1
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res +14 -4
- package/src/adapter/EventCollector/EventCollectorChannel_DynamoDbStream_Runtime.res.mjs +19 -14
- package/src/adapter/EventLog/EventLogBackend.res +82 -0
- package/src/adapter/EventLog/EventLogBackend.res.mjs +53 -0
- package/src/adapter/EventLog/EventLogStorage.res +15 -0
- package/src/adapter/EventLog/EventLogStorage.res.mjs +20 -1
- package/src/adapter/EventLog/EventLogStorage_Postgres.res +37 -0
- package/src/adapter/EventLog/EventLogStorage_Postgres.res.mjs +21 -0
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +26 -11
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +15 -6
- package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res +78 -7
- package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs +30 -2
- package/src/adapter/Postgres/PgProjectionFeed.res +100 -0
- package/src/adapter/Postgres/PgProjectionFeed.res.mjs +54 -0
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +192 -0
- package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +141 -0
- package/src/adapter/QueryDb/QueryDbBackend.res +52 -0
- package/src/adapter/QueryDb/QueryDbBackend.res.mjs +44 -0
- package/src/adapter/QueryDb/QueryDbResolvers.res +48 -0
- package/src/adapter/QueryDb/QueryDbResolvers.res.mjs +17 -1
- package/src/adapter/QueryDb/QueryDbStorage.res +44 -0
- package/src/adapter/QueryDb/QueryDbStorage.res.mjs +34 -1
- package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +43 -0
- package/src/adapter/QueryDb/QueryDbStorage_Postgres.res.mjs +21 -0
- package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res +19 -0
- package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res.mjs +15 -0
- package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +12 -1
- package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +2 -1
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +83 -3
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +46 -3
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +83 -3
- package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +48 -3
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +130 -5
- package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +66 -6
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +15 -10
- package/src/adapter/Runtime/ReadModelEntryPoint.mjs +32 -15
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +26 -12
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +138 -8
- package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +76 -11
- package/src/components/Aggregate_Builder_Single.res +12 -5
- package/src/components/Aggregate_Builder_Single.res.mjs +12 -6
- package/src/components/Aggregate_Builder_Single_Async.res +12 -5
- package/src/components/Aggregate_Builder_Single_Async.res.mjs +12 -6
- package/src/components/ReadModel_Builder_NoResolver.res.mjs +1 -1
- package/src/components/ReadModel_Builder_NoResolver_Stream.res +13 -4
- package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +14 -9
- package/src/components/ReadModel_Builder_Single.res +14 -5
- package/src/components/ReadModel_Builder_Single.res.mjs +15 -12
- package/src/components/ReadModel_Builder_Single_Stream.res +14 -5
- package/src/components/ReadModel_Builder_Single_Stream.res.mjs +15 -12
- package/src/components/StateViewSlice_Builder.res +10 -4
- package/src/components/StateViewSlice_Builder.res.mjs +8 -10
- package/src/components/StateViewSlice_Builder_Stream.res +10 -4
- package/src/components/StateViewSlice_Builder_Stream.res.mjs +8 -10
- package/src/plugin/runtime/PluginRuntime_Builder.res +12 -0
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +4 -0
- package/tests/EventCollectorChannelStreamRuntimeTest.res +55 -0
- package/tests/EventCollectorChannelStreamRuntimeTest.res.mjs +78 -0
- package/tests/PgChangeFeedRelay_IntegrationTest.res +259 -0
- package/tests/PgChangeFeedRelay_IntegrationTest.res.mjs +238 -0
- package/tests/PgPipeline_IntegrationTest.res +208 -0
- package/tests/PgPipeline_IntegrationTest.res.mjs +260 -0
- package/tests/PgQueryResolver_LambdaTest.res +206 -0
- package/tests/PgQueryResolver_LambdaTest.res.mjs +259 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Deploy-time QueryDb storage for the Postgres backend (B3.1) — the read-model
|
|
2
|
+
// analogue of `EventLogStorage_Postgres` / `DcbEventLogStorage_Postgres`.
|
|
3
|
+
//
|
|
4
|
+
// Creates NO DynamoDB table and NO AppSync data source: read-model state lives in
|
|
5
|
+
// Postgres (`qdb_<name>`, JSONB) and is written by the projection Lambdas via the
|
|
6
|
+
// runtime ops. Returns:
|
|
7
|
+
// - `resources: []` — no stream, no direct AppSync resolvers (suppressed by
|
|
8
|
+
// `QueryDbResolvers.Selectable` until B3.2's Lambda data source);
|
|
9
|
+
// - `dataSourceName: ""` — nothing to attach resolvers to;
|
|
10
|
+
// - `operations` — the Postgres op set bound to the shared `PgConnection` pool.
|
|
11
|
+
// Schema setup is lazy (first operation), so nothing connects during
|
|
12
|
+
// `pulumi up` — a real query only runs inside a Lambda at runtime.
|
|
13
|
+
//
|
|
14
|
+
// Selected by `QueryDbStorage.Selectable(Stream)` when `QueryDbBackend` holds a
|
|
15
|
+
// selection and the read model is not admin-exempt.
|
|
16
|
+
|
|
17
|
+
type api = Types.AppSync.api
|
|
18
|
+
type role = Types.AppSync.role
|
|
19
|
+
|
|
20
|
+
let make: ReventlessCore.QueryDb_Adapter.storageMaker<api, role> = (
|
|
21
|
+
~name,
|
|
22
|
+
~indexes,
|
|
23
|
+
~subIdField=?,
|
|
24
|
+
~ttl as _=?,
|
|
25
|
+
~api as _,
|
|
26
|
+
~apiRole as _,
|
|
27
|
+
~opts as _,
|
|
28
|
+
) => {
|
|
29
|
+
let operations = switch QueryDbBackend.get() {
|
|
30
|
+
| Some({connectionConfig}) =>
|
|
31
|
+
connectionConfig->Pulumi.Output.apply(config =>
|
|
32
|
+
QueryDbStorage_Postgres_Runtime.opsFor(config, ~name, ~indexes, ~subIdField?)
|
|
33
|
+
)
|
|
34
|
+
| None =>
|
|
35
|
+
// Selectable only routes here when a selection is set; guard defensively.
|
|
36
|
+
JsError.throwWithMessage("QueryDbStorage_Postgres.make called without a QueryDbBackend selection")
|
|
37
|
+
}
|
|
38
|
+
{
|
|
39
|
+
ReventlessCore.QueryDb_Adapter.resources: [],
|
|
40
|
+
dataSourceName: ""->Pulumi.Output.make,
|
|
41
|
+
operations,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Pulumi from "@pulumi/pulumi";
|
|
4
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
5
|
+
import * as QueryDbBackend$ReventlessAws from "./QueryDbBackend.res.mjs";
|
|
6
|
+
import * as QueryDbStorage_Postgres_Runtime$ReventlessAws from "./QueryDbStorage_Postgres_Runtime.res.mjs";
|
|
7
|
+
|
|
8
|
+
function make(name, indexes, subIdField, param, param$1, param$2, param$3) {
|
|
9
|
+
let match = QueryDbBackend$ReventlessAws.get();
|
|
10
|
+
let operations = match !== undefined ? match.connectionConfig.apply(config => QueryDbStorage_Postgres_Runtime$ReventlessAws.opsFor(config, name, indexes, subIdField)) : Stdlib_JsError.throwWithMessage("QueryDbStorage_Postgres.make called without a QueryDbBackend selection");
|
|
11
|
+
return {
|
|
12
|
+
resources: [],
|
|
13
|
+
dataSourceName: Pulumi.output(""),
|
|
14
|
+
operations: operations
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
make,
|
|
20
|
+
}
|
|
21
|
+
/* @pulumi/pulumi Not a pure module */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// AWS-side runtime QueryDb operations for the Postgres backend. Mirrors the role
|
|
2
|
+
// of QueryDbStorage_DynamoDb_Runtime: turn a resolved connection config into the
|
|
3
|
+
// read-model operation set, bound to the container-lifetime pool from PgRuntime.
|
|
4
|
+
// Consumed by ReadModelEntryPoint.mjs / StateViewSliceEntryPoint.mjs (Postgres
|
|
5
|
+
// branch, selected when the handler's HANDLER_CONFIG entry carries a
|
|
6
|
+
// `pgConnection`).
|
|
7
|
+
//
|
|
8
|
+
// `name` is the read-model spec name — the same discriminator the deploy-time
|
|
9
|
+
// maker used, so runtime and deploy-time ops target the same `qdb_<name>` table.
|
|
10
|
+
|
|
11
|
+
let opsFor = (
|
|
12
|
+
config: PgConnection.connectionConfig,
|
|
13
|
+
~name: string,
|
|
14
|
+
~indexes: array<Reventless.ReadModel.indexConfig>=[],
|
|
15
|
+
~subIdField: option<string>=?,
|
|
16
|
+
): ReventlessCore.QueryDb_Adapter.operations => {
|
|
17
|
+
let pool = PgRuntime.poolFor(config)
|
|
18
|
+
ReventlessPostgres.QueryDbStorage_Postgres.makeOperations(~pool, ~name, ~indexes, ~subIdField)
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as PgRuntime$ReventlessAws from "../Postgres/PgRuntime.res.mjs";
|
|
4
|
+
import * as QueryDbStorage_Postgres$ReventlessPostgres from "@reventlessdev/reventless-postgres/src/QueryDbStorage_Postgres.res.mjs";
|
|
5
|
+
|
|
6
|
+
function opsFor(config, name, indexesOpt, subIdField) {
|
|
7
|
+
let indexes = indexesOpt !== undefined ? indexesOpt : [];
|
|
8
|
+
let pool = PgRuntime$ReventlessAws.poolFor(config);
|
|
9
|
+
return QueryDbStorage_Postgres$ReventlessPostgres.makeOperations(pool, name, indexes, subIdField);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
opsFor,
|
|
14
|
+
}
|
|
15
|
+
/* PgRuntime-ReventlessAws Not a pure module */
|
|
@@ -144,7 +144,18 @@ let scanByTableName = (~tableName, ~filterConfigs, ~limit) => {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
let make: ReventlessCore.QueryDb_Adapter.queryEngineMaker = allQueryDbs => {
|
|
147
|
-
|
|
147
|
+
// B3.1: Postgres-backed read models have NO DynamoDB resource — exclude them
|
|
148
|
+
// instead of failing findResource at deploy time. They are not reachable via
|
|
149
|
+
// this engine (getRuntimeResource raises its explicit not-found error at
|
|
150
|
+
// query time); see docs/plans/aws-postgres-querydb-adapter.md § B3.1b.
|
|
151
|
+
let dynamoDbQueryDbs =
|
|
152
|
+
allQueryDbs
|
|
153
|
+
->Dict.toArray
|
|
154
|
+
->Array.filter(((_, queryDb: ReventlessCore.QueryDb.outputs)) =>
|
|
155
|
+
queryDb.resources->Array.length > 0
|
|
156
|
+
)
|
|
157
|
+
->Dict.fromArray
|
|
158
|
+
let allRuntimeQueryDbsOutputs = Dict.mapValues(dynamoDbQueryDbs, (
|
|
148
159
|
queryDb: ReventlessCore.QueryDb.outputs,
|
|
149
160
|
) =>
|
|
150
161
|
queryDb.resources
|
|
@@ -203,7 +203,8 @@ function scanByTableName(tableName, filterConfigs, limit) {
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
function make(allQueryDbs) {
|
|
206
|
-
let
|
|
206
|
+
let dynamoDbQueryDbs = Object.fromEntries(Object.entries(allQueryDbs).filter(param => param[1].resources.length !== 0));
|
|
207
|
+
let allRuntimeQueryDbsOutputs = Pulumi.all(Stdlib_Dict.mapValues(dynamoDbQueryDbs, queryDb => Adapter$ReventlessCore.resourceToResolvedOutput(Util_DynamoDb$ReventlessAws.findResource(queryDb.resources))));
|
|
207
208
|
return allRuntimeQueryDbsOutputs.apply(allRuntimeQueryDbs => ({
|
|
208
209
|
scan: (readModelName, filterConfigs, limit) => scanByTableName(Util_QueryDbRuntime$ReventlessCore.getRuntimeResource(allRuntimeQueryDbs, readModelName).name, filterConfigs, limit),
|
|
209
210
|
query: (readModelName, key, id, subIdConfig, filterConfigs, ascending, limit) => queryByTableName(Util_QueryDbRuntime$ReventlessCore.getRuntimeResource(allRuntimeQueryDbs, readModelName).name, key, id, subIdConfig, filterConfigs, ascending, limit)
|
|
@@ -207,6 +207,30 @@ let finish = () =>
|
|
|
207
207
|
let handlerOutputs: array<Pulumi.Output.t<string>> = []
|
|
208
208
|
let packageDirs: dict<string> = Dict.make()
|
|
209
209
|
|
|
210
|
+
// Classic Postgres backend: add a `pgConnection` object to every handler
|
|
211
|
+
// entry so the AggregateEntryPoint Postgres branch activates (absent →
|
|
212
|
+
// DynamoDB path, unchanged). Whole-Lambda toggle — all aggregates follow
|
|
213
|
+
// the platform selection, matching EventLogStorage.Selectable.
|
|
214
|
+
let pgSelection = EventLogBackend.get()
|
|
215
|
+
let pgConnectionFragment = switch pgSelection {
|
|
216
|
+
| Some(sel) =>
|
|
217
|
+
sel.connectionConfig->Pulumi.Output.apply(cc => {
|
|
218
|
+
let pgConnectionJson =
|
|
219
|
+
[
|
|
220
|
+
("host", cc.host->JSON.Encode.string),
|
|
221
|
+
("port", cc.port->Int.toFloat->JSON.Encode.float),
|
|
222
|
+
("database", cc.database->JSON.Encode.string),
|
|
223
|
+
("username", cc.username->JSON.Encode.string),
|
|
224
|
+
("secretArn", cc.secretArn->JSON.Encode.string),
|
|
225
|
+
]
|
|
226
|
+
->Dict.fromArray
|
|
227
|
+
->JSON.Encode.object
|
|
228
|
+
->JSON.stringify
|
|
229
|
+
`,"pgConnection":${pgConnectionJson}`
|
|
230
|
+
})
|
|
231
|
+
| None => Pulumi.Output.make("")
|
|
232
|
+
}
|
|
233
|
+
|
|
210
234
|
specs->Array.forEach(spec => {
|
|
211
235
|
switch aggregateInfos->Dict.get(spec.aggregateName) {
|
|
212
236
|
| Some(info) =>
|
|
@@ -223,9 +247,14 @@ let finish = () =>
|
|
|
223
247
|
info.behaviorModulePath->JSON.stringifyAny->Option.getOr(`""`)
|
|
224
248
|
|
|
225
249
|
let handlerJson =
|
|
226
|
-
Pulumi.Output.
|
|
227
|
-
|
|
228
|
-
|
|
250
|
+
Pulumi.Output.all4((
|
|
251
|
+
info.eventLogTableName,
|
|
252
|
+
spec.queueUrl,
|
|
253
|
+
spec.queueArn,
|
|
254
|
+
pgConnectionFragment,
|
|
255
|
+
))
|
|
256
|
+
->Pulumi.Output.apply(((table, queueUrl, queueArn, pgFragment)) =>
|
|
257
|
+
`{"specModule":${specModule},"behaviorModule":${behaviorModule},"eventLogTable":"${table}","queueUrl":"${queueUrl}","queueArn":"${queueArn}"${pgFragment}}`
|
|
229
258
|
)
|
|
230
259
|
let _ = handlerOutputs->Array.push(handlerJson)
|
|
231
260
|
| None =>
|
|
@@ -270,6 +299,27 @@ let finish = () =>
|
|
|
270
299
|
// so the next flavor's `finish()` starts from a clean slate.
|
|
271
300
|
cfg.sqsBatchSize->Option.forEach(CommandTopicChannel.setBatchSize)
|
|
272
301
|
|
|
302
|
+
// Postgres-backed classic EventLog → the command Lambda talks to RDS, so it
|
|
303
|
+
// must land in-VPC on the DB-access security group + private subnets.
|
|
304
|
+
// makeFromCodeAsset adds the EC2-ENI IAM automatically when vpcConfig is
|
|
305
|
+
// present (C1).
|
|
306
|
+
let vpcConfig = switch pgSelection {
|
|
307
|
+
| Some(sel) =>
|
|
308
|
+
Some(
|
|
309
|
+
sel.securityGroupId
|
|
310
|
+
->Pulumi.Output.apply(sgId =>
|
|
311
|
+
(
|
|
312
|
+
{
|
|
313
|
+
PulumiAws.Lambda.Function.subnetIds: sel.subnetIds->Pulumi.Input.make,
|
|
314
|
+
securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
|
|
315
|
+
}: PulumiAws.Lambda.Function.vpcConfig
|
|
316
|
+
)
|
|
317
|
+
)
|
|
318
|
+
->Pulumi.Output.asInput,
|
|
319
|
+
)
|
|
320
|
+
| None => None
|
|
321
|
+
}
|
|
322
|
+
|
|
273
323
|
let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
|
|
274
324
|
~name="AllAggregatesCmdHandler",
|
|
275
325
|
~code,
|
|
@@ -280,9 +330,39 @@ let finish = () =>
|
|
|
280
330
|
~reservedConcurrency=?cfg.reservedConcurrency,
|
|
281
331
|
~ephemeralStorageMb=?cfg.ephemeralStorageMb,
|
|
282
332
|
~logRetentionDays=?cfg.logRetentionDays,
|
|
333
|
+
~vpcConfig=?vpcConfig,
|
|
283
334
|
~opts,
|
|
284
335
|
)
|
|
285
336
|
|
|
337
|
+
// Grant the Postgres-backed command Lambda read access to the RDS-managed
|
|
338
|
+
// master secret so PgRuntime can resolve the DB password at cold start.
|
|
339
|
+
switch pgSelection {
|
|
340
|
+
| Some(sel) =>
|
|
341
|
+
let _ = sel.connectionConfig->Pulumi.Output.apply(cc => {
|
|
342
|
+
open PulumiAws.PolicyDocument
|
|
343
|
+
PulumiAws.IAM.RolePolicy.make(
|
|
344
|
+
~name="AllAggregatesCmdHandler-pgSecret",
|
|
345
|
+
~args={
|
|
346
|
+
policy: PulumiAws.PolicyDocument.make(
|
|
347
|
+
~id="AllAggregatesCmdHandler-pgSecretPolicy",
|
|
348
|
+
~statements=[
|
|
349
|
+
{
|
|
350
|
+
sid: "AllowGetPgSecret",
|
|
351
|
+
effect: Allow,
|
|
352
|
+
actions: Action("secretsmanager:GetSecretValue"),
|
|
353
|
+
resources: Resource(cc.secretArn),
|
|
354
|
+
},
|
|
355
|
+
],
|
|
356
|
+
)
|
|
357
|
+
->PulumiAws.PolicyDocument.toJsonString
|
|
358
|
+
->Pulumi.Input.make,
|
|
359
|
+
role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
|
|
360
|
+
},
|
|
361
|
+
)
|
|
362
|
+
})
|
|
363
|
+
| None => ()
|
|
364
|
+
}
|
|
365
|
+
|
|
286
366
|
// Grant the AllAggregates Lambda dynamodb:Scan on the Plugin RM table so
|
|
287
367
|
// the in-Lambda plugin status gate (AggregateEntryPoint.mjs::checkPluginStatus)
|
|
288
368
|
// can read plugin status at command-dispatch time. Skipped if the platform
|
|
@@ -10,6 +10,7 @@ import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/
|
|
|
10
10
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
11
11
|
import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
|
|
12
12
|
import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
13
|
+
import * as EventLogBackend$ReventlessAws from "../EventLog/EventLogBackend.res.mjs";
|
|
13
14
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
|
|
14
15
|
import * as CommandTopicChannel_SQS_Sync$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs";
|
|
15
16
|
import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
|
|
@@ -154,6 +155,32 @@ function finish() {
|
|
|
154
155
|
};
|
|
155
156
|
let handlerOutputs = [];
|
|
156
157
|
let packageDirs = {};
|
|
158
|
+
let pgSelection = EventLogBackend$ReventlessAws.get();
|
|
159
|
+
let pgConnectionFragment = pgSelection !== undefined ? pgSelection.connectionConfig.apply(cc => {
|
|
160
|
+
let pgConnectionJson = JSON.stringify(Object.fromEntries([
|
|
161
|
+
[
|
|
162
|
+
"host",
|
|
163
|
+
cc.host
|
|
164
|
+
],
|
|
165
|
+
[
|
|
166
|
+
"port",
|
|
167
|
+
cc.port
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
"database",
|
|
171
|
+
cc.database
|
|
172
|
+
],
|
|
173
|
+
[
|
|
174
|
+
"username",
|
|
175
|
+
cc.username
|
|
176
|
+
],
|
|
177
|
+
[
|
|
178
|
+
"secretArn",
|
|
179
|
+
cc.secretArn
|
|
180
|
+
]
|
|
181
|
+
]));
|
|
182
|
+
return `,"pgConnection":` + pgConnectionJson;
|
|
183
|
+
}) : Pulumi.output("");
|
|
157
184
|
specs.forEach(spec => {
|
|
158
185
|
let info = aggregateInfos[spec.aggregateName];
|
|
159
186
|
if (info === undefined) {
|
|
@@ -168,8 +195,9 @@ function finish() {
|
|
|
168
195
|
let handlerJson = Pulumi.all([
|
|
169
196
|
info.eventLogTableName,
|
|
170
197
|
spec.queueUrl,
|
|
171
|
-
spec.queueArn
|
|
172
|
-
|
|
198
|
+
spec.queueArn,
|
|
199
|
+
pgConnectionFragment
|
|
200
|
+
]).apply(param => `{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + param[3] + `}`);
|
|
173
201
|
handlerOutputs.push(handlerJson);
|
|
174
202
|
});
|
|
175
203
|
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => `{"handlers":[` + handlers.join(",") + `]}`);
|
|
@@ -187,7 +215,22 @@ function finish() {
|
|
|
187
215
|
}));
|
|
188
216
|
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
|
|
189
217
|
Stdlib_Option.forEach(cfg.sqsBatchSize, CommandTopicChannel_SQS_Sync$ReventlessAws.setBatchSize);
|
|
190
|
-
let
|
|
218
|
+
let vpcConfig = pgSelection !== undefined ? pgSelection.securityGroupId.apply(sgId => ({
|
|
219
|
+
subnetIds: pgSelection.subnetIds,
|
|
220
|
+
securityGroupIds: [sgId]
|
|
221
|
+
})) : undefined;
|
|
222
|
+
let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAggregatesCmdHandler", match.code, match.sourceCodeHash, envVars, cfg.memorySize, cfg.timeout, cfg.reservedConcurrency, cfg.ephemeralStorageMb, cfg.logRetentionDays, undefined, vpcConfig, opts);
|
|
223
|
+
if (pgSelection !== undefined) {
|
|
224
|
+
pgSelection.connectionConfig.apply(cc => new (Aws.iam.RolePolicy)("AllAggregatesCmdHandler-pgSecret", {
|
|
225
|
+
policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, "AllAggregatesCmdHandler-pgSecretPolicy", [{
|
|
226
|
+
Sid: "AllowGetPgSecret",
|
|
227
|
+
Effect: "Allow",
|
|
228
|
+
Action: "secretsmanager:GetSecretValue",
|
|
229
|
+
Resource: cc.secretArn
|
|
230
|
+
}])),
|
|
231
|
+
role: runtime.parts.lambdaRole.id
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
191
234
|
let tableName$1 = pluginRmTableName.contents;
|
|
192
235
|
if (tableName$1 !== undefined) {
|
|
193
236
|
Pulumi.all([
|
|
@@ -194,6 +194,30 @@ let finish = () =>
|
|
|
194
194
|
let handlerOutputs: array<Pulumi.Output.t<string>> = []
|
|
195
195
|
let packageDirs: dict<string> = Dict.make()
|
|
196
196
|
|
|
197
|
+
// Classic Postgres backend: add a `pgConnection` object to every handler
|
|
198
|
+
// entry so the AggregateEntryPoint Postgres branch activates (absent →
|
|
199
|
+
// DynamoDB path, unchanged). Whole-Lambda toggle — all aggregates follow
|
|
200
|
+
// the platform selection, matching EventLogStorage.Selectable.
|
|
201
|
+
let pgSelection = EventLogBackend.get()
|
|
202
|
+
let pgConnectionFragment = switch pgSelection {
|
|
203
|
+
| Some(sel) =>
|
|
204
|
+
sel.connectionConfig->Pulumi.Output.apply(cc => {
|
|
205
|
+
let pgConnectionJson =
|
|
206
|
+
[
|
|
207
|
+
("host", cc.host->JSON.Encode.string),
|
|
208
|
+
("port", cc.port->Int.toFloat->JSON.Encode.float),
|
|
209
|
+
("database", cc.database->JSON.Encode.string),
|
|
210
|
+
("username", cc.username->JSON.Encode.string),
|
|
211
|
+
("secretArn", cc.secretArn->JSON.Encode.string),
|
|
212
|
+
]
|
|
213
|
+
->Dict.fromArray
|
|
214
|
+
->JSON.Encode.object
|
|
215
|
+
->JSON.stringify
|
|
216
|
+
`,"pgConnection":${pgConnectionJson}`
|
|
217
|
+
})
|
|
218
|
+
| None => Pulumi.Output.make("")
|
|
219
|
+
}
|
|
220
|
+
|
|
197
221
|
specs->Array.forEach(spec => {
|
|
198
222
|
switch aggregateInfos->Dict.get(spec.aggregateName) {
|
|
199
223
|
| Some(info) =>
|
|
@@ -208,9 +232,14 @@ let finish = () =>
|
|
|
208
232
|
info.behaviorModulePath->JSON.stringifyAny->Option.getOr(`""`)
|
|
209
233
|
|
|
210
234
|
let handlerJson =
|
|
211
|
-
Pulumi.Output.
|
|
212
|
-
|
|
213
|
-
|
|
235
|
+
Pulumi.Output.all4((
|
|
236
|
+
info.eventLogTableName,
|
|
237
|
+
spec.queueUrl,
|
|
238
|
+
spec.queueArn,
|
|
239
|
+
pgConnectionFragment,
|
|
240
|
+
))
|
|
241
|
+
->Pulumi.Output.apply(((table, queueUrl, queueArn, pgFragment)) =>
|
|
242
|
+
`{"specModule":${specModule},"behaviorModule":${behaviorModule},"eventLogTable":"${table}","queueUrl":"${queueUrl}","queueArn":"${queueArn}"${pgFragment}}`
|
|
214
243
|
)
|
|
215
244
|
let _ = handlerOutputs->Array.push(handlerJson)
|
|
216
245
|
| None =>
|
|
@@ -249,6 +278,27 @@ let finish = () =>
|
|
|
249
278
|
|
|
250
279
|
cfg.sqsBatchSize->Option.forEach(CommandTopicChannel.setBatchSize)
|
|
251
280
|
|
|
281
|
+
// Postgres-backed classic EventLog → the command Lambda talks to RDS, so it
|
|
282
|
+
// must land in-VPC on the DB-access security group + private subnets.
|
|
283
|
+
// makeFromCodeAsset adds the EC2-ENI IAM automatically when vpcConfig is
|
|
284
|
+
// present (C1).
|
|
285
|
+
let vpcConfig = switch pgSelection {
|
|
286
|
+
| Some(sel) =>
|
|
287
|
+
Some(
|
|
288
|
+
sel.securityGroupId
|
|
289
|
+
->Pulumi.Output.apply(sgId =>
|
|
290
|
+
(
|
|
291
|
+
{
|
|
292
|
+
PulumiAws.Lambda.Function.subnetIds: sel.subnetIds->Pulumi.Input.make,
|
|
293
|
+
securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
|
|
294
|
+
}: PulumiAws.Lambda.Function.vpcConfig
|
|
295
|
+
)
|
|
296
|
+
)
|
|
297
|
+
->Pulumi.Output.asInput,
|
|
298
|
+
)
|
|
299
|
+
| None => None
|
|
300
|
+
}
|
|
301
|
+
|
|
252
302
|
let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
|
|
253
303
|
~name="AllAggregatesAsyncCmdHandler",
|
|
254
304
|
~code,
|
|
@@ -259,9 +309,39 @@ let finish = () =>
|
|
|
259
309
|
~reservedConcurrency=?cfg.reservedConcurrency,
|
|
260
310
|
~ephemeralStorageMb=?cfg.ephemeralStorageMb,
|
|
261
311
|
~logRetentionDays=?cfg.logRetentionDays,
|
|
312
|
+
~vpcConfig=?vpcConfig,
|
|
262
313
|
~opts,
|
|
263
314
|
)
|
|
264
315
|
|
|
316
|
+
// Grant the Postgres-backed command Lambda read access to the RDS-managed
|
|
317
|
+
// master secret so PgRuntime can resolve the DB password at cold start.
|
|
318
|
+
switch pgSelection {
|
|
319
|
+
| Some(sel) =>
|
|
320
|
+
let _ = sel.connectionConfig->Pulumi.Output.apply(cc => {
|
|
321
|
+
open PulumiAws.PolicyDocument
|
|
322
|
+
PulumiAws.IAM.RolePolicy.make(
|
|
323
|
+
~name="AllAggregatesAsyncCmdHandler-pgSecret",
|
|
324
|
+
~args={
|
|
325
|
+
policy: PulumiAws.PolicyDocument.make(
|
|
326
|
+
~id="AllAggregatesAsyncCmdHandler-pgSecretPolicy",
|
|
327
|
+
~statements=[
|
|
328
|
+
{
|
|
329
|
+
sid: "AllowGetPgSecret",
|
|
330
|
+
effect: Allow,
|
|
331
|
+
actions: Action("secretsmanager:GetSecretValue"),
|
|
332
|
+
resources: Resource(cc.secretArn),
|
|
333
|
+
},
|
|
334
|
+
],
|
|
335
|
+
)
|
|
336
|
+
->PulumiAws.PolicyDocument.toJsonString
|
|
337
|
+
->Pulumi.Input.make,
|
|
338
|
+
role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
|
|
339
|
+
},
|
|
340
|
+
)
|
|
341
|
+
})
|
|
342
|
+
| None => ()
|
|
343
|
+
}
|
|
344
|
+
|
|
265
345
|
specs->Array.forEach(({connects}) => {
|
|
266
346
|
connects->Array.forEach(connect => connect(~runtime))
|
|
267
347
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
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";
|
|
@@ -7,7 +8,9 @@ import * as Pulumi from "@pulumi/pulumi";
|
|
|
7
8
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
8
9
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
9
10
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
11
|
+
import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
|
|
10
12
|
import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
13
|
+
import * as EventLogBackend$ReventlessAws from "../EventLog/EventLogBackend.res.mjs";
|
|
11
14
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
|
|
12
15
|
import * as CommandTopicChannel_SQS_Async$ReventlessAws from "../CommandTopic/CommandTopicChannel_SQS_Async.res.mjs";
|
|
13
16
|
import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
|
|
@@ -144,6 +147,32 @@ function finish() {
|
|
|
144
147
|
};
|
|
145
148
|
let handlerOutputs = [];
|
|
146
149
|
let packageDirs = {};
|
|
150
|
+
let pgSelection = EventLogBackend$ReventlessAws.get();
|
|
151
|
+
let pgConnectionFragment = pgSelection !== undefined ? pgSelection.connectionConfig.apply(cc => {
|
|
152
|
+
let pgConnectionJson = JSON.stringify(Object.fromEntries([
|
|
153
|
+
[
|
|
154
|
+
"host",
|
|
155
|
+
cc.host
|
|
156
|
+
],
|
|
157
|
+
[
|
|
158
|
+
"port",
|
|
159
|
+
cc.port
|
|
160
|
+
],
|
|
161
|
+
[
|
|
162
|
+
"database",
|
|
163
|
+
cc.database
|
|
164
|
+
],
|
|
165
|
+
[
|
|
166
|
+
"username",
|
|
167
|
+
cc.username
|
|
168
|
+
],
|
|
169
|
+
[
|
|
170
|
+
"secretArn",
|
|
171
|
+
cc.secretArn
|
|
172
|
+
]
|
|
173
|
+
]));
|
|
174
|
+
return `,"pgConnection":` + pgConnectionJson;
|
|
175
|
+
}) : Pulumi.output("");
|
|
147
176
|
specs.forEach(spec => {
|
|
148
177
|
let info = aggregateInfos[spec.aggregateName];
|
|
149
178
|
if (info === undefined) {
|
|
@@ -158,8 +187,9 @@ function finish() {
|
|
|
158
187
|
let handlerJson = Pulumi.all([
|
|
159
188
|
info.eventLogTableName,
|
|
160
189
|
spec.queueUrl,
|
|
161
|
-
spec.queueArn
|
|
162
|
-
|
|
190
|
+
spec.queueArn,
|
|
191
|
+
pgConnectionFragment
|
|
192
|
+
]).apply(param => `{"specModule":` + specModule + `,"behaviorModule":` + behaviorModule + `,"eventLogTable":"` + param[0] + `","queueUrl":"` + param[1] + `","queueArn":"` + param[2] + `"` + param[3] + `}`);
|
|
163
193
|
handlerOutputs.push(handlerJson);
|
|
164
194
|
});
|
|
165
195
|
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => `{"handlers":[` + handlers.join(",") + `]}`);
|
|
@@ -174,7 +204,22 @@ function finish() {
|
|
|
174
204
|
}));
|
|
175
205
|
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs", packageDirs, undefined);
|
|
176
206
|
Stdlib_Option.forEach(cfg.sqsBatchSize, CommandTopicChannel_SQS_Async$ReventlessAws.setBatchSize);
|
|
177
|
-
let
|
|
207
|
+
let vpcConfig = pgSelection !== undefined ? pgSelection.securityGroupId.apply(sgId => ({
|
|
208
|
+
subnetIds: pgSelection.subnetIds,
|
|
209
|
+
securityGroupIds: [sgId]
|
|
210
|
+
})) : undefined;
|
|
211
|
+
let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllAggregatesAsyncCmdHandler", match.code, match.sourceCodeHash, envVars, cfg.memorySize, cfg.timeout, cfg.reservedConcurrency, cfg.ephemeralStorageMb, cfg.logRetentionDays, undefined, vpcConfig, opts);
|
|
212
|
+
if (pgSelection !== undefined) {
|
|
213
|
+
pgSelection.connectionConfig.apply(cc => new (Aws.iam.RolePolicy)("AllAggregatesAsyncCmdHandler-pgSecret", {
|
|
214
|
+
policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, "AllAggregatesAsyncCmdHandler-pgSecretPolicy", [{
|
|
215
|
+
Sid: "AllowGetPgSecret",
|
|
216
|
+
Effect: "Allow",
|
|
217
|
+
Action: "secretsmanager:GetSecretValue",
|
|
218
|
+
Resource: cc.secretArn
|
|
219
|
+
}])),
|
|
220
|
+
role: runtime.parts.lambdaRole.id
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
178
223
|
specs.forEach(param => {
|
|
179
224
|
param.connects.forEach(connect => connect(runtime));
|
|
180
225
|
});
|