@reventlessdev/reventless-aws 3.0.0-alpha.211 → 3.0.0-alpha.213
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 +8 -8
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +16 -3
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +11 -1
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +4 -1
- package/src/components/Api/AppSync_Adapter.res +19 -0
- package/src/components/Api/AppSync_Adapter.res.mjs +12 -1
- package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res +40 -35
- package/src/plugin/heartbeat/HeartbeatRunner_CloudWatchEvents.res.mjs +14 -15
- package/src/plugin/runtime/PluginRuntime_Builder.res +58 -69
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +31 -29
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,27 @@
|
|
|
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.213 (2026-07-15)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @reventlessdev/reventless-aws
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# 3.0.0-alpha.212 (2026-07-14)
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **aws:** create EC publishToAggregates SendMessage grants — cross-plugin extension→DCB publishes no longer AccessDenied ([c2126df](https://github.com/ReventlessDev/reventless-core/commit/c2126df0b9af4a79a980f2668ffc18a7760ef4c3))
|
|
19
|
+
* **aws:** create heartbeat SendMessage grant at top level; document the pulumi.all side-channel gotcha ([6ffa06a](https://github.com/ReventlessDev/reventless-core/commit/6ffa06ac87070af4027ba5753ac7c2cb216a76d0))
|
|
20
|
+
* **aws:** filter Internal read models from Platform_ComponentDefinitions ([fda1a81](https://github.com/ReventlessDev/reventless-core/commit/fda1a81f420f64bf4bdf3c988f84829719ffc0d1))
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
* **core:** thread ~comp through Projection.handleAction for attributed action logging ([fa32d93](https://github.com/ReventlessDev/reventless-core/commit/fa32d93dedabea1b1f655f01a4ea769a5908b143))
|
|
24
|
+
* surface masked GraphQL resolver errors server-side (local + AWS) ([b3bca51](https://github.com/ReventlessDev/reventless-core/commit/b3bca51b539e40c604b3db338d570de6215ea837))
|
|
25
|
+
|
|
26
|
+
|
|
6
27
|
# 3.0.0-alpha.211 (2026-07-14)
|
|
7
28
|
|
|
8
29
|
### Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.213",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -10,15 +10,15 @@
|
|
|
10
10
|
"uuid": "^13.0.0",
|
|
11
11
|
"@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.21",
|
|
12
12
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.27",
|
|
13
|
-
"@reventlessdev/rescript-
|
|
14
|
-
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.52",
|
|
13
|
+
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.53",
|
|
15
14
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.15",
|
|
16
|
-
"@reventlessdev/
|
|
17
|
-
"@reventlessdev/reventless-
|
|
15
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.7",
|
|
16
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.169",
|
|
17
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.33",
|
|
18
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.100",
|
|
18
19
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.15",
|
|
19
|
-
"@reventlessdev/reventless-
|
|
20
|
-
"@reventlessdev/reventless-
|
|
21
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.76"
|
|
20
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.77",
|
|
21
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.26"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"rescript": "^12.3.0",
|
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
// field. The persisted structure is sury-encoded with the exact same shape
|
|
6
6
|
// the in-memory adapter's `Platform_ComponentDefinitionsApi.encodePluginStructureEntry`
|
|
7
7
|
// produces (literal-string `commandLevel`, `null`-encoded options), so the
|
|
8
|
-
// handler
|
|
9
|
-
//
|
|
8
|
+
// handler wraps each persisted structure with `pluginId` without decoding.
|
|
9
|
+
// One caveat: the persisted structure is PRE-filter — it still carries Internal
|
|
10
|
+
// ReadModels / StateViewSlices for developer tooling. The handler re-applies the
|
|
11
|
+
// `isPublicQueryable` filter (mirroring `encodePluginStructureEntry`) so Internal
|
|
12
|
+
// components stay out of the deployed AutoUI, matching the in-memory adapter.
|
|
10
13
|
|
|
11
14
|
open PulumiAws
|
|
12
15
|
|
|
@@ -64,6 +67,16 @@ export async function handler() {
|
|
|
64
67
|
}
|
|
65
68
|
return 0;
|
|
66
69
|
};
|
|
70
|
+
// The persisted structure carries Internal ReadModels / StateViewSlices for
|
|
71
|
+
// developer tooling; they must stay out of the deployed AutoUI. Mirror
|
|
72
|
+
// ReventlessCore.Platform_ComponentDefinitionsApi.isPublicQueryable here on the
|
|
73
|
+
// read path (the persisted structure is pre-filter — it is NOT re-encoded).
|
|
74
|
+
const isPublicQueryable = (q) => q?.visibility !== "Internal";
|
|
75
|
+
const filterStructure = (s) => ({
|
|
76
|
+
...s,
|
|
77
|
+
readModels: (s.readModels ?? []).filter(isPublicQueryable),
|
|
78
|
+
stateViewSlices: (s.stateViewSlices ?? []).filter(isPublicQueryable),
|
|
79
|
+
});
|
|
67
80
|
const latestByName = new Map();
|
|
68
81
|
for (const item of items) {
|
|
69
82
|
if (!item || !item.structure) continue;
|
|
@@ -71,7 +84,7 @@ export async function handler() {
|
|
|
71
84
|
const version = String(item.name).split("@")[1] ?? "";
|
|
72
85
|
const prev = latestByName.get(name);
|
|
73
86
|
if (!prev || cmpVer(version, prev.version) > 0) {
|
|
74
|
-
latestByName.set(name, { version, entry: { pluginId: name, ...item.structure } });
|
|
87
|
+
latestByName.set(name, { version, entry: { pluginId: name, ...filterStructure(item.structure) } });
|
|
75
88
|
}
|
|
76
89
|
}
|
|
77
90
|
const userEntries = [...latestByName.values()].map(v => v.entry);
|
|
@@ -65,6 +65,16 @@ export async function handler() {
|
|
|
65
65
|
}
|
|
66
66
|
return 0;
|
|
67
67
|
};
|
|
68
|
+
// The persisted structure carries Internal ReadModels / StateViewSlices for
|
|
69
|
+
// developer tooling; they must stay out of the deployed AutoUI. Mirror
|
|
70
|
+
// ReventlessCore.Platform_ComponentDefinitionsApi.isPublicQueryable here on the
|
|
71
|
+
// read path (the persisted structure is pre-filter — it is NOT re-encoded).
|
|
72
|
+
const isPublicQueryable = (q) => q?.visibility !== "Internal";
|
|
73
|
+
const filterStructure = (s) => ({
|
|
74
|
+
...s,
|
|
75
|
+
readModels: (s.readModels ?? []).filter(isPublicQueryable),
|
|
76
|
+
stateViewSlices: (s.stateViewSlices ?? []).filter(isPublicQueryable),
|
|
77
|
+
});
|
|
68
78
|
const latestByName = new Map();
|
|
69
79
|
for (const item of items) {
|
|
70
80
|
if (!item || !item.structure) continue;
|
|
@@ -72,7 +82,7 @@ export async function handler() {
|
|
|
72
82
|
const version = String(item.name).split("@")[1] ?? "";
|
|
73
83
|
const prev = latestByName.get(name);
|
|
74
84
|
if (!prev || cmpVer(version, prev.version) > 0) {
|
|
75
|
-
latestByName.set(name, { version, entry: { pluginId: name, ...item.structure } });
|
|
85
|
+
latestByName.set(name, { version, entry: { pluginId: name, ...filterStructure(item.structure) } });
|
|
76
86
|
}
|
|
77
87
|
}
|
|
78
88
|
const userEntries = [...latestByName.values()].map(v => v.entry);
|
|
@@ -91,7 +91,10 @@ export function buildJsonEventsHandler(specModule, projectionModule, queryDbTabl
|
|
|
91
91
|
// every such action hit the `MissingSubIdConfig` guard and silently write nothing,
|
|
92
92
|
// while the test-harness callback path (which threads it) stayed green. `undefined`
|
|
93
93
|
// for slices without an `@subId` is correct — those never emit sub-id actions.
|
|
94
|
-
|
|
94
|
+
// First positional arg is the compiled `~comp=` optional (attribution
|
|
95
|
+
// for Projection.handleAction's debug-lazy action log) — the slice's
|
|
96
|
+
// spec name. Compiled shape: (comp, action, operations, subIdConfig).
|
|
97
|
+
Effect.promise(() => handleAction(specModule.name, action, queryDbOps, specModule.subIdConfig)),
|
|
95
98
|
_ => {}
|
|
96
99
|
)
|
|
97
100
|
);
|
|
@@ -486,6 +486,24 @@ let _makeApiResourceWith = (
|
|
|
486
486
|
~opts=Some(customOpts),
|
|
487
487
|
)
|
|
488
488
|
|
|
489
|
+
// Let AppSync push field-resolver errors to CloudWatch. The API's own service
|
|
490
|
+
// role is already assumable by appsync.amazonaws.com, so attach the AWS-managed
|
|
491
|
+
// push policy to it and reuse it as the logging role. `fieldLogLevel = ERROR`
|
|
492
|
+
// (below) then captures resolver failures AppSync otherwise swallows into the
|
|
493
|
+
// client's `errors[]` — e.g. a non-null coercion on a stale read-model row.
|
|
494
|
+
let _appsyncCwLogsAttachment = IAM.RolePolicyAttachment.make(
|
|
495
|
+
~name=`${name}-appsync-cwlogs`,
|
|
496
|
+
~args={
|
|
497
|
+
role: iamRole.name->Pulumi.Output.asInput,
|
|
498
|
+
policyArn: "arn:aws:iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs"->Pulumi.Input.make,
|
|
499
|
+
},
|
|
500
|
+
~opts=Some(customOpts),
|
|
501
|
+
)
|
|
502
|
+
let appsyncLogConfig: AppSync.GraphQLApi.logConfig = {
|
|
503
|
+
cloudwatchLogsRoleArn: iamRole.arn->Pulumi.Output.asInput,
|
|
504
|
+
fieldLogLevel: AppSync.GraphQLApi.ERROR->Pulumi.Input.make,
|
|
505
|
+
}
|
|
506
|
+
|
|
489
507
|
// Resolve the Cognito UserPool — either supplied by the caller (plugin-stack
|
|
490
508
|
// source APIs read it from the platform's StackReference exports so they
|
|
491
509
|
// never provision pool/client resources of their own) or resolved via
|
|
@@ -520,6 +538,7 @@ let _makeApiResourceWith = (
|
|
|
520
538
|
)->Pulumi.Input.make,
|
|
521
539
|
]->Pulumi.Input.make,
|
|
522
540
|
schema: ?(schema->Option.map(Pulumi.Input.make)),
|
|
541
|
+
logConfig: appsyncLogConfig->Pulumi.Input.make,
|
|
523
542
|
}
|
|
524
543
|
let graphQLApi = AppSync.GraphQLApi.make(~name, ~args=apiArgs, ~opts=Some(customOpts))
|
|
525
544
|
|
|
@@ -276,6 +276,15 @@ function _makeApiResourceWith(name, schema, userPoolConfig, opts) {
|
|
|
276
276
|
let iamRole = new (Aws.iam.Role)(name + `-appsync-role`, {
|
|
277
277
|
assumeRolePolicy: `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Service":"appsync.amazonaws.com"},"Action":"sts:AssumeRole"}]}`
|
|
278
278
|
}, customOpts);
|
|
279
|
+
new (Aws.iam.RolePolicyAttachment)(name + `-appsync-cwlogs`, {
|
|
280
|
+
policyArn: "arn:aws:iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs",
|
|
281
|
+
role: iamRole.name
|
|
282
|
+
}, customOpts);
|
|
283
|
+
let appsyncLogConfig_cloudwatchLogsRoleArn = iamRole.arn;
|
|
284
|
+
let appsyncLogConfig = {
|
|
285
|
+
cloudwatchLogsRoleArn: appsyncLogConfig_cloudwatchLogsRoleArn,
|
|
286
|
+
fieldLogLevel: "ERROR"
|
|
287
|
+
};
|
|
279
288
|
let userPoolConfigOut = userPoolConfig !== undefined ? userPoolConfig : Auth_Cognito$ReventlessAws.make(name + `-auth`, undefined).apply(c => ({
|
|
280
289
|
userPoolId: c.userPoolId,
|
|
281
290
|
defaultAction: "ALLOW",
|
|
@@ -286,11 +295,13 @@ function _makeApiResourceWith(name, schema, userPoolConfig, opts) {
|
|
|
286
295
|
let apiArgs_additionalAuthenticationProviders = [{
|
|
287
296
|
authenticationType: "AWS_IAM"
|
|
288
297
|
}];
|
|
298
|
+
let apiArgs_logConfig = appsyncLogConfig;
|
|
289
299
|
let apiArgs = {
|
|
290
300
|
authenticationType: "AMAZON_COGNITO_USER_POOLS",
|
|
291
301
|
schema: apiArgs_schema,
|
|
292
302
|
userPoolConfig: apiArgs_userPoolConfig,
|
|
293
|
-
additionalAuthenticationProviders: apiArgs_additionalAuthenticationProviders
|
|
303
|
+
additionalAuthenticationProviders: apiArgs_additionalAuthenticationProviders,
|
|
304
|
+
logConfig: apiArgs_logConfig
|
|
294
305
|
};
|
|
295
306
|
let graphQLApi = new (Aws.appsync.GraphQLApi)(name, apiArgs, customOpts);
|
|
296
307
|
return [
|
|
@@ -25,16 +25,48 @@ let make: ReventlessCore.Heartbeat_Adapter.runnerMaker<runtimeParts> = (
|
|
|
25
25
|
let lambdaRole = runtime.parts.lambdaRole
|
|
26
26
|
let coreSqsQueue = remoteChannel.resources->Util_SQS.findResolvedResource
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
// Grant sqs:SendMessage on the Core Plugin ExtensionPoint command topic at
|
|
29
|
+
// TOP LEVEL. Previously this RolePolicy was created inside the
|
|
30
|
+
// `Pulumi.Output.apply` below — and a resource created inside an apply
|
|
31
|
+
// callback does not reliably register with the engine, which intermittently
|
|
32
|
+
// left the heartbeat Lambda without this grant (IAM AccessDenied:
|
|
33
|
+
// `sqs:sendmessage on CorePluginExtPointCmdTopic`). `coreSqsQueue.urn` is an
|
|
34
|
+
// already-resolved ARN string from the remote channel and `lambdaRole.id` an
|
|
35
|
+
// Output, so neither needs an apply. Mirrors the EC `pta` grant fix — see
|
|
36
|
+
// docs/analysis/ec-publish-to-aggregates-grant-broken.md.
|
|
37
|
+
let _attachHeartbeatLambdaPolicy = {
|
|
38
|
+
open PulumiAws.PolicyDocument
|
|
39
|
+
let heartbeatLambdaSendMessagePolicyDocument = PulumiAws.PolicyDocument.make(
|
|
40
|
+
~statements=[
|
|
41
|
+
{
|
|
42
|
+
sid: "AllowLambdaToSendSQS",
|
|
43
|
+
effect: Allow,
|
|
44
|
+
actions: Action("sqs:SendMessage"),
|
|
45
|
+
resources: Resource(coreSqsQueue.urn),
|
|
46
|
+
},
|
|
47
|
+
],
|
|
33
48
|
)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
49
|
+
PulumiAws.IAM.RolePolicy.make(
|
|
50
|
+
~name=name ++ "RolePolicy",
|
|
51
|
+
~args={
|
|
52
|
+
policy: PulumiAws.PolicyDocument.mergePolicyDocuments(
|
|
53
|
+
name ++ "Policy",
|
|
54
|
+
[
|
|
55
|
+
PulumiAws.Lambda.defaultLoggingPolicyDocument,
|
|
56
|
+
heartbeatLambdaSendMessagePolicyDocument,
|
|
57
|
+
],
|
|
58
|
+
)->Pulumi.Output.asInput,
|
|
59
|
+
role: lambdaRole.id->Pulumi.Output.asInput,
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
}
|
|
37
63
|
|
|
64
|
+
// The Lambda permission and CloudWatch event target genuinely need the
|
|
65
|
+
// Lambda's resolved arn/name, so they stay inside an apply.
|
|
66
|
+
let _permissionAndEventTarget =
|
|
67
|
+
(lambda->Pulumi.Output.flatMap(lambda => lambda.arn), lambda->Pulumi.Output.flatMap(lambda => lambda.name))
|
|
68
|
+
->Pulumi.Output.all2
|
|
69
|
+
->Pulumi.Output.apply(((lambdaArn, lambdaName)) => {
|
|
38
70
|
let _addHeartbeatLambdaPermission = PulumiAws.Lambda.Permission.make(
|
|
39
71
|
~name,
|
|
40
72
|
~args={
|
|
@@ -45,33 +77,6 @@ let make: ReventlessCore.Heartbeat_Adapter.runnerMaker<runtimeParts> = (
|
|
|
45
77
|
~opts,
|
|
46
78
|
)
|
|
47
79
|
|
|
48
|
-
let heartbeatLambdaSendMessagePolicyDocument = {
|
|
49
|
-
PulumiAws.PolicyDocument.make(
|
|
50
|
-
~statements=[
|
|
51
|
-
{
|
|
52
|
-
sid: "AllowLambdaToSendSQS",
|
|
53
|
-
effect: Allow,
|
|
54
|
-
actions: Action("sqs:SendMessage"),
|
|
55
|
-
resources: Resource(coreSqsQueue.urn),
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
let _attachHeartbeatLambdaPolicy = PulumiAws.IAM.RolePolicy.make(
|
|
62
|
-
~name=name ++ "RolePolicy",
|
|
63
|
-
~args={
|
|
64
|
-
policy: PulumiAws.PolicyDocument.mergePolicyDocuments(
|
|
65
|
-
name ++ "Policy",
|
|
66
|
-
[
|
|
67
|
-
PulumiAws.Lambda.defaultLoggingPolicyDocument,
|
|
68
|
-
heartbeatLambdaSendMessagePolicyDocument,
|
|
69
|
-
],
|
|
70
|
-
)->Pulumi.Output.asInput,
|
|
71
|
-
role: heartbeatRoleId->Pulumi.Input.make,
|
|
72
|
-
},
|
|
73
|
-
)
|
|
74
|
-
|
|
75
80
|
let _cloudwatchEventTarget = {
|
|
76
81
|
open PulumiAws.Cloudwatch
|
|
77
82
|
EventTarget.make(
|
|
@@ -27,29 +27,28 @@ function make(name, remoteChannel, timeout, runtime, opts) {
|
|
|
27
27
|
let lambda = runtime.parts.lambda;
|
|
28
28
|
let lambdaRole = runtime.parts.lambdaRole;
|
|
29
29
|
let coreSqsQueue = Util_SQS$ReventlessAws.findResolvedResource(remoteChannel.resources);
|
|
30
|
+
let heartbeatLambdaSendMessagePolicyDocument = PolicyDocument$PulumiAws.make(undefined, undefined, [{
|
|
31
|
+
Sid: "AllowLambdaToSendSQS",
|
|
32
|
+
Effect: "Allow",
|
|
33
|
+
Action: "sqs:SendMessage",
|
|
34
|
+
Resource: coreSqsQueue.urn
|
|
35
|
+
}]);
|
|
36
|
+
new (Aws.iam.RolePolicy)(name + "RolePolicy", {
|
|
37
|
+
policy: PolicyDocument$PulumiAws.mergePolicyDocuments(name + "Policy", [
|
|
38
|
+
Lambda$PulumiAws.defaultLoggingPolicyDocument,
|
|
39
|
+
heartbeatLambdaSendMessagePolicyDocument
|
|
40
|
+
]),
|
|
41
|
+
role: lambdaRole.id
|
|
42
|
+
});
|
|
30
43
|
Pulumi.all([
|
|
31
44
|
Output$Pulumi.flatMap(lambda, lambda => lambda.arn),
|
|
32
|
-
Output$Pulumi.flatMap(lambda, lambda => lambda.name)
|
|
33
|
-
lambdaRole.id
|
|
45
|
+
Output$Pulumi.flatMap(lambda, lambda => lambda.name)
|
|
34
46
|
]).apply(param => {
|
|
35
47
|
new (Aws.lambda.Permission)(name, {
|
|
36
48
|
action: "lambda:InvokeFunction",
|
|
37
49
|
function: param[1],
|
|
38
50
|
principal: AWS$ReventlessAws.CloudwatchEventRule.principal
|
|
39
51
|
}, opts$1);
|
|
40
|
-
let heartbeatLambdaSendMessagePolicyDocument = PolicyDocument$PulumiAws.make(undefined, undefined, [{
|
|
41
|
-
Sid: "AllowLambdaToSendSQS",
|
|
42
|
-
Effect: "Allow",
|
|
43
|
-
Action: "sqs:SendMessage",
|
|
44
|
-
Resource: coreSqsQueue.urn
|
|
45
|
-
}]);
|
|
46
|
-
new (Aws.iam.RolePolicy)(name + "RolePolicy", {
|
|
47
|
-
policy: PolicyDocument$PulumiAws.mergePolicyDocuments(name + "Policy", [
|
|
48
|
-
Lambda$PulumiAws.defaultLoggingPolicyDocument,
|
|
49
|
-
heartbeatLambdaSendMessagePolicyDocument
|
|
50
|
-
]),
|
|
51
|
-
role: param[2]
|
|
52
|
-
});
|
|
53
52
|
new (Aws.cloudwatch.EventTarget)(name, {
|
|
54
53
|
rule: Cloudwatch_EventTarget$PulumiAws.Rule.ofEventRule(cloudwatchEventRule),
|
|
55
54
|
arn: param[0]
|
|
@@ -622,79 +622,68 @@ module Make = (
|
|
|
622
622
|
aggregateNameSet->Dict.set(aggName, ())
|
|
623
623
|
)
|
|
624
624
|
)
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
None
|
|
625
|
+
// One RolePolicy per extension target, each derived through a SINGLE
|
|
626
|
+
// `.apply` on that target's queue-URL Output. `Pulumi.Output.all` is
|
|
627
|
+
// deliberately NOT used here: the DCB slice's URL is an `.apply`-lifted
|
|
628
|
+
// Output (Dcb_Builder reads the queue id via `operations.apply(_ =>
|
|
629
|
+
// resources[0].id)`), and `Pulumi.Output.all` mis-resolves such lifted
|
|
630
|
+
// Outputs to the `{BS_PRIVATE_NESTED_SOME_NONE: 0}` sentinel (both at
|
|
631
|
+
// preview and up) while a direct `.apply` on the same Output resolves the
|
|
632
|
+
// real URL — verified by deploying both variants. RolePolicy creation
|
|
633
|
+
// happens at TOP LEVEL: calling `RolePolicy.make` inside a
|
|
634
|
+
// `Pulumi.Output.apply` callback does not reliably register the resource
|
|
635
|
+
// (the previous inside-apply variant existed on no collector role in the
|
|
636
|
+
// account). Dict values are heterogeneous at runtime — plain resolved URL
|
|
637
|
+
// strings for aggregate targets, Outputs for DCB-slice targets; anything
|
|
638
|
+
// else is skipped. Inside the apply a non-string URL falls back to a
|
|
639
|
+
// benign placeholder ARN so the policy document stays valid (IAM rejects
|
|
640
|
+
// empty Resource entries with MalformedPolicyDocument).
|
|
641
|
+
// See docs/analysis/ec-publish-to-aggregates-grant-broken.md.
|
|
642
|
+
aggregateNameSet
|
|
643
|
+
->Dict.keysToArray
|
|
644
|
+
->Array.forEach(aggName => {
|
|
645
|
+
let urlOutput = switch context.publishToAggregates->Dict.get(aggName) {
|
|
646
|
+
| Some(o) if Pulumi.Output.isOutput(o) => Some(o)
|
|
647
|
+
| Some(o) if typeof(o) === #string => Some(Pulumi.Output.make((o->Obj.magic: string)))
|
|
648
|
+
| _ => None
|
|
649
|
+
}
|
|
650
|
+
switch urlOutput {
|
|
651
|
+
| Some(urlOutput) =>
|
|
652
|
+
let policyJson = urlOutput->Pulumi.Output.apply(url => {
|
|
653
|
+
let arn = if typeof(url) === #string {
|
|
654
|
+
// queue URL → ARN: https://sqs.<region>.amazonaws.com/<acct>/<name>
|
|
655
|
+
// → arn:aws:sqs:<region>:<acct>:<name>
|
|
656
|
+
switch url->String.split("/") {
|
|
657
|
+
| [_, _, host, acct, name] =>
|
|
658
|
+
let region = host->String.split(".")->Array.get(1)->Option.getOr("eu-west-1")
|
|
659
|
+
`arn:aws:sqs:${region}:${acct}:${name}`
|
|
660
|
+
| _ => url
|
|
661
|
+
}
|
|
663
662
|
} else {
|
|
664
|
-
|
|
665
|
-
PulumiAws.PolicyDocument.make(
|
|
666
|
-
~id=`${name}PublishToAggregatesPolicy`,
|
|
667
|
-
~statements=[
|
|
668
|
-
{
|
|
669
|
-
sid: "AllowEcPublishToAggregateCmdTopics",
|
|
670
|
-
effect: Allow,
|
|
671
|
-
actions: Action("sqs:SendMessage"),
|
|
672
|
-
resources: Resources(queueArns),
|
|
673
|
-
},
|
|
674
|
-
],
|
|
675
|
-
)->PulumiAws.PolicyDocument.toJsonString,
|
|
676
|
-
)
|
|
663
|
+
"arn:aws:sqs:eu-west-1:000000000000:reventless-unresolved-cmd-topic"
|
|
677
664
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
| Some(policyJson) =>
|
|
687
|
-
let _ = PulumiAws.IAM.RolePolicy.make(
|
|
688
|
-
~name=`${name}-publishToAggregates`,
|
|
689
|
-
~args={
|
|
690
|
-
policy: policyJson->Pulumi.Input.make,
|
|
691
|
-
role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
|
|
665
|
+
PulumiAws.PolicyDocument.make(
|
|
666
|
+
~id=`${name}Pta${aggName}Policy`,
|
|
667
|
+
~statements=[
|
|
668
|
+
{
|
|
669
|
+
sid: "AllowEcPublishToAggregateCmdTopic",
|
|
670
|
+
effect: Allow,
|
|
671
|
+
actions: Action("sqs:SendMessage"),
|
|
672
|
+
resources: Resource(arn),
|
|
692
673
|
},
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
674
|
+
],
|
|
675
|
+
)->PulumiAws.PolicyDocument.toJsonString
|
|
676
|
+
})
|
|
677
|
+
let _ = PulumiAws.IAM.RolePolicy.make(
|
|
678
|
+
~name=`${name}-pta-${aggName}`,
|
|
679
|
+
~args={
|
|
680
|
+
policy: policyJson->Pulumi.Output.asInput,
|
|
681
|
+
role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
|
|
682
|
+
},
|
|
696
683
|
)
|
|
697
|
-
|
|
684
|
+
| None => ()
|
|
685
|
+
}
|
|
686
|
+
})
|
|
698
687
|
}
|
|
699
688
|
|
|
700
689
|
let forPluginHeartbeat: ReventlessCore.Runtime.forComponent<
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Aws from "@pulumi/aws";
|
|
4
|
-
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
5
|
import * as Pulumi from "@pulumi/pulumi";
|
|
7
6
|
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
@@ -366,41 +365,44 @@ function Make(EventCollectorChannel) {
|
|
|
366
365
|
aggregateNameSet[aggName] = undefined;
|
|
367
366
|
});
|
|
368
367
|
});
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
let
|
|
372
|
-
|
|
368
|
+
Object.keys(aggregateNameSet).forEach(aggName => {
|
|
369
|
+
let o = context.publishToAggregates[aggName];
|
|
370
|
+
let urlOutput = o !== undefined ? (
|
|
371
|
+
Pulumi.Output.isInstance(o) ? o : (
|
|
372
|
+
typeof o === "string" ? Pulumi.output(o) : undefined
|
|
373
|
+
)
|
|
374
|
+
) : undefined;
|
|
375
|
+
if (urlOutput === undefined) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
let policyJson = urlOutput.apply(url => {
|
|
379
|
+
let arn;
|
|
380
|
+
if (typeof url === "string") {
|
|
373
381
|
let match = url.split("/");
|
|
374
382
|
if (match.length !== 5) {
|
|
375
|
-
|
|
383
|
+
arn = url;
|
|
384
|
+
} else {
|
|
385
|
+
let host = match[2];
|
|
386
|
+
let acct = match[3];
|
|
387
|
+
let name$1 = match[4];
|
|
388
|
+
let region = Stdlib_Option.getOr(host.split(".")[1], "eu-west-1");
|
|
389
|
+
arn = `arn:aws:sqs:` + region + `:` + acct + `:` + name$1;
|
|
376
390
|
}
|
|
377
|
-
let host = match[2];
|
|
378
|
-
let acct = match[3];
|
|
379
|
-
let name = match[4];
|
|
380
|
-
let region = Stdlib_Option.getOr(host.split(".")[1], "eu-west-1");
|
|
381
|
-
return `arn:aws:sqs:` + region + `:` + acct + `:` + name;
|
|
382
|
-
});
|
|
383
|
-
if (queueArns.length === 0) {
|
|
384
|
-
return;
|
|
385
391
|
} else {
|
|
386
|
-
|
|
387
|
-
Sid: "AllowEcPublishToAggregateCmdTopics",
|
|
388
|
-
Effect: "Allow",
|
|
389
|
-
Action: "sqs:SendMessage",
|
|
390
|
-
Resource: queueArns
|
|
391
|
-
}]));
|
|
392
|
+
arn = "arn:aws:sqs:eu-west-1:000000000000:reventless-unresolved-cmd-topic";
|
|
392
393
|
}
|
|
394
|
+
return PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + `Pta` + aggName + `Policy`, [{
|
|
395
|
+
Sid: "AllowEcPublishToAggregateCmdTopic",
|
|
396
|
+
Effect: "Allow",
|
|
397
|
+
Action: "sqs:SendMessage",
|
|
398
|
+
Resource: arn
|
|
399
|
+
}]));
|
|
393
400
|
});
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
policy: policyOpt,
|
|
398
|
-
role: runtime.parts.lambdaRole.id
|
|
399
|
-
});
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
401
|
+
new (Aws.iam.RolePolicy)(name + `-pta-` + aggName, {
|
|
402
|
+
policy: policyJson,
|
|
403
|
+
role: runtime.parts.lambdaRole.id
|
|
402
404
|
});
|
|
403
|
-
}
|
|
405
|
+
});
|
|
404
406
|
};
|
|
405
407
|
let forPluginHeartbeat = (param, connect, memorySizeOpt, timeoutOpt, heartbeat) => {
|
|
406
408
|
let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : 1024;
|