@reventlessdev/reventless-aws 3.0.0-alpha.272 → 3.0.0-alpha.274
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 +19 -0
- package/package.json +8 -7
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +15 -13
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +3 -2
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +15 -13
- package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +3 -2
- package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res +8 -1
- package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res.mjs +3 -2
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver.res +15 -13
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver.res.mjs +3 -2
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +6 -12
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs +4 -8
- package/src/adapter/QueryDb/QueryInterceptor_Provisioning.res +155 -0
- package/src/adapter/QueryDb/QueryInterceptor_Provisioning.res.mjs +92 -0
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +4 -9
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +8 -2
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +32 -62
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +7 -20
- package/src/adapter/StateTopic/StateTopic_AppSync.res +15 -13
- package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +3 -2
- package/src/adapter/Upload/Upload_Claim_S3.res +15 -13
- package/src/adapter/Upload/Upload_Claim_S3.res.mjs +3 -2
- package/src/adapter/Upload/Upload_Presign_S3.res +15 -13
- package/src/adapter/Upload/Upload_Presign_S3.res.mjs +3 -2
- package/src/components/Api/AppSync_Adapter.res +13 -9
- package/src/components/Api/AppSync_Adapter.res.mjs +4 -3
- package/src/util/Util_LambdaLogging.res +83 -21
- package/src/util/Util_LambdaLogging.res.mjs +21 -8
- package/src/util/Util_LogGroup_Adopting.res +480 -0
- package/src/util/Util_LogGroup_Adopting.res.mjs +365 -0
- package/src/util/Util_LogGroup_Adopting_Js.mjs +20 -0
- package/src/util/Util_LogRetention.res +11 -12
- package/tests/Util_LambdaLoggingTest.res +48 -0
- package/tests/Util_LambdaLoggingTest.res.mjs +30 -0
- package/tests/Util_LogGroup_AdoptingTest.res +169 -0
- package/tests/Util_LogGroup_AdoptingTest.res.mjs +208 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,25 @@
|
|
|
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.274 (2026-08-08)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **aws:** scope a managed log group by project as well as stack ([f3ea01a](https://github.com/ReventlessDev/reventless-core/commit/f3ea01a254465012becf6a27b401f10382439864))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# 3.0.0-alpha.273 (2026-08-08)
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **aws:** create a Lambda's log group before the function that writes to it ([8d1e459](https://github.com/ReventlessDev/reventless-core/commit/8d1e4591127489d2ae8e84e7ade3843a1a859eb6))
|
|
18
|
+
* **core:** attribute resources built after a builder's construct returns ([67d6d9c](https://github.com/ReventlessDev/reventless-core/commit/67d6d9c0ff3ff1d38cd9e8ad43a4b86121abf827))
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
* **aws:** adopt an existing AppSync log group instead of failing on it ([ebed6c1](https://github.com/ReventlessDev/reventless-core/commit/ebed6c1ceb77b8deebff10a9c45715c107861f04))
|
|
22
|
+
* **core:** let an extension switch query interception on, and provision it ([4523b10](https://github.com/ReventlessDev/reventless-core/commit/4523b10597f1a028eedccc42391b250b25a6ddd3))
|
|
23
|
+
|
|
24
|
+
|
|
6
25
|
# 3.0.0-alpha.272 (2026-08-08)
|
|
7
26
|
|
|
8
27
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.274",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@aws-sdk/client-appsync": "3.970.0",
|
|
8
8
|
"@aws-sdk/client-cloudfront": "3.970.0",
|
|
9
|
+
"@aws-sdk/client-cloudwatch-logs": "3.970.0",
|
|
9
10
|
"@aws-sdk/client-location": "3.970.0",
|
|
10
11
|
"@aws-sdk/client-s3": "3.970.0",
|
|
11
12
|
"@aws-sdk/s3-request-presigner": "3.970.0",
|
|
12
13
|
"sury": "11.0.0-alpha.4",
|
|
13
14
|
"uuid": "^13.0.0",
|
|
14
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
15
|
-
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.66",
|
|
16
15
|
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.4",
|
|
17
|
-
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
18
16
|
"@reventlessdev/rescript-node": "2.0.0-alpha.2",
|
|
17
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
18
|
+
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
19
|
+
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.67",
|
|
19
20
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
|
|
20
21
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
22
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.217",
|
|
21
23
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.30",
|
|
22
|
-
"@reventlessdev/reventless-postgres": "3.0.0-alpha.80",
|
|
23
24
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.128",
|
|
24
|
-
"@reventlessdev/reventless-
|
|
25
|
-
"@reventlessdev/reventless-
|
|
25
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.81",
|
|
26
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.102"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"rescript": "12.3.0",
|
|
@@ -135,6 +135,20 @@ let make = (
|
|
|
135
135
|
->Option.getOr([])
|
|
136
136
|
->Pulumi.Input.make
|
|
137
137
|
|
|
138
|
+
// Created before the function that writes to it — see
|
|
139
|
+
// `Util_LambdaLogging.makeManagedLogGroup` for why the ordering matters.
|
|
140
|
+
let logGroup = Util_LambdaLogging.makeManagedLogGroup(
|
|
141
|
+
~name=name ++ "Lambda",
|
|
142
|
+
~tags=AWS.Tags.make(
|
|
143
|
+
~name=name ++ "LambdaLogGroup",
|
|
144
|
+
~kind=ReventlessCore.ComponentType.Platform,
|
|
145
|
+
~role=Logs,
|
|
146
|
+
~scope=Platform,
|
|
147
|
+
),
|
|
148
|
+
~opts,
|
|
149
|
+
(),
|
|
150
|
+
)
|
|
151
|
+
|
|
138
152
|
let lambda = Lambda.Function.make(
|
|
139
153
|
~name=name ++ "Lambda",
|
|
140
154
|
~args={
|
|
@@ -159,23 +173,11 @@ let make = (
|
|
|
159
173
|
]),
|
|
160
174
|
}: Lambda.Function.functionEnvironment
|
|
161
175
|
)->Pulumi.Input.make,
|
|
176
|
+
loggingConfig: ?Util_LambdaLogging.loggingConfigFor(logGroup),
|
|
162
177
|
},
|
|
163
178
|
~opts,
|
|
164
179
|
)
|
|
165
180
|
|
|
166
|
-
Util_LambdaLogging.makeManagedLogGroup(
|
|
167
|
-
~name=name ++ "Lambda",
|
|
168
|
-
~lambdaName=lambda.name,
|
|
169
|
-
~tags=AWS.Tags.make(
|
|
170
|
-
~name=name ++ "LambdaLogGroup",
|
|
171
|
-
~kind=ReventlessCore.ComponentType.Platform,
|
|
172
|
-
~role=Logs,
|
|
173
|
-
~scope=Platform,
|
|
174
|
-
),
|
|
175
|
-
~opts,
|
|
176
|
-
(),
|
|
177
|
-
)
|
|
178
|
-
|
|
179
181
|
let dataSourceRole = IAM.Role.makeWithDefaultPolicy(
|
|
180
182
|
~name=name ++ "DataSource",
|
|
181
183
|
~servicePrincipal=AWS.AppSync.principal->Pulumi.Output.make,
|
|
@@ -80,6 +80,7 @@ function make(api, pluginReadModelTableName, offloadBucketName, schemaReady, opt
|
|
|
80
80
|
adminEntryJson
|
|
81
81
|
]]), false);
|
|
82
82
|
let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
|
|
83
|
+
let logGroup = Util_LambdaLogging$ReventlessAws.makeManagedLogGroup(name + "Lambda", undefined, AWS_Tags$ReventlessAws.make(name + "LambdaLogGroup", "Platform", "Logs", "Platform", undefined, undefined, undefined, undefined), opts$1, undefined);
|
|
83
84
|
let lambda = new (Aws.lambda.Function)(name + "Lambda", {
|
|
84
85
|
handler: "index.handler",
|
|
85
86
|
runtime: "nodejs22.x",
|
|
@@ -114,9 +115,9 @@ function make(api, pluginReadModelTableName, offloadBucketName, schemaReady, opt
|
|
|
114
115
|
Util_LambdaLogging$ReventlessAws.logLevelEntry()
|
|
115
116
|
])
|
|
116
117
|
},
|
|
117
|
-
sourceCodeHash: match.sourceCodeHash
|
|
118
|
+
sourceCodeHash: match.sourceCodeHash,
|
|
119
|
+
loggingConfig: Util_LambdaLogging$ReventlessAws.loggingConfigFor(logGroup)
|
|
118
120
|
}, opts$1);
|
|
119
|
-
Util_LambdaLogging$ReventlessAws.makeManagedLogGroup(name + "Lambda", lambda.name, AWS_Tags$ReventlessAws.make(name + "LambdaLogGroup", "Platform", "Logs", "Platform", undefined, undefined, undefined, undefined), opts$1, undefined);
|
|
120
121
|
let dataSourceRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "DataSource", Pulumi.output(AWS$ReventlessAws.AppSync.principal), AWS_Tags$ReventlessAws.make(name + "DataSource", "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
|
|
121
122
|
Pulumi.all([
|
|
122
123
|
lambda.arn,
|
|
@@ -93,6 +93,20 @@ let make = (
|
|
|
93
93
|
->Option.getOr([])
|
|
94
94
|
->Pulumi.Input.make
|
|
95
95
|
|
|
96
|
+
// Created before the function that writes to it — see
|
|
97
|
+
// `Util_LambdaLogging.makeManagedLogGroup` for why the ordering matters.
|
|
98
|
+
let logGroup = Util_LambdaLogging.makeManagedLogGroup(
|
|
99
|
+
~name=name ++ "Lambda",
|
|
100
|
+
~tags=AWS.Tags.make(
|
|
101
|
+
~name=name ++ "LambdaLogGroup",
|
|
102
|
+
~kind=ReventlessCore.ComponentType.Platform,
|
|
103
|
+
~role=Logs,
|
|
104
|
+
~scope=Platform,
|
|
105
|
+
),
|
|
106
|
+
~opts,
|
|
107
|
+
(),
|
|
108
|
+
)
|
|
109
|
+
|
|
96
110
|
let lambda = Lambda.Function.make(
|
|
97
111
|
~name=name ++ "Lambda",
|
|
98
112
|
~args={
|
|
@@ -116,23 +130,11 @@ let make = (
|
|
|
116
130
|
]),
|
|
117
131
|
}: Lambda.Function.functionEnvironment
|
|
118
132
|
)->Pulumi.Input.make,
|
|
133
|
+
loggingConfig: ?Util_LambdaLogging.loggingConfigFor(logGroup),
|
|
119
134
|
},
|
|
120
135
|
~opts,
|
|
121
136
|
)
|
|
122
137
|
|
|
123
|
-
Util_LambdaLogging.makeManagedLogGroup(
|
|
124
|
-
~name=name ++ "Lambda",
|
|
125
|
-
~lambdaName=lambda.name,
|
|
126
|
-
~tags=AWS.Tags.make(
|
|
127
|
-
~name=name ++ "LambdaLogGroup",
|
|
128
|
-
~kind=ReventlessCore.ComponentType.Platform,
|
|
129
|
-
~role=Logs,
|
|
130
|
-
~scope=Platform,
|
|
131
|
-
),
|
|
132
|
-
~opts,
|
|
133
|
-
(),
|
|
134
|
-
)
|
|
135
|
-
|
|
136
138
|
let dataSourceRole = IAM.Role.makeWithDefaultPolicy(
|
|
137
139
|
~name=name ++ "DataSource",
|
|
138
140
|
~servicePrincipal=AWS.AppSync.principal->Pulumi.Output.make,
|
|
@@ -54,6 +54,7 @@ function make(api, uiFragmentRegistryTableName, schemaReady, opts) {
|
|
|
54
54
|
]]);
|
|
55
55
|
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs", packageDirs, undefined, false);
|
|
56
56
|
let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
|
|
57
|
+
let logGroup = Util_LambdaLogging$ReventlessAws.makeManagedLogGroup(name + "Lambda", undefined, AWS_Tags$ReventlessAws.make(name + "LambdaLogGroup", "Platform", "Logs", "Platform", undefined, undefined, undefined, undefined), opts$1, undefined);
|
|
57
58
|
let lambda = new (Aws.lambda.Function)(name + "Lambda", {
|
|
58
59
|
handler: "index.handler",
|
|
59
60
|
runtime: "nodejs22.x",
|
|
@@ -84,9 +85,9 @@ function make(api, uiFragmentRegistryTableName, schemaReady, opts) {
|
|
|
84
85
|
Util_LambdaLogging$ReventlessAws.logLevelEntry()
|
|
85
86
|
])
|
|
86
87
|
},
|
|
87
|
-
sourceCodeHash: match.sourceCodeHash
|
|
88
|
+
sourceCodeHash: match.sourceCodeHash,
|
|
89
|
+
loggingConfig: Util_LambdaLogging$ReventlessAws.loggingConfigFor(logGroup)
|
|
88
90
|
}, opts$1);
|
|
89
|
-
Util_LambdaLogging$ReventlessAws.makeManagedLogGroup(name + "Lambda", lambda.name, AWS_Tags$ReventlessAws.make(name + "LambdaLogGroup", "Platform", "Logs", "Platform", undefined, undefined, undefined, undefined), opts$1, undefined);
|
|
90
91
|
let dataSourceRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "DataSource", Pulumi.output(AWS$ReventlessAws.AppSync.principal), AWS_Tags$ReventlessAws.make(name + "DataSource", "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
|
|
91
92
|
Pulumi.all([
|
|
92
93
|
lambda.arn,
|
|
@@ -125,7 +125,14 @@ let connectLambda = (
|
|
|
125
125
|
resources->ReventlessCore.Adapter.resourcesToResolvedOutput,
|
|
126
126
|
)
|
|
127
127
|
->Pulumi.Output.all3
|
|
128
|
-
|
|
128
|
+
// Provisions the collector's role, policies and event-source mappings, so it
|
|
129
|
+
// carries the plugin's attribution across the apply — by the time this runs
|
|
130
|
+
// the builder's construct has returned and the ambient context is empty.
|
|
131
|
+
->ReventlessCore.ResourceAttribution_Deploytime.applyAttributed(((
|
|
132
|
+
eventTopicResources,
|
|
133
|
+
queueArns,
|
|
134
|
+
resources,
|
|
135
|
+
)) => {
|
|
129
136
|
log.debug(
|
|
130
137
|
~comp="EventCollector",
|
|
131
138
|
`connectLambda ${name}: ${eventTopicResources->Array.length->Int.toString} topic resource(s), ${resources->Array.length->Int.toString} resource(s)`,
|
|
@@ -15,6 +15,7 @@ import * as Adapter_Helpers$ReventlessAws from "../Adapter_Helpers.res.mjs";
|
|
|
15
15
|
import * as EventCollector$ReventlessCore from "@reventlessdev/reventless-core/src/components/EventCollector/EventCollector.res.mjs";
|
|
16
16
|
import * as AdapterDeploytime$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/AdapterDeploytime.res.mjs";
|
|
17
17
|
import * as Util_EventSourceMapping$ReventlessAws from "../../util/Util_EventSourceMapping.res.mjs";
|
|
18
|
+
import * as ResourceAttribution_Deploytime$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/ResourceAttribution_Deploytime.res.mjs";
|
|
18
19
|
|
|
19
20
|
let log = Logger$ReventlessCore.fromEnv();
|
|
20
21
|
|
|
@@ -76,11 +77,11 @@ function esmTags(name, esmName) {
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
function connectLambda(lambda, name, lambdaRole, queues, eventTopics, resources, opts) {
|
|
79
|
-
Pulumi.all([
|
|
80
|
+
ResourceAttribution_Deploytime$ReventlessCore.applyAttributed(Pulumi.all([
|
|
80
81
|
toResources(eventTopics),
|
|
81
82
|
Pulumi.all(queues.map(queue => queue.arn)),
|
|
82
83
|
Adapter$ReventlessCore.resourcesToResolvedOutput(resources)
|
|
83
|
-
])
|
|
84
|
+
]), param => {
|
|
84
85
|
let resources = param[2];
|
|
85
86
|
let queueArns = param[1];
|
|
86
87
|
let eventTopicResources = param[0];
|
|
@@ -118,6 +118,20 @@ let make = (
|
|
|
118
118
|
->Option.getOr([])
|
|
119
119
|
->Pulumi.Input.make
|
|
120
120
|
|
|
121
|
+
// Created before the function that writes to it — see
|
|
122
|
+
// `Util_LambdaLogging.makeManagedLogGroup` for why the ordering matters.
|
|
123
|
+
let logGroup = Util_LambdaLogging.makeManagedLogGroup(
|
|
124
|
+
~name=name ++ "Lambda",
|
|
125
|
+
~tags=AWS.Tags.make(
|
|
126
|
+
~name=name ++ "LambdaLogGroup",
|
|
127
|
+
~kind=ReventlessCore.ComponentType.Platform,
|
|
128
|
+
~role=Logs,
|
|
129
|
+
~scope=Platform,
|
|
130
|
+
),
|
|
131
|
+
~opts,
|
|
132
|
+
(),
|
|
133
|
+
)
|
|
134
|
+
|
|
121
135
|
let lambda = Lambda.Function.make(
|
|
122
136
|
~name=name ++ "Lambda",
|
|
123
137
|
~args={
|
|
@@ -146,23 +160,11 @@ let make = (
|
|
|
146
160
|
]),
|
|
147
161
|
}: Lambda.Function.functionEnvironment
|
|
148
162
|
)->Pulumi.Input.make,
|
|
163
|
+
loggingConfig: ?Util_LambdaLogging.loggingConfigFor(logGroup),
|
|
149
164
|
},
|
|
150
165
|
~opts,
|
|
151
166
|
)
|
|
152
167
|
|
|
153
|
-
Util_LambdaLogging.makeManagedLogGroup(
|
|
154
|
-
~name=name ++ "Lambda",
|
|
155
|
-
~lambdaName=lambda.name,
|
|
156
|
-
~tags=AWS.Tags.make(
|
|
157
|
-
~name=name ++ "LambdaLogGroup",
|
|
158
|
-
~kind=ReventlessCore.ComponentType.Platform,
|
|
159
|
-
~role=Logs,
|
|
160
|
-
~scope=Platform,
|
|
161
|
-
),
|
|
162
|
-
~opts,
|
|
163
|
-
(),
|
|
164
|
-
)
|
|
165
|
-
|
|
166
168
|
let dataSourceRole = IAM.Role.makeWithDefaultPolicy(
|
|
167
169
|
~name=name ++ "DataSource",
|
|
168
170
|
~servicePrincipal=AWS.AppSync.principal->Pulumi.Output.make,
|
|
@@ -58,6 +58,7 @@ function make(api, placeIndexName, nameOpt, opts) {
|
|
|
58
58
|
]]);
|
|
59
59
|
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Geocoder/Geocoder_AwsLocation_Resolver_Ops.res.mjs", packageDirs, undefined, false);
|
|
60
60
|
let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
|
|
61
|
+
let logGroup = Util_LambdaLogging$ReventlessAws.makeManagedLogGroup(name + "Lambda", undefined, AWS_Tags$ReventlessAws.make(name + "LambdaLogGroup", "Platform", "Logs", "Platform", undefined, undefined, undefined, undefined), opts$1, undefined);
|
|
61
62
|
let lambda = new (Aws.lambda.Function)(name + "Lambda", {
|
|
62
63
|
handler: "index.handler",
|
|
63
64
|
runtime: "nodejs22.x",
|
|
@@ -88,9 +89,9 @@ function make(api, placeIndexName, nameOpt, opts) {
|
|
|
88
89
|
Util_LambdaLogging$ReventlessAws.logLevelEntry()
|
|
89
90
|
])
|
|
90
91
|
},
|
|
91
|
-
sourceCodeHash: match.sourceCodeHash
|
|
92
|
+
sourceCodeHash: match.sourceCodeHash,
|
|
93
|
+
loggingConfig: Util_LambdaLogging$ReventlessAws.loggingConfigFor(logGroup)
|
|
92
94
|
}, opts$1);
|
|
93
|
-
Util_LambdaLogging$ReventlessAws.makeManagedLogGroup(name + "Lambda", lambda.name, AWS_Tags$ReventlessAws.make(name + "LambdaLogGroup", "Platform", "Logs", "Platform", undefined, undefined, undefined, undefined), opts$1, undefined);
|
|
94
95
|
let dataSourceRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "DataSource", Pulumi.output(AWS$ReventlessAws.AppSync.principal), AWS_Tags$ReventlessAws.make(name + "DataSource", "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
|
|
95
96
|
Pulumi.all([
|
|
96
97
|
lambda.arn,
|
|
@@ -13,15 +13,6 @@ let log = ReventlessCore.Logger.fromEnv()
|
|
|
13
13
|
type api = Types.AppSync.api
|
|
14
14
|
type role = Types.AppSync.role
|
|
15
15
|
|
|
16
|
-
type interceptorConfig = {
|
|
17
|
-
dataSourceName: Pulumi.Input.t<string>,
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** Deploy-time config for the query interceptor. When set, all top-level Query
|
|
21
|
-
resolvers become pipeline resolvers with an interceptor Lambda function
|
|
22
|
-
preceding the DynamoDB query. Set this before calling `make`. */
|
|
23
|
-
let queryInterceptorConfig: ref<option<interceptorConfig>> = ref(None)
|
|
24
|
-
|
|
25
16
|
let interceptorCode = readModelName =>
|
|
26
17
|
`import { util } from '@aws-appsync/utils';
|
|
27
18
|
export function request(ctx) {
|
|
@@ -106,9 +97,12 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
106
97
|
}
|
|
107
98
|
|
|
108
99
|
// Creates either a unit resolver (no interceptor) or a pipeline resolver
|
|
109
|
-
// (interceptor Lambda → DynamoDB query) depending on
|
|
100
|
+
// (interceptor Lambda → DynamoDB query), depending on whether an extension
|
|
101
|
+
// switched interception on for this deployment. Provisioning the interceptor
|
|
102
|
+
// is the framework's job — see `QueryInterceptor_Provisioning`; the first
|
|
103
|
+
// Query resolver under a plugin creates it and the rest reuse it.
|
|
110
104
|
let makeQueryResolver = (~resolverName, ~field, ~code) =>
|
|
111
|
-
switch
|
|
105
|
+
switch QueryInterceptor_Provisioning.dataSourceName(~api, ~opts) {
|
|
112
106
|
| None =>
|
|
113
107
|
Resolver.makeUnitJsResolver(
|
|
114
108
|
~name=resolverName,
|
|
@@ -119,7 +113,7 @@ let make: ReventlessCore.QueryDb_Adapter.resolversMaker<api, role> = (
|
|
|
119
113
|
~code,
|
|
120
114
|
~opts,
|
|
121
115
|
)
|
|
122
|
-
| Some(
|
|
116
|
+
| Some(interceptorDsName) =>
|
|
123
117
|
let interceptorFn = Function.makeJs(
|
|
124
118
|
~name=resolverName ++ "Interceptor",
|
|
125
119
|
~api,
|
|
@@ -14,13 +14,10 @@ import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/s
|
|
|
14
14
|
import * as AppSync_Resolver_Functions$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/AppSync/AppSync_Resolver_Functions.res.mjs";
|
|
15
15
|
import * as AppSync_Resolver_Retrying$ReventlessAws from "../Api/AppSync_Resolver_Retrying.res.mjs";
|
|
16
16
|
import * as GraphQL_FragmentGenerator$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/GraphQL_FragmentGenerator.res.mjs";
|
|
17
|
+
import * as QueryInterceptor_Provisioning$ReventlessAws from "./QueryInterceptor_Provisioning.res.mjs";
|
|
17
18
|
|
|
18
19
|
let log = Logger$ReventlessCore.fromEnv();
|
|
19
20
|
|
|
20
|
-
let queryInterceptorConfig = {
|
|
21
|
-
contents: undefined
|
|
22
|
-
};
|
|
23
|
-
|
|
24
21
|
function interceptorCode(readModelName) {
|
|
25
22
|
return `import { util } from '@aws-appsync/utils';
|
|
26
23
|
export function request(ctx) {
|
|
@@ -70,11 +67,11 @@ function make(name, api, apiRole, dataSourceName, indexes, subIdField, idResolve
|
|
|
70
67
|
let includeIdParam = registryEntry !== undefined ? registryEntry.includeIdParam : true;
|
|
71
68
|
let connectionSpec = registryEntry !== undefined ? registryEntry.connectionSpec : true;
|
|
72
69
|
let makeQueryResolver = (resolverName, field, code) => {
|
|
73
|
-
let
|
|
74
|
-
if (
|
|
70
|
+
let interceptorDsName = QueryInterceptor_Provisioning$ReventlessAws.dataSourceName(api, opts);
|
|
71
|
+
if (interceptorDsName === undefined) {
|
|
75
72
|
return AppSync_Resolver_Retrying$ReventlessAws.makeUnitJsResolver(resolverName, api, dataSourceName, "Query", field, code, opts);
|
|
76
73
|
}
|
|
77
|
-
let interceptorFn = AppSync_Function$PulumiAws.makeJs(resolverName + "Interceptor", api,
|
|
74
|
+
let interceptorFn = AppSync_Function$PulumiAws.makeJs(resolverName + "Interceptor", api, interceptorDsName, interceptorCode(name$1), opts);
|
|
78
75
|
let queryFn = AppSync_Function$PulumiAws.makeJs(resolverName + "Query", api, dataSourceName, code, opts);
|
|
79
76
|
return AppSync_Resolver_Retrying$ReventlessAws.makePipelineJsResolver(resolverName, api, "Query", field, AppSync_Resolver_Functions$PulumiAws.pipelinePassThrough, [
|
|
80
77
|
interceptorFn,
|
|
@@ -238,7 +235,6 @@ let Resolver;
|
|
|
238
235
|
export {
|
|
239
236
|
Resolver,
|
|
240
237
|
log,
|
|
241
|
-
queryInterceptorConfig,
|
|
242
238
|
interceptorCode,
|
|
243
239
|
internalRowRequiredAttr,
|
|
244
240
|
make,
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/** Provisions the query-interceptor path on AppSync, when an extension has asked
|
|
2
|
+
for it via `ReventlessCore.QueryInterception.use`.
|
|
3
|
+
|
|
4
|
+
The framework owns everything provider-shaped here — the runtime, its
|
|
5
|
+
execution role, the AppSync Lambda data source and the role that lets AppSync
|
|
6
|
+
invoke it — because all of it needs the api handle and a service role, which
|
|
7
|
+
exist only inside the plugin build. An extension supplies none of it; see the
|
|
8
|
+
seam's own docstring for why that split is the point.
|
|
9
|
+
|
|
10
|
+
**One interceptor per api, not per read model.** A data source belongs to one
|
|
11
|
+
api, so it is memoised on the api handle *by identity* — the builders thread
|
|
12
|
+
a single `api` value through every read model that resolves against it, and
|
|
13
|
+
identity is what guarantees the data source ends up attached to the very api
|
|
14
|
+
the resolver names. Keying on anything looser (the owning plugin, say) would
|
|
15
|
+
be right until one key spanned two apis, and the failure then is a resolver
|
|
16
|
+
pointing at a data source that does not exist on its api — rejected at deploy
|
|
17
|
+
time, with nothing in the message about why.
|
|
18
|
+
|
|
19
|
+
The api's *contents* are an `Output` and are unknown at
|
|
20
|
+
resolver-construction time, which is why the ambient `ResourceAttribution`
|
|
21
|
+
plugin supplies the resource NAMES instead. That is presentation, not
|
|
22
|
+
identity: a second api under one plugin gets a suffixed name rather than a
|
|
23
|
+
shared data source. Substrate built outside any plugin construct (the admin
|
|
24
|
+
read models on the platform api) is named `Platform`.
|
|
25
|
+
|
|
26
|
+
The runtime is built through `RuntimeEnvironment_Lambda.makeFromCodeAsset`
|
|
27
|
+
rather than a bespoke `Lambda.Function.make`, which is what makes the seams
|
|
28
|
+
compose: that path bundles registered runtime extensions into the archive and
|
|
29
|
+
writes `RUNTIME_EXTENSIONS`, so an extension registering its interceptor in
|
|
30
|
+
`onColdStart` reaches this runtime without a second registration. */
|
|
31
|
+
|
|
32
|
+
open PulumiAws
|
|
33
|
+
|
|
34
|
+
// Memoised on the api handle itself — see the module docstring for why identity
|
|
35
|
+
// rather than a name. The value is the data-source name the resolver pipeline
|
|
36
|
+
// references.
|
|
37
|
+
let byApi: Map.t<Types.AppSync.api, Pulumi.Input.t<string>> = Map.make()
|
|
38
|
+
|
|
39
|
+
// How many interceptors a given owner already named, so a second api under one
|
|
40
|
+
// owner gets a distinct resource name instead of colliding with the first.
|
|
41
|
+
let namesUsed: dict<int> = Dict.make()
|
|
42
|
+
|
|
43
|
+
/** A stable, readable resource-name stem for the interceptor: the owning plugin,
|
|
44
|
+
or `Platform` for substrate provisioned outside any plugin construct. */
|
|
45
|
+
let nameFor = () => {
|
|
46
|
+
let owner = ReventlessCore.ResourceAttribution.current.contents.plugin->Option.getOr("Platform")
|
|
47
|
+
let used = namesUsed->Dict.get(owner)->Option.getOr(0)
|
|
48
|
+
namesUsed->Dict.set(owner, used + 1)
|
|
49
|
+
used == 0
|
|
50
|
+
? owner ++ "QueryInterceptor"
|
|
51
|
+
: owner ++ "QueryInterceptor" ++ (used + 1)->Int.toString
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let buildInterceptor = (~api: Types.AppSync.api, ~opts) => {
|
|
55
|
+
let name = nameFor()
|
|
56
|
+
let componentOpts: Pulumi.ComponentResource.options = {
|
|
57
|
+
parent: ?(opts: Pulumi.CustomResourceOptions.t).parent,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let packageDirs = Dict.fromArray([
|
|
61
|
+
(
|
|
62
|
+
"@reventlessdev/reventless-aws",
|
|
63
|
+
Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-aws"),
|
|
64
|
+
),
|
|
65
|
+
])
|
|
66
|
+
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
67
|
+
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/QueryDb/QueryInterceptor_Lambda.res.mjs",
|
|
68
|
+
~packageDirs,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
// Sits in front of every read, so it is sized for a decision rather than for
|
|
72
|
+
// work: small memory, short timeout. A hook that needs longer than this is
|
|
73
|
+
// doing something a read path cannot afford anyway.
|
|
74
|
+
let runtime = RuntimeEnvironment_Lambda.makeFromCodeAsset(
|
|
75
|
+
~name,
|
|
76
|
+
~unitKind=ReventlessCore.Monitoring.Other("QueryInterceptor"),
|
|
77
|
+
~componentKind=ReventlessCore.ComponentType.Plugin,
|
|
78
|
+
~code,
|
|
79
|
+
~sourceCodeHash,
|
|
80
|
+
~memorySize=256,
|
|
81
|
+
~timeout=10,
|
|
82
|
+
~opts=componentOpts,
|
|
83
|
+
)
|
|
84
|
+
let lambdaArn = runtime.parts.lambda->Pulumi.Output.flatMap(lambda => lambda.arn)
|
|
85
|
+
|
|
86
|
+
let dataSourceRole = IAM.Role.makeWithDefaultPolicy(
|
|
87
|
+
~name=name ++ "DataSource",
|
|
88
|
+
~servicePrincipal=AWS.AppSync.principal->Pulumi.Output.make,
|
|
89
|
+
~tags=AWS.Tags.make(
|
|
90
|
+
~name=name ++ "DataSource",
|
|
91
|
+
~kind=ReventlessCore.ComponentType.Plugin,
|
|
92
|
+
~role=Identity,
|
|
93
|
+
~scope=Plugin,
|
|
94
|
+
),
|
|
95
|
+
~opts,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
let _ =
|
|
99
|
+
(lambdaArn, dataSourceRole.id)
|
|
100
|
+
->Pulumi.Output.all2
|
|
101
|
+
->Pulumi.Output.apply(((lambdaArn, dataSourceRoleId)) => {
|
|
102
|
+
open PolicyDocument
|
|
103
|
+
let _attach = IAM.RolePolicy.make(
|
|
104
|
+
~name=name ++ "DataSource",
|
|
105
|
+
~args={
|
|
106
|
+
IAM.RolePolicy.policy: PolicyDocument.make(
|
|
107
|
+
~id=name ++ "DataSourcePolicy",
|
|
108
|
+
~statements=[
|
|
109
|
+
{
|
|
110
|
+
sid: "AllowDataSourceInvokeInterceptor",
|
|
111
|
+
effect: Allow,
|
|
112
|
+
actions: Action("lambda:InvokeFunction"),
|
|
113
|
+
resources: Resource(lambdaArn),
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
)
|
|
117
|
+
->PolicyDocument.toJsonString
|
|
118
|
+
->Pulumi.Input.make,
|
|
119
|
+
role: dataSourceRoleId->Pulumi.Input.make,
|
|
120
|
+
},
|
|
121
|
+
~opts,
|
|
122
|
+
)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
let dataSource = AppSync.DataSource.make(
|
|
126
|
+
~name=name ++ "DataSource",
|
|
127
|
+
~args={
|
|
128
|
+
type_: AWS_LAMBDA,
|
|
129
|
+
apiId: api->Pulumi.Output.flatMap(api => api.id)->Pulumi.Output.asInput,
|
|
130
|
+
lambdaConfig: {
|
|
131
|
+
AppSync.DataSource.functionArn: lambdaArn->Pulumi.Output.asInput,
|
|
132
|
+
}->Pulumi.Input.make,
|
|
133
|
+
serviceRoleArn: dataSourceRole.arn->Pulumi.Output.asInput,
|
|
134
|
+
},
|
|
135
|
+
~opts=Some(opts),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
dataSource.name->Pulumi.Output.asInput
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** The data source a Query resolver should put in front of its DynamoDB read, or
|
|
142
|
+
`None` when interception is off — in which case the caller builds the unit
|
|
143
|
+
resolver it always did and nothing here is provisioned. */
|
|
144
|
+
let dataSourceName = (~api: Types.AppSync.api, ~opts): option<Pulumi.Input.t<string>> =>
|
|
145
|
+
if !ReventlessCore.QueryInterception.isEnabled() {
|
|
146
|
+
None
|
|
147
|
+
} else {
|
|
148
|
+
switch byApi->Map.get(api) {
|
|
149
|
+
| Some(existing) => Some(existing)
|
|
150
|
+
| None =>
|
|
151
|
+
let created = buildInterceptor(~api, ~opts)
|
|
152
|
+
byApi->Map.set(api, created)
|
|
153
|
+
Some(created)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Aws from "@pulumi/aws";
|
|
4
|
+
import * as IAM$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/IAM.res.mjs";
|
|
5
|
+
import * as Output$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Output.res.mjs";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Pulumi from "@pulumi/pulumi";
|
|
8
|
+
import * as AWS$ReventlessAws from "../AWS.res.mjs";
|
|
9
|
+
import * as AWS_Tags$ReventlessAws from "../AWS_Tags.res.mjs";
|
|
10
|
+
import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
|
|
11
|
+
import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
12
|
+
import * as QueryInterception$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/QueryInterception/QueryInterception.res.mjs";
|
|
13
|
+
import * as ResourceAttribution$ReventlessCore from "@reventlessdev/reventless-core/src/ResourceAttribution.res.mjs";
|
|
14
|
+
import * as RuntimeEnvironment_Lambda$ReventlessAws from "../Runtime/RuntimeEnvironment_Lambda.res.mjs";
|
|
15
|
+
|
|
16
|
+
let byApi = new Map();
|
|
17
|
+
|
|
18
|
+
let namesUsed = {};
|
|
19
|
+
|
|
20
|
+
function nameFor() {
|
|
21
|
+
let owner = Stdlib_Option.getOr(ResourceAttribution$ReventlessCore.current.contents.plugin, "Platform");
|
|
22
|
+
let used = Stdlib_Option.getOr(namesUsed[owner], 0);
|
|
23
|
+
namesUsed[owner] = used + 1 | 0;
|
|
24
|
+
if (used === 0) {
|
|
25
|
+
return owner + "QueryInterceptor";
|
|
26
|
+
} else {
|
|
27
|
+
return owner + "QueryInterceptor" + (used + 1 | 0).toString();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function buildInterceptor(api, opts) {
|
|
32
|
+
let name = nameFor();
|
|
33
|
+
let componentOpts_parent = opts.parent;
|
|
34
|
+
let componentOpts = {
|
|
35
|
+
parent: componentOpts_parent
|
|
36
|
+
};
|
|
37
|
+
let packageDirs = Object.fromEntries([[
|
|
38
|
+
"@reventlessdev/reventless-aws",
|
|
39
|
+
Util_Bundle$ReventlessAws.resolvePackageRoot(undefined, "@reventlessdev/reventless-aws")
|
|
40
|
+
]]);
|
|
41
|
+
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/QueryDb/QueryInterceptor_Lambda.res.mjs", packageDirs, undefined, undefined);
|
|
42
|
+
let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, {
|
|
43
|
+
TAG: "Other",
|
|
44
|
+
_0: "QueryInterceptor"
|
|
45
|
+
}, "Plugin", match.code, match.sourceCodeHash, undefined, 256, 10, undefined, undefined, undefined, undefined, undefined, componentOpts);
|
|
46
|
+
let lambdaArn = Output$Pulumi.flatMap(runtime.parts.lambda, lambda => lambda.arn);
|
|
47
|
+
let dataSourceRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "DataSource", Pulumi.output(AWS$ReventlessAws.AppSync.principal), AWS_Tags$ReventlessAws.make(name + "DataSource", "Plugin", "Identity", "Plugin", undefined, undefined, undefined, undefined), opts);
|
|
48
|
+
Pulumi.all([
|
|
49
|
+
lambdaArn,
|
|
50
|
+
dataSourceRole.id
|
|
51
|
+
]).apply(param => {
|
|
52
|
+
new (Aws.iam.RolePolicy)(name + "DataSource", {
|
|
53
|
+
policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + "DataSourcePolicy", [{
|
|
54
|
+
Sid: "AllowDataSourceInvokeInterceptor",
|
|
55
|
+
Effect: "Allow",
|
|
56
|
+
Action: "lambda:InvokeFunction",
|
|
57
|
+
Resource: param[0]
|
|
58
|
+
}])),
|
|
59
|
+
role: param[1]
|
|
60
|
+
}, opts);
|
|
61
|
+
});
|
|
62
|
+
return new (Aws.appsync.DataSource)(name + "DataSource", {
|
|
63
|
+
type: "AWS_LAMBDA",
|
|
64
|
+
apiId: Output$Pulumi.flatMap(api, api => api.id),
|
|
65
|
+
lambdaConfig: {
|
|
66
|
+
functionArn: lambdaArn
|
|
67
|
+
},
|
|
68
|
+
serviceRoleArn: dataSourceRole.arn
|
|
69
|
+
}, opts).name;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function dataSourceName(api, opts) {
|
|
73
|
+
if (!QueryInterception$ReventlessCore.isEnabled()) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
let existing = byApi.get(api);
|
|
77
|
+
if (existing !== undefined) {
|
|
78
|
+
return existing;
|
|
79
|
+
}
|
|
80
|
+
let created = buildInterceptor(api, opts);
|
|
81
|
+
byApi.set(api, created);
|
|
82
|
+
return created;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export {
|
|
86
|
+
byApi,
|
|
87
|
+
namesUsed,
|
|
88
|
+
nameFor,
|
|
89
|
+
buildInterceptor,
|
|
90
|
+
dataSourceName,
|
|
91
|
+
}
|
|
92
|
+
/* byApi Not a pure module */
|