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

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 CHANGED
@@ -3,6 +3,15 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.177 (2026-07-06)
7
+
8
+ ### Features
9
+
10
+ * **reventless-aws:** AppSync read path for Postgres read models (B3.2b) ([a6dd6cb](https://github.com/ReventlessDev/reventless-core/commit/a6dd6cbddfc65410bf96da49bb2603c88670aced))
11
+ * **reventless-aws:** cross-table @resolves/[@resolves](https://github.com/resolves)Many + node resolvers for Postgres reads (B3.2c) ([713b6ab](https://github.com/ReventlessDev/reventless-core/commit/713b6ab736715209e7198b2b4ab609c5447adef5))
12
+ * **reventless-postgres,reventless-aws:** items (sub-id connection) resolver for Postgres reads (B3.2c) ([2477220](https://github.com/ReventlessDev/reventless-core/commit/247722062913f5d0bcb5895b152f05033d8297d8))
13
+
14
+
6
15
  # 3.0.0-alpha.176 (2026-07-06)
7
16
 
8
17
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-aws",
3
- "version": "3.0.0-alpha.176",
3
+ "version": "3.0.0-alpha.177",
4
4
  "description": "AWS adapters for Reventless",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
@@ -9,16 +9,16 @@
9
9
  "sury": "11.0.0-alpha.4",
10
10
  "uuid": "^13.0.0",
11
11
  "@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.20",
12
- "@reventlessdev/rescript-effect": "0.1.0-alpha.25",
13
12
  "@reventlessdev/rescript-jest": "1.0.0-alpha.6",
13
+ "@reventlessdev/rescript-effect": "0.1.0-alpha.25",
14
14
  "@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.45",
15
15
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.14",
16
16
  "@reventlessdev/rescript-uuid": "1.1.0-alpha.14",
17
- "@reventlessdev/reventless-interop": "3.0.0-alpha.24",
17
+ "@reventlessdev/reventless-core": "3.0.0-alpha.143",
18
18
  "@reventlessdev/reventless-infra": "3.0.0-alpha.89",
19
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.5",
20
- "@reventlessdev/reventless-spec": "3.0.0-alpha.67",
21
- "@reventlessdev/reventless-core": "3.0.0-alpha.143"
19
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.6",
20
+ "@reventlessdev/reventless-interop": "3.0.0-alpha.24",
21
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.67"
22
22
  },
23
23
  "devDependencies": {
24
24
  "rescript": "^12.3.0",
package/src/Platform.res CHANGED
@@ -1470,6 +1470,18 @@ module MakeWithConfig = (
1470
1470
  // built above, so the DcbBackend relay registry is complete).
1471
1471
  provisionPgChangeFeedRelay()
1472
1472
 
1473
+ // B3.2b: provision the shared PgQueryResolver Lambda + AppSync data source for
1474
+ // Postgres-backed read models (monolithic mode). Runs after construct so the
1475
+ // resolver-binding registry (QueryDbResolvers_Lambda.make) and the ReadModel
1476
+ // spec-module registry are complete; it fulfils the deferred `dataSourceName`
1477
+ // the Postgres storage maker handed to the (schema-pushed) resolvers. App read
1478
+ // model resolvers attach to the Domain API. Plugin-stack mode is deferred —
1479
+ // it needs a per-stack data-source name to avoid collisions on the shared API.
1480
+ switch QueryDbBackend.get() {
1481
+ | Some(sel) => PgQueryResolver_Builder.provision(~api=domainApi, ~selection=sel, ~opts={})
1482
+ | None => ()
1483
+ }
1484
+
1473
1485
  // Admin schema push is fired by preAdminResolversSchemaHook from inside
1474
1486
  // Admin.construct (with createResolvers gated on it). Only exports below.
1475
1487
  if Config.splitApi {
@@ -61,6 +61,7 @@ import * as Task_Builder_PerBucket$ReventlessAws from "./components/Task_Builder
61
61
  import * as AutomationSlice_Builder$ReventlessAws from "./components/AutomationSlice_Builder.res.mjs";
62
62
  import * as EventTopicPublisher_SNS$ReventlessAws from "./adapter/EventTopic/EventTopicPublisher_SNS.res.mjs";
63
63
  import * as ExtensionPointMapping$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/ExtensionPointMapping.res.mjs";
64
+ import * as PgQueryResolver_Builder$ReventlessAws from "./adapter/QueryDb/PgQueryResolver_Builder.res.mjs";
64
65
  import * as Aggregate_Builder_Single$ReventlessAws from "./components/Aggregate_Builder_Single.res.mjs";
65
66
  import * as ReadModel_Builder_Single$ReventlessAws from "./components/ReadModel_Builder_Single.res.mjs";
66
67
  import * as StateChangeSlice_Builder$ReventlessAws from "./components/StateChangeSlice_Builder.res.mjs";
@@ -981,6 +982,10 @@ function MakeWithConfig(Config) {
981
982
  Plugin_Helpers$ReventlessCore.exportPluginOutputs(Component$ReventlessCore.outputs(Primitive_option.valFromOption(pluginComponent)));
982
983
  }
983
984
  provisionPgChangeFeedRelay();
985
+ let sel = QueryDbBackend$ReventlessAws.get();
986
+ if (sel !== undefined) {
987
+ PgQueryResolver_Builder$ReventlessAws.provision(domainApi, sel, {});
988
+ }
984
989
  if (Config.splitApi) {
985
990
  Pulumi$Pulumi.$$export("platformApiId", Output$Pulumi.flatMap(platformApi, api => api.id));
986
991
  Pulumi$Pulumi.$$export("platformApiEndpoint", Output$Pulumi.flatMap(platformApi, api => api.uris.apply(uris => uris.GRAPHQL)));
@@ -2185,6 +2190,10 @@ function Make($star) {
2185
2190
  Plugin_Helpers$ReventlessCore.exportPluginOutputs(Component$ReventlessCore.outputs(Primitive_option.valFromOption(pluginComponent)));
2186
2191
  }
2187
2192
  provisionPgChangeFeedRelay();
2193
+ let sel = QueryDbBackend$ReventlessAws.get();
2194
+ if (sel !== undefined) {
2195
+ PgQueryResolver_Builder$ReventlessAws.provision(domainApi, sel, {});
2196
+ }
2188
2197
  Pulumi$Pulumi.$$export("platformApiId", Output$Pulumi.flatMap(platformApi, api => api.id));
2189
2198
  Pulumi$Pulumi.$$export("platformApiEndpoint", Output$Pulumi.flatMap(platformApi, api => api.uris.apply(uris => uris.GRAPHQL)));
2190
2199
  Pulumi$Pulumi.$$export("platformApiRoleArn", Output$Pulumi.flatMap(platformApiRole, role => role.arn));
@@ -0,0 +1,281 @@
1
+ // Deploy-time provisioner for the shared Postgres GraphQL-read Lambda (B3.2b).
2
+ //
3
+ // Postgres-backed read models have no per-table AppSync data source, so instead
4
+ // of the direct DynamoDB resolvers there is ONE in-VPC "PgQueryResolver" Lambda
5
+ // (PgQueryResolverEntryPoint.mjs → PgQueryResolver_Lambda.dispatch) registered
6
+ // as a single AppSync Lambda data source. Every Postgres read model's resolvers
7
+ // (QueryDbResolvers_Lambda) are thin APPSYNC_JS `Invoke` templates pointing at
8
+ // this one data source.
9
+ //
10
+ // Ordering wrinkle: `QueryDbStorage_Postgres.make` runs DURING construct and
11
+ // must return the data source name, but the Lambda's code bundle needs the set
12
+ // of Postgres read models (their spec packages), known only AFTER construct. So
13
+ // the data source name is a DEFERRED Output (`dataSourceName`), fulfilled by
14
+ // `provision` — which the platform calls in makePlatform once every plugin is
15
+ // built (alongside provisionPgChangeFeedRelay). The resolvers themselves are
16
+ // created even later (inside the schema-pushed `resourcesMaker`), so they read
17
+ // the name after `provision` has resolved it.
18
+
19
+ open PulumiAws
20
+
21
+ let log = ReventlessCore.Logger.fromEnv()
22
+
23
+ // -- Deferred data source name ----------------------------------------------
24
+ // Created once at module load. `QueryDbStorage_Postgres.make` returns this for
25
+ // every Postgres read model (one shared data source); `provision` resolves it.
26
+ let resolveDataSourceName: ref<string => unit> = ref(_ => ())
27
+ let dataSourceNamePromise: promise<string> = Promise.make((resolve, _reject) =>
28
+ resolveDataSourceName := resolve
29
+ )
30
+ let dataSourceName: Pulumi.Output.t<string> = Pulumi.Output.fromPromise(dataSourceNamePromise)
31
+
32
+ // -- Resolver-binding registry ----------------------------------------------
33
+ // Populated by QueryDbResolvers_Lambda.make during construct (per Postgres read
34
+ // model): the bits `provision` bakes into the Lambda env config that the entry
35
+ // point can't get from the spec module (they come from the deploy-time
36
+ // queryFieldNamesRegistry). specModulePath is looked up from
37
+ // EventCollectorRuntime_Builder_Single.readModelInfos at provision time.
38
+ type resolverEntry = {
39
+ readModelName: string,
40
+ labelField: string,
41
+ includeIdParam: bool,
42
+ }
43
+ let entries: dict<resolverEntry> = Dict.make()
44
+ let register = (entry: resolverEntry): unit =>
45
+ entries->Dict.set(entry.readModelName, entry)
46
+
47
+ // Relay node type → read-model-name, populated per RM by QueryDbResolvers_Lambda.
48
+ // Baked into the env config so the entry point can serve the shared node(id).
49
+ let nodeTypes: dict<string> = Dict.make()
50
+ let registerNodeType = (~typeName: string, ~readModelName: string): unit =>
51
+ nodeTypes->Dict.set(typeName, readModelName)
52
+
53
+ // -- Provision --------------------------------------------------------------
54
+ let bool = b => b ? "true" : "false"
55
+
56
+ // One `node(id: ID!)` resolver on the shared data source → Invoke {kind:"node"}.
57
+ let nodeResolverCode =
58
+ `import { util } from '@aws-appsync/utils';
59
+ export function request(ctx) {
60
+ const id = ctx.identity;
61
+ return {
62
+ operation: 'Invoke',
63
+ payload: {
64
+ readModelName: '',
65
+ kind: 'node',
66
+ arguments: ctx.args,
67
+ identity: id != null && id.sub != null
68
+ ? { userId: id.sub, username: id.username, groups: id.claims?.['cognito:groups'] ?? [], claims: id.claims, provider: 'Cognito' }
69
+ : id != null
70
+ ? { userArn: id.userArn ?? null, accountId: id.accountId ?? null, username: id.username ?? null, provider: 'IAM' }
71
+ : null
72
+ }
73
+ };
74
+ }
75
+ export function response(ctx) {
76
+ if (ctx.error) util.error(ctx.error.message, ctx.error.type);
77
+ return ctx.result;
78
+ }
79
+ `->Pulumi.Input.make
80
+
81
+ // Serialize the shared connection config into the env-config JSON (same shape
82
+ // EventCollectorRuntime_Builder_Single bakes into HANDLER_CONFIG).
83
+ let pgConnectionJson = (cc: PgConnection.connectionConfig): string =>
84
+ [
85
+ ("host", cc.host->JSON.Encode.string),
86
+ ("port", cc.port->Int.toFloat->JSON.Encode.float),
87
+ ("database", cc.database->JSON.Encode.string),
88
+ ("username", cc.username->JSON.Encode.string),
89
+ ("secretArn", cc.secretArn->JSON.Encode.string),
90
+ ]
91
+ ->Dict.fromArray
92
+ ->JSON.Encode.object
93
+ ->JSON.stringify
94
+
95
+ let provision = (
96
+ ~api: Pulumi.Output.t<AppSync.GraphQLApi.t>,
97
+ ~selection: QueryDbBackend.selection,
98
+ ~opts: Pulumi.ComponentResource.options,
99
+ ): unit => {
100
+ // Join the resolver-binding registry (labelField/includeIdParam) with the
101
+ // ReadModel runtime registry (specModulePath, pgBacked). Only read models
102
+ // that both got Lambda resolvers AND are Postgres-backed are handled here.
103
+ let handlers =
104
+ entries
105
+ ->Dict.valuesToArray
106
+ ->Array.filterMap(entry =>
107
+ switch EventCollectorRuntime_Builder_Single.readModelInfos->Dict.get(entry.readModelName) {
108
+ | Some(info) if info.pgBacked => Some((entry, info.specModulePath))
109
+ | _ => None
110
+ }
111
+ )
112
+ if handlers->Array.length == 0 {
113
+ // No Postgres read models with Lambda resolvers — nothing to provision.
114
+ ()
115
+ } else {
116
+ // makeFromCodeAsset takes ComponentResource.options (the original); the raw
117
+ // IAM / DataSource resources take the CustomResourceOptions projection.
118
+ let customOpts =
119
+ opts->ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions
120
+ let name = "PgQueryResolver"
121
+
122
+ // Bundle the entry point plus each read model's spec package (imported at
123
+ // Lambda init for indexes / subIdField / schema → capability).
124
+ let packageDirs: dict<string> = Dict.make()
125
+ handlers->Array.forEach(((_entry, specModulePath)) => {
126
+ let pkg = Util_Bundle.extractPackageName(specModulePath)
127
+ packageDirs->Dict.set(pkg, Util_Bundle.resolvePackageRoot(pkg))
128
+ })
129
+
130
+ // Env config: shared pgConnection + one handler per read model. Sorted for
131
+ // deploy-stable output. specModule/labelField/includeIdParam are baked;
132
+ // indexes/subIdField/authorization come from the imported spec at runtime.
133
+ let handlerJsons =
134
+ handlers
135
+ ->Array.toSorted(((a, _), (b, _)) => String.compare(a.readModelName, b.readModelName))
136
+ ->Array.map(((entry, specModulePath)) => {
137
+ let specModule = specModulePath->JSON.stringifyAny->Option.getOr(`""`)
138
+ `{"readModelName":"${entry.readModelName}","specModule":${specModule},"labelField":"${entry.labelField}","includeIdParam":${bool(
139
+ entry.includeIdParam,
140
+ )}}`
141
+ })
142
+
143
+ // Relay node type → read-model map, baked so the entry point can serve node(id).
144
+ let nodeTypesJson =
145
+ nodeTypes
146
+ ->Dict.toArray
147
+ ->Array.toSorted(((a, _), (b, _)) => String.compare(a, b))
148
+ ->Array.map(((typeName, rm)) => `"${typeName}":"${rm}"`)
149
+ ->Array.join(",")
150
+
151
+ let queryResolverConfig =
152
+ selection.connectionConfig->Pulumi.Output.apply(cc =>
153
+ `{"pgConnection":${pgConnectionJson(cc)},"handlers":[${handlerJsons->Array.join(
154
+ ",",
155
+ )}],"nodeTypes":{${nodeTypesJson}}}`
156
+ )
157
+
158
+ let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
159
+ envVars->Dict.set("QUERY_RESOLVER_CONFIG", queryResolverConfig->Pulumi.Output.asInput)
160
+ envVars->Dict.set("Environment", Pulumi.Pulumi.getStackName()->Pulumi.Input.make)
161
+
162
+ let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
163
+ ~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs",
164
+ ~packageDirs,
165
+ )
166
+
167
+ // In-VPC on the DB-access security group + private subnets (reach RDS).
168
+ let vpcConfig =
169
+ selection.securityGroupId
170
+ ->Pulumi.Output.apply(sgId =>
171
+ (
172
+ {
173
+ Lambda.Function.subnetIds: selection.subnetIds->Pulumi.Input.make,
174
+ securityGroupIds: [sgId->Pulumi.Input.make]->Pulumi.Input.make,
175
+ }: Lambda.Function.vpcConfig
176
+ )
177
+ )
178
+ ->Pulumi.Output.asInput
179
+
180
+ let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
181
+ ~name,
182
+ ~code,
183
+ ~sourceCodeHash,
184
+ ~envVars,
185
+ ~memorySize=512,
186
+ ~timeout=30,
187
+ ~vpcConfig,
188
+ ~opts,
189
+ )
190
+
191
+ // Read the RDS-managed secret so PgRuntime can resolve the DB password.
192
+ let _ = selection.connectionConfig->Pulumi.Output.apply(cc => {
193
+ open PolicyDocument
194
+ IAM.RolePolicy.make(
195
+ ~name=name ++ "-pgSecret",
196
+ ~args={
197
+ policy: PolicyDocument.make(
198
+ ~id=name ++ "-pgSecretPolicy",
199
+ ~statements=[
200
+ {
201
+ sid: "AllowGetPgSecret",
202
+ effect: Allow,
203
+ actions: Action("secretsmanager:GetSecretValue"),
204
+ resources: Resource(cc.secretArn),
205
+ },
206
+ ],
207
+ )
208
+ ->PolicyDocument.toJsonString
209
+ ->Pulumi.Input.make,
210
+ role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
211
+ },
212
+ )
213
+ })
214
+
215
+ // AppSync → Lambda data source (one, shared by every Postgres RM's resolvers).
216
+ let dataSourceRole = IAM.Role.makeWithDefaultPolicy(
217
+ ~name=name ++ "DataSource",
218
+ ~servicePrincipal=AWS.AppSync.principal->Pulumi.Output.make,
219
+ ~opts=customOpts,
220
+ )
221
+ let lambdaArn = runtime.parts.lambda->Pulumi.Output.flatMap(l => l.arn)
222
+ let _ =
223
+ (lambdaArn, dataSourceRole.id)
224
+ ->Pulumi.Output.all2
225
+ ->Pulumi.Output.apply(((arn, dsRoleId)) => {
226
+ open PolicyDocument
227
+ IAM.RolePolicy.make(
228
+ ~name=name ++ "DataSourceInvoke",
229
+ ~args={
230
+ policy: PolicyDocument.make(
231
+ ~id=name ++ "DataSourceInvokePolicy",
232
+ ~statements=[
233
+ {
234
+ sid: "AllowDataSourceInvokeLambda",
235
+ effect: Allow,
236
+ actions: Action("lambda:InvokeFunction"),
237
+ resources: Resource(arn),
238
+ },
239
+ ],
240
+ )
241
+ ->PolicyDocument.toJsonString
242
+ ->Pulumi.Input.make,
243
+ role: dsRoleId->Pulumi.Input.make,
244
+ },
245
+ )
246
+ })
247
+
248
+ let dataSource = AppSync.DataSource.make(
249
+ ~name=name ++ "DataSource",
250
+ ~args={
251
+ type_: AWS_LAMBDA,
252
+ apiId: api->Pulumi.Output.flatMap(a => a.id)->Pulumi.Output.asInput,
253
+ lambdaConfig: {
254
+ AppSync.DataSource.functionArn: lambdaArn->Pulumi.Output.asInput,
255
+ }->Pulumi.Input.make,
256
+ serviceRoleArn: dataSourceRole.arn->Pulumi.Output.asInput,
257
+ },
258
+ ~opts=Some(customOpts),
259
+ )
260
+
261
+ // Shared node(id) resolver (B3.2c) — one for the whole API, dispatched by
262
+ // typeName in the Lambda. Only when some entity registered a node type
263
+ // (else the `node` field isn't in the schema). Mirrors NodeResolver_AppSync,
264
+ // which likewise creates the node resolver directly at deploy time.
265
+ if nodeTypes->Dict.toArray->Array.length > 0 {
266
+ let _nodeResolver = AppSync_Resolver_Retrying.makeUnitJsResolver(
267
+ ~name=name ++ "NodeResolver",
268
+ ~api,
269
+ ~dataSourceName=dataSource.name->Pulumi.Output.asInput,
270
+ ~type_="Query"->Pulumi.Input.make,
271
+ ~field="node"->Pulumi.Input.make,
272
+ ~code=nodeResolverCode,
273
+ ~opts=customOpts,
274
+ )
275
+ }
276
+
277
+ // Fulfil the deferred name the Postgres storage maker handed to resolvers.
278
+ let _ = dataSource.name->Pulumi.Output.apply(n => resolveDataSourceName.contents(n))
279
+ log.info(~comp="PgQueryResolver_Builder", `provisioned for ${handlers->Array.length->Int.toString} read model(s)`)
280
+ }
281
+ }
@@ -0,0 +1,189 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Aws from "@pulumi/aws";
4
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
5
+ import * as IAM$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/IAM.res.mjs";
6
+ import * as Output$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Output.res.mjs";
7
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
8
+ import * as Pulumi from "@pulumi/pulumi";
9
+ import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
10
+ import * as AWS$ReventlessAws from "../AWS.res.mjs";
11
+ import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
12
+ import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
13
+ import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
14
+ import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
15
+ import * as AppSync_Resolver_Retrying$ReventlessAws from "../Api/AppSync_Resolver_Retrying.res.mjs";
16
+ import * as RuntimeEnvironment_Lambda$ReventlessAws from "../Runtime/RuntimeEnvironment_Lambda.res.mjs";
17
+ import * as EventCollectorRuntime_Builder_Single$ReventlessAws from "../Runtime/EventCollectorRuntime_Builder_Single.res.mjs";
18
+
19
+ let log = Logger$ReventlessCore.fromEnv();
20
+
21
+ let resolveDataSourceName = {
22
+ contents: param => {}
23
+ };
24
+
25
+ let dataSourceNamePromise = new Promise((resolve, _reject) => {
26
+ resolveDataSourceName.contents = resolve;
27
+ });
28
+
29
+ let entries = {};
30
+
31
+ function register(entry) {
32
+ entries[entry.readModelName] = entry;
33
+ }
34
+
35
+ let nodeTypes = {};
36
+
37
+ function registerNodeType(typeName, readModelName) {
38
+ nodeTypes[typeName] = readModelName;
39
+ }
40
+
41
+ function bool(b) {
42
+ if (b) {
43
+ return "true";
44
+ } else {
45
+ return "false";
46
+ }
47
+ }
48
+
49
+ let nodeResolverCode = `import { util } from '@aws-appsync/utils';
50
+ export function request(ctx) {
51
+ const id = ctx.identity;
52
+ return {
53
+ operation: 'Invoke',
54
+ payload: {
55
+ readModelName: '',
56
+ kind: 'node',
57
+ arguments: ctx.args,
58
+ identity: id != null && id.sub != null
59
+ ? { userId: id.sub, username: id.username, groups: id.claims?.['cognito:groups'] ?? [], claims: id.claims, provider: 'Cognito' }
60
+ : id != null
61
+ ? { userArn: id.userArn ?? null, accountId: id.accountId ?? null, username: id.username ?? null, provider: 'IAM' }
62
+ : null
63
+ }
64
+ };
65
+ }
66
+ export function response(ctx) {
67
+ if (ctx.error) util.error(ctx.error.message, ctx.error.type);
68
+ return ctx.result;
69
+ }
70
+ `;
71
+
72
+ function pgConnectionJson(cc) {
73
+ return JSON.stringify(Object.fromEntries([
74
+ [
75
+ "host",
76
+ cc.host
77
+ ],
78
+ [
79
+ "port",
80
+ cc.port
81
+ ],
82
+ [
83
+ "database",
84
+ cc.database
85
+ ],
86
+ [
87
+ "username",
88
+ cc.username
89
+ ],
90
+ [
91
+ "secretArn",
92
+ cc.secretArn
93
+ ]
94
+ ]));
95
+ }
96
+
97
+ function provision(api, selection, opts) {
98
+ let handlers = Stdlib_Array.filterMap(Object.values(entries), entry => {
99
+ let info = EventCollectorRuntime_Builder_Single$ReventlessAws.readModelInfos[entry.readModelName];
100
+ if (info !== undefined && info.pgBacked) {
101
+ return [
102
+ entry,
103
+ info.specModulePath
104
+ ];
105
+ }
106
+ });
107
+ if (handlers.length === 0) {
108
+ return;
109
+ }
110
+ let customOpts = Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions(opts);
111
+ let name = "PgQueryResolver";
112
+ let packageDirs = {};
113
+ handlers.forEach(param => {
114
+ let pkg = Util_Bundle$ReventlessAws.extractPackageName(param[1]);
115
+ packageDirs[pkg] = Util_Bundle$ReventlessAws.resolvePackageRoot(pkg);
116
+ });
117
+ let handlerJsons = handlers.toSorted((param, param$1) => Primitive_string.compare(param[0].readModelName, param$1[0].readModelName)).map(param => {
118
+ let entry = param[0];
119
+ let specModule = Stdlib_Option.getOr(JSON.stringify(param[1]), `""`);
120
+ return `{"readModelName":"` + entry.readModelName + `","specModule":` + specModule + `,"labelField":"` + entry.labelField + `","includeIdParam":` + (
121
+ entry.includeIdParam ? "true" : "false"
122
+ ) + `}`;
123
+ });
124
+ let nodeTypesJson = Object.entries(nodeTypes).toSorted((param, param$1) => Primitive_string.compare(param[0], param$1[0])).map(param => `"` + param[0] + `":"` + param[1] + `"`).join(",");
125
+ let queryResolverConfig = selection.connectionConfig.apply(cc => `{"pgConnection":` + pgConnectionJson(cc) + `,"handlers":[` + handlerJsons.join(",") + `],"nodeTypes":{` + nodeTypesJson + `}}`);
126
+ let envVars = {};
127
+ envVars["QUERY_RESOLVER_CONFIG"] = queryResolverConfig;
128
+ envVars["Environment"] = Pulumi.getStack();
129
+ let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs", packageDirs, undefined);
130
+ let vpcConfig = selection.securityGroupId.apply(sgId => ({
131
+ subnetIds: selection.subnetIds,
132
+ securityGroupIds: [sgId]
133
+ }));
134
+ let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, match.code, match.sourceCodeHash, envVars, 512, 30, undefined, undefined, undefined, undefined, vpcConfig, opts);
135
+ selection.connectionConfig.apply(cc => new (Aws.iam.RolePolicy)(name + "-pgSecret", {
136
+ policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + "-pgSecretPolicy", [{
137
+ Sid: "AllowGetPgSecret",
138
+ Effect: "Allow",
139
+ Action: "secretsmanager:GetSecretValue",
140
+ Resource: cc.secretArn
141
+ }])),
142
+ role: runtime.parts.lambdaRole.id
143
+ }));
144
+ let dataSourceRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "DataSource", Pulumi.output(AWS$ReventlessAws.AppSync.principal), customOpts);
145
+ let lambdaArn = Output$Pulumi.flatMap(runtime.parts.lambda, l => l.arn);
146
+ Pulumi.all([
147
+ lambdaArn,
148
+ dataSourceRole.id
149
+ ]).apply(param => new (Aws.iam.RolePolicy)(name + "DataSourceInvoke", {
150
+ policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + "DataSourceInvokePolicy", [{
151
+ Sid: "AllowDataSourceInvokeLambda",
152
+ Effect: "Allow",
153
+ Action: "lambda:InvokeFunction",
154
+ Resource: param[0]
155
+ }])),
156
+ role: param[1]
157
+ }));
158
+ let dataSource = new (Aws.appsync.DataSource)(name + "DataSource", {
159
+ type: "AWS_LAMBDA",
160
+ apiId: Output$Pulumi.flatMap(api, a => a.id),
161
+ lambdaConfig: {
162
+ functionArn: lambdaArn
163
+ },
164
+ serviceRoleArn: dataSourceRole.arn
165
+ }, customOpts);
166
+ if (Object.entries(nodeTypes).length !== 0) {
167
+ AppSync_Resolver_Retrying$ReventlessAws.makeUnitJsResolver(name + "NodeResolver", api, dataSource.name, "Query", "node", nodeResolverCode, customOpts);
168
+ }
169
+ dataSource.name.apply(n => resolveDataSourceName.contents(n));
170
+ log.info("PgQueryResolver_Builder", undefined, `provisioned for ` + handlers.length.toString() + ` read model(s)`);
171
+ }
172
+
173
+ let dataSourceName = dataSourceNamePromise;
174
+
175
+ export {
176
+ log,
177
+ resolveDataSourceName,
178
+ dataSourceNamePromise,
179
+ dataSourceName,
180
+ entries,
181
+ register,
182
+ nodeTypes,
183
+ registerNodeType,
184
+ bool,
185
+ nodeResolverCode,
186
+ pgConnectionJson,
187
+ provision,
188
+ }
189
+ /* log Not a pure module */