@reventlessdev/reventless-aws 3.0.0-alpha.175 → 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.
Files changed (76) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/package.json +10 -10
  3. package/src/Platform.res +127 -31
  4. package/src/Platform.res.mjs +226 -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 +54 -0
  19. package/src/adapter/Postgres/PgChangeFeedRelay_Runtime.res.mjs +23 -0
  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_Builder.res +281 -0
  23. package/src/adapter/QueryDb/PgQueryResolver_Builder.res.mjs +189 -0
  24. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res +335 -0
  25. package/src/adapter/QueryDb/PgQueryResolver_Lambda.res.mjs +252 -0
  26. package/src/adapter/QueryDb/QueryDbBackend.res +52 -0
  27. package/src/adapter/QueryDb/QueryDbBackend.res.mjs +44 -0
  28. package/src/adapter/QueryDb/QueryDbResolvers.res +49 -0
  29. package/src/adapter/QueryDb/QueryDbResolvers.res.mjs +20 -1
  30. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res +264 -0
  31. package/src/adapter/QueryDb/QueryDbResolvers_Lambda.res.mjs +164 -0
  32. package/src/adapter/QueryDb/QueryDbStorage.res +44 -0
  33. package/src/adapter/QueryDb/QueryDbStorage.res.mjs +34 -1
  34. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res +47 -0
  35. package/src/adapter/QueryDb/QueryDbStorage_Postgres.res.mjs +21 -0
  36. package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res +19 -0
  37. package/src/adapter/QueryDb/QueryDbStorage_Postgres_Runtime.res.mjs +15 -0
  38. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res +12 -1
  39. package/src/adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs +2 -1
  40. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res +83 -3
  41. package/src/adapter/Runtime/AggregateRuntime_Builder_Single.res.mjs +46 -3
  42. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res +83 -3
  43. package/src/adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs +48 -3
  44. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res +130 -5
  45. package/src/adapter/Runtime/EventCollectorRuntime_Builder_Single.res.mjs +66 -6
  46. package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +15 -10
  47. package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +88 -0
  48. package/src/adapter/Runtime/ReadModelEntryPoint.mjs +32 -15
  49. package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +26 -12
  50. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res +138 -8
  51. package/src/adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs +76 -11
  52. package/src/components/Aggregate_Builder_Single.res +12 -5
  53. package/src/components/Aggregate_Builder_Single.res.mjs +12 -6
  54. package/src/components/Aggregate_Builder_Single_Async.res +12 -5
  55. package/src/components/Aggregate_Builder_Single_Async.res.mjs +12 -6
  56. package/src/components/ReadModel_Builder_NoResolver.res.mjs +1 -1
  57. package/src/components/ReadModel_Builder_NoResolver_Stream.res +13 -4
  58. package/src/components/ReadModel_Builder_NoResolver_Stream.res.mjs +14 -9
  59. package/src/components/ReadModel_Builder_Single.res +14 -5
  60. package/src/components/ReadModel_Builder_Single.res.mjs +15 -12
  61. package/src/components/ReadModel_Builder_Single_Stream.res +14 -5
  62. package/src/components/ReadModel_Builder_Single_Stream.res.mjs +15 -12
  63. package/src/components/StateViewSlice_Builder.res +10 -4
  64. package/src/components/StateViewSlice_Builder.res.mjs +8 -10
  65. package/src/components/StateViewSlice_Builder_Stream.res +10 -4
  66. package/src/components/StateViewSlice_Builder_Stream.res.mjs +8 -10
  67. package/src/plugin/runtime/PluginRuntime_Builder.res +12 -0
  68. package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +4 -0
  69. package/tests/EventCollectorChannelStreamRuntimeTest.res +55 -0
  70. package/tests/EventCollectorChannelStreamRuntimeTest.res.mjs +78 -0
  71. package/tests/PgChangeFeedRelay_IntegrationTest.res +115 -4
  72. package/tests/PgChangeFeedRelay_IntegrationTest.res.mjs +84 -0
  73. package/tests/PgPipeline_IntegrationTest.res +208 -0
  74. package/tests/PgPipeline_IntegrationTest.res.mjs +260 -0
  75. package/tests/PgQueryResolver_LambdaTest.res +308 -0
  76. package/tests/PgQueryResolver_LambdaTest.res.mjs +392 -0
@@ -0,0 +1,54 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Aws from "@pulumi/aws";
4
+ import * as SQS_Queue$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/SQS/SQS_Queue.res.mjs";
5
+ import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
6
+ import * as Util_DeadLetterQueue$ReventlessAws from "../../util/Util_DeadLetterQueue.res.mjs";
7
+ import * as Util_EventSourceMapping$ReventlessAws from "../../util/Util_EventSourceMapping.res.mjs";
8
+
9
+ let feedQueues = [];
10
+
11
+ function getFeedQueues() {
12
+ return feedQueues;
13
+ }
14
+
15
+ function makeQueue(name, scope, includeClassic, includeDcb, opts) {
16
+ let queue = new (Aws.sqs.Queue)(name, {
17
+ redrivePolicy: Util_DeadLetterQueue$ReventlessAws.queue.arn.apply(dlqArn => SQS_Queue$PulumiAws.RedrivePolicy.make(dlqArn, 5)),
18
+ visibilityTimeoutSeconds: 120,
19
+ sqsManagedSseEnabled: false
20
+ }, opts);
21
+ feedQueues.push({
22
+ scope: scope,
23
+ includeClassic: includeClassic,
24
+ includeDcb: includeDcb,
25
+ url: queue.id,
26
+ arn: queue.arn
27
+ });
28
+ return queue;
29
+ }
30
+
31
+ function connect(name, queue, lambda, lambdaRole, opts) {
32
+ Util_EventSourceMapping$ReventlessAws.subscribeSqs(lambda, name, queue, undefined, opts);
33
+ queue.arn.apply(queueArn => new (Aws.iam.RolePolicy)(name + `Receive`, {
34
+ policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + `ReceivePolicy`, [{
35
+ Sid: "AllowReceiveFeedQueue",
36
+ Effect: "Allow",
37
+ Action: [
38
+ "sqs:ReceiveMessage",
39
+ "sqs:DeleteMessage",
40
+ "sqs:GetQueueAttributes"
41
+ ],
42
+ Resource: queueArn
43
+ }])),
44
+ role: lambdaRole.id
45
+ }, opts));
46
+ }
47
+
48
+ export {
49
+ feedQueues,
50
+ getFeedQueues,
51
+ makeQueue,
52
+ connect,
53
+ }
54
+ /* @pulumi/aws Not a pure module */
@@ -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 */