@reventlessdev/reventless-aws 3.0.0-alpha.223 → 3.0.0-alpha.225
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 +17 -0
- package/package.json +6 -6
- package/src/adapter/AWS_Tags.res +36 -0
- package/src/adapter/Adapter_Helpers.res +6 -0
- package/src/adapter/Adapter_Helpers.res.mjs +6 -0
- package/src/adapter/CommandTopic/CommandTopicChannel_Helpers.res +77 -48
- package/src/adapter/CommandTopic/CommandTopicChannel_Helpers.res.mjs +55 -40
- package/src/adapter/Counter/CounterHandler_DynamoDbStream.res +6 -0
- package/src/adapter/Counter/CounterHandler_DynamoDbStream.res.mjs +3 -1
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res +29 -6
- package/src/adapter/DcbEventLog/DcbEventLogStorage_DynamoDb_Runtime.res.mjs +13 -1
- package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res +83 -60
- package/src/adapter/EventCollector/EventCollectorChannel_Helpers.res.mjs +20 -18
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +44 -30
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +22 -23
- package/src/adapter/Postgres/PgProjectionFeed.res +20 -1
- package/src/adapter/Postgres/PgProjectionFeed.res.mjs +3 -1
- package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +6 -2
- package/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs +9 -2
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +10 -4
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs +1 -1
- package/src/adapter/StateTopic/StateTopic_AppSync.res +8 -1
- package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +4 -2
- package/src/plugin/cloner/ClonerRunner_Fargate.res +6 -0
- package/src/plugin/cloner/ClonerRunner_Fargate.res.mjs +2 -1
- package/src/util/Util_DeadLetterQueue.res +39 -4
- package/src/util/Util_DeadLetterQueue.res.mjs +16 -4
- package/src/util/Util_EventSourceMapping.res +8 -0
- package/src/util/Util_EventSourceMapping.res.mjs +6 -4
- package/tests/CommandTopicQueuePolicyTest.res +65 -0
- package/tests/CommandTopicQueuePolicyTest.res.mjs +53 -0
- package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res +50 -0
- package/tests/DcbEventLogStorage_DynamoDb_RuntimeTest.res.mjs +47 -0
- package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res +58 -0
- package/tests/integration/DcbEventLogStorage_DynamoDb_IntegrationTest.res.mjs +78 -0
|
@@ -10,64 +10,62 @@ let toResources = (eventTopics: ReventlessCore.EventTopic.allOutputs) =>
|
|
|
10
10
|
->Array.flatMap(outputs => outputs.resources)
|
|
11
11
|
->ReventlessCore.Adapter.resourcesToResolvedOutput
|
|
12
12
|
|
|
13
|
-
let createQueuePolicy = (queue: PulumiAws.SQS.Queue.t, name,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
arnLike: [
|
|
49
|
-
("aws:SourceArn", ConditionValue(`arn:aws:sns:*:${accountId}:*EventTopic-*`)),
|
|
50
|
-
]->Dict.fromArray,
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
],
|
|
54
|
-
)
|
|
55
|
-
->toJsonString
|
|
56
|
-
->Pulumi.Input.make
|
|
57
|
-
let _queuePolicy = {
|
|
58
|
-
PulumiAws.SQS.QueuePolicy.make(
|
|
59
|
-
~name,
|
|
60
|
-
~args={
|
|
61
|
-
queueUrl: queueId->Pulumi.Input.make,
|
|
62
|
-
policy: queuePolicyDocument,
|
|
13
|
+
let createQueuePolicy = (queue: PulumiAws.SQS.Queue.t, name, opts) => {
|
|
14
|
+
// The document is a *value* derived from the resolved ARN, so it is built in
|
|
15
|
+
// an apply. The QueuePolicy itself stays outside: passing `queue.id` as an
|
|
16
|
+
// Output is what registers the policy -> queue dependency, without which
|
|
17
|
+
// Pulumi has no ordering constraint and can delete the queue first on a
|
|
18
|
+
// replacement, leaving the policy's delete polling a queue that is gone.
|
|
19
|
+
let queuePolicyDocument = queue.arn->Pulumi.Output.apply(queueArn => {
|
|
20
|
+
// Scopes cross-plugin SNS sources to this account (aws:SourceAccount) and
|
|
21
|
+
// to topic names following the Reventless EventTopic naming convention
|
|
22
|
+
// (aws:SourceArn arnLike).
|
|
23
|
+
let accountId = queueArn->accountIdOfQueueArn
|
|
24
|
+
PulumiAws.PolicyDocument.make(
|
|
25
|
+
~id=name ++ "QueuePolicy",
|
|
26
|
+
~statements=[
|
|
27
|
+
// Single statement allows SendMessage from any SNS topic owned by
|
|
28
|
+
// this AWS account whose name matches the Reventless EventTopic
|
|
29
|
+
// naming convention. A per-topic arnEquals list would require a
|
|
30
|
+
// redeploy of the receiving plugin whenever the platform's
|
|
31
|
+
// manageSubscriptions hook creates a cross-plugin SNS subscription at
|
|
32
|
+
// runtime. Security boundary: SourceAccount keeps third-party-account
|
|
33
|
+
// topics out; ArnLike + SNS service principal further narrows accepted
|
|
34
|
+
// senders to in-account EventTopic resources.
|
|
35
|
+
{
|
|
36
|
+
sid: "AllowReceiveSnsEvents",
|
|
37
|
+
principal: Principals({
|
|
38
|
+
service: PrincipalIds([AWS.SNS.principal]),
|
|
39
|
+
}),
|
|
40
|
+
effect: Allow,
|
|
41
|
+
actions: Actions(["sqs:SendMessage"]),
|
|
42
|
+
resources: Resource(queueArn),
|
|
43
|
+
conditions: {
|
|
44
|
+
stringEquals: [("aws:SourceAccount", ConditionValue(accountId))]->Dict.fromArray,
|
|
45
|
+
arnLike: [
|
|
46
|
+
("aws:SourceArn", ConditionValue(`arn:aws:sns:*:${accountId}:*EventTopic-*`)),
|
|
47
|
+
]->Dict.fromArray,
|
|
63
48
|
},
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
)->toJsonString
|
|
52
|
+
})
|
|
53
|
+
let _queuePolicy = PulumiAws.SQS.QueuePolicy.make(
|
|
54
|
+
~name,
|
|
55
|
+
~args={
|
|
56
|
+
queueUrl: queue.id->Pulumi.Output.asInput,
|
|
57
|
+
policy: queuePolicyDocument->Pulumi.Output.asInput,
|
|
58
|
+
},
|
|
59
|
+
~opts=Some(opts),
|
|
60
|
+
)
|
|
68
61
|
}
|
|
69
62
|
|
|
70
|
-
let subscribeQueue2SnsTopic = (
|
|
63
|
+
let subscribeQueue2SnsTopic = (
|
|
64
|
+
queue,
|
|
65
|
+
name,
|
|
66
|
+
resources: array<ReventlessInfra.Adapter.resolvedResource>,
|
|
67
|
+
opts,
|
|
68
|
+
) => {
|
|
71
69
|
let _snsTopicSubscriptions = resources->Array.map(resource => {
|
|
72
70
|
log.debug(~comp="EventCollector", `subscribeToSnsTopic: ${name} -> ${resource.name}`)
|
|
73
71
|
let subscription = Util_SQS.subscribeToSnsTopic(
|
|
@@ -82,20 +80,35 @@ let subscribeQueue2SnsTopic = (queue, name, resources: array<ReventlessInfra.Ada
|
|
|
82
80
|
}
|
|
83
81
|
|
|
84
82
|
let connectSqsQueue2SnsTopics = (queue: PulumiAws.SQS.Queue.t, name, eventTopics, opts) => {
|
|
83
|
+
// The policy grants SendMessage to any in-account EventTopic by naming
|
|
84
|
+
// convention, so it does not depend on the resolved topic resources and is
|
|
85
|
+
// created outside the apply. Only the SNS subscriptions need them.
|
|
86
|
+
queue->createQueuePolicy(name, opts)
|
|
85
87
|
let _ =
|
|
86
88
|
(eventTopics->toResources, queue.id)
|
|
87
89
|
->Pulumi.Output.all2
|
|
88
90
|
->Pulumi.Output.apply(((eventTopicResources, queueId)) => {
|
|
89
91
|
log.debug(
|
|
90
92
|
~comp="EventCollector",
|
|
91
|
-
`connectSqsQueue2SnsTopics ${queueId}: ${eventTopicResources
|
|
93
|
+
`connectSqsQueue2SnsTopics ${queueId}: ${eventTopicResources
|
|
94
|
+
->Array.length
|
|
95
|
+
->Int.toString} topic resource(s)`,
|
|
92
96
|
)
|
|
93
|
-
|
|
94
|
-
queue->createQueuePolicy(name, snsResources, opts)
|
|
95
|
-
queue->subscribeQueue2SnsTopic(name, snsResources, opts)
|
|
97
|
+
queue->subscribeQueue2SnsTopic(name, eventTopicResources->snsResources, opts)
|
|
96
98
|
})
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
// The EventCollector's transport wiring: one mapping per stream source plus one
|
|
102
|
+
// per buffer queue. `~component=name` keeps them attributed to the collector they
|
|
103
|
+
// feed, not to the mapping's own generated name.
|
|
104
|
+
let esmTags = (name, esmName) =>
|
|
105
|
+
AWS.Tags.make(
|
|
106
|
+
~name=esmName,
|
|
107
|
+
~kind=ReventlessCore.EventCollector.componentType,
|
|
108
|
+
~role=EventSourceMapping,
|
|
109
|
+
~component=name,
|
|
110
|
+
)
|
|
111
|
+
|
|
99
112
|
let connectLambda = (
|
|
100
113
|
lambda: Pulumi.Output.t<PulumiAws.Lambda.Function.t>,
|
|
101
114
|
name: string,
|
|
@@ -252,6 +265,10 @@ let connectLambda = (
|
|
|
252
265
|
~targetName=name,
|
|
253
266
|
~sourceName=dynamoDbStreamResource.name,
|
|
254
267
|
~source=dynamoDbStreamResource->ReventlessCore.AdapterDeploytime.resolvedToResource,
|
|
268
|
+
~tags=esmTags(
|
|
269
|
+
name,
|
|
270
|
+
dynamoDbStreamResource.name->ReventlessCore.Util.baseName ++ ("2" ++ name),
|
|
271
|
+
),
|
|
255
272
|
~opts,
|
|
256
273
|
)
|
|
257
274
|
)
|
|
@@ -259,7 +276,13 @@ let connectLambda = (
|
|
|
259
276
|
|
|
260
277
|
let subscriptionResources = queues->Array.mapWithIndex((queue, idx) => {
|
|
261
278
|
let esmName = queues->Array.length > 1 ? `${name}Sqs${Int.toString(idx)}` : name
|
|
262
|
-
Util_EventSourceMapping.subscribeSqs(
|
|
279
|
+
Util_EventSourceMapping.subscribeSqs(
|
|
280
|
+
~lambda,
|
|
281
|
+
~name=esmName,
|
|
282
|
+
~queue,
|
|
283
|
+
~tags=esmTags(name, esmName),
|
|
284
|
+
~opts,
|
|
285
|
+
)
|
|
263
286
|
})
|
|
264
287
|
|
|
265
288
|
subscriptionResources
|
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Aws from "@pulumi/aws";
|
|
4
4
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
|
-
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
5
|
import * as Pulumi from "@pulumi/pulumi";
|
|
7
6
|
import * as Lambda$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/Lambda/Lambda.res.mjs";
|
|
8
7
|
import * as AWS$ReventlessAws from "../AWS.res.mjs";
|
|
8
|
+
import * as Util$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util.res.mjs";
|
|
9
9
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
10
|
+
import * as AWS_Tags$ReventlessAws from "../AWS_Tags.res.mjs";
|
|
10
11
|
import * as Adapter$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Adapter.res.mjs";
|
|
11
12
|
import * as Util_SQS$ReventlessAws from "../../util/Util_SQS.res.mjs";
|
|
12
13
|
import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
|
|
13
14
|
import * as Adapter_Helpers$ReventlessAws from "../Adapter_Helpers.res.mjs";
|
|
15
|
+
import * as EventCollector$ReventlessCore from "@reventlessdev/reventless-core/src/components/EventCollector/EventCollector.res.mjs";
|
|
14
16
|
import * as AdapterDeploytime$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/AdapterDeploytime.res.mjs";
|
|
15
17
|
import * as Util_EventSourceMapping$ReventlessAws from "../../util/Util_EventSourceMapping.res.mjs";
|
|
16
18
|
|
|
@@ -20,14 +22,10 @@ function toResources(eventTopics) {
|
|
|
20
22
|
return Adapter$ReventlessCore.resourcesToResolvedOutput(Object.values(eventTopics).flatMap(outputs => outputs.resources));
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
function createQueuePolicy(queue, name,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
]).apply(param => {
|
|
28
|
-
let queueArn = param[0];
|
|
29
|
-
let accountId = Stdlib_Option.getOr(queueArn.split(":")[4], "");
|
|
30
|
-
let queuePolicyDocument = PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + "QueuePolicy", [{
|
|
25
|
+
function createQueuePolicy(queue, name, opts) {
|
|
26
|
+
let queuePolicyDocument = queue.arn.apply(queueArn => {
|
|
27
|
+
let accountId = Adapter_Helpers$ReventlessAws.accountIdOfQueueArn(queueArn);
|
|
28
|
+
return PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + "QueuePolicy", [{
|
|
31
29
|
Sid: "AllowReceiveSnsEvents",
|
|
32
30
|
Principal: {
|
|
33
31
|
Service: [AWS$ReventlessAws.SNS.principal]
|
|
@@ -46,11 +44,11 @@ function createQueuePolicy(queue, name, _resources, opts) {
|
|
|
46
44
|
]])
|
|
47
45
|
}
|
|
48
46
|
}]));
|
|
49
|
-
new (Aws.sqs.QueuePolicy)(name, {
|
|
50
|
-
policy: queuePolicyDocument,
|
|
51
|
-
queueUrl: param[1]
|
|
52
|
-
}, opts);
|
|
53
47
|
});
|
|
48
|
+
new (Aws.sqs.QueuePolicy)(name, {
|
|
49
|
+
policy: queuePolicyDocument,
|
|
50
|
+
queueUrl: queue.id
|
|
51
|
+
}, opts);
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
function subscribeQueue2SnsTopic(queue, name, resources, opts) {
|
|
@@ -62,18 +60,21 @@ function subscribeQueue2SnsTopic(queue, name, resources, opts) {
|
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
function connectSqsQueue2SnsTopics(queue, name, eventTopics, opts) {
|
|
63
|
+
createQueuePolicy(queue, name, opts);
|
|
65
64
|
Pulumi.all([
|
|
66
65
|
toResources(eventTopics),
|
|
67
66
|
queue.id
|
|
68
67
|
]).apply(param => {
|
|
69
68
|
let eventTopicResources = param[0];
|
|
70
69
|
log.debug("EventCollector", undefined, `connectSqsQueue2SnsTopics ` + param[1] + `: ` + eventTopicResources.length.toString() + ` topic resource(s)`);
|
|
71
|
-
|
|
72
|
-
createQueuePolicy(queue, name, snsResources, opts);
|
|
73
|
-
subscribeQueue2SnsTopic(queue, name, snsResources, opts);
|
|
70
|
+
subscribeQueue2SnsTopic(queue, name, Adapter_Helpers$ReventlessAws.snsResources(eventTopicResources), opts);
|
|
74
71
|
});
|
|
75
72
|
}
|
|
76
73
|
|
|
74
|
+
function esmTags(name, esmName) {
|
|
75
|
+
return AWS_Tags$ReventlessAws.make(esmName, EventCollector$ReventlessCore.componentType, "EventSourceMapping", undefined, name, undefined, undefined, undefined);
|
|
76
|
+
}
|
|
77
|
+
|
|
77
78
|
function connectLambda(lambda, name, lambdaRole, queues, eventTopics, resources, opts) {
|
|
78
79
|
Pulumi.all([
|
|
79
80
|
toResources(eventTopics),
|
|
@@ -147,11 +148,11 @@ function connectLambda(lambda, name, lambdaRole, queues, eventTopics, resources,
|
|
|
147
148
|
])),
|
|
148
149
|
role: lambdaRole.id
|
|
149
150
|
}, opts);
|
|
150
|
-
dynamoDbStreamResources.map(dynamoDbStreamResource => Util_EventSourceMapping$ReventlessAws.subscribe(undefined, lambda, name, dynamoDbStreamResource.name, AdapterDeploytime$ReventlessCore.resolvedToResource(dynamoDbStreamResource), opts));
|
|
151
|
+
dynamoDbStreamResources.map(dynamoDbStreamResource => Util_EventSourceMapping$ReventlessAws.subscribe(undefined, lambda, name, dynamoDbStreamResource.name, AdapterDeploytime$ReventlessCore.resolvedToResource(dynamoDbStreamResource), esmTags(name, Util$ReventlessCore.baseName(dynamoDbStreamResource.name) + ("2" + name)), opts));
|
|
151
152
|
});
|
|
152
153
|
return queues.map((queue, idx) => {
|
|
153
154
|
let esmName = queues.length > 1 ? name + `Sqs` + idx.toString() : name;
|
|
154
|
-
return Util_EventSourceMapping$ReventlessAws.subscribeSqs(lambda, esmName, queue, undefined, opts);
|
|
155
|
+
return Util_EventSourceMapping$ReventlessAws.subscribeSqs(lambda, esmName, queue, undefined, esmTags(name, esmName), opts);
|
|
155
156
|
});
|
|
156
157
|
}
|
|
157
158
|
|
|
@@ -161,6 +162,7 @@ export {
|
|
|
161
162
|
createQueuePolicy,
|
|
162
163
|
subscribeQueue2SnsTopic,
|
|
163
164
|
connectSqsQueue2SnsTopics,
|
|
165
|
+
esmTags,
|
|
164
166
|
connectLambda,
|
|
165
167
|
}
|
|
166
168
|
/* log Not a pure module */
|
|
@@ -108,44 +108,52 @@ let make = (
|
|
|
108
108
|
)
|
|
109
109
|
->Pulumi.Output.asInput,
|
|
110
110
|
sqsManagedSseEnabled: false->Pulumi.Input.make,
|
|
111
|
+
tags: AWS.Tags.make(
|
|
112
|
+
~name=name ++ "EventLogSubQueue",
|
|
113
|
+
~kind=ReventlessCore.EventTopic.componentType,
|
|
114
|
+
~role=EventLogSubscription,
|
|
115
|
+
~component=name,
|
|
116
|
+
),
|
|
111
117
|
},
|
|
112
118
|
~opts,
|
|
113
119
|
)
|
|
114
120
|
|
|
115
121
|
// SQS queue policy — allow SNS to send messages
|
|
116
122
|
let snsResource = eventTopicOutputs.resources->Array.getUnsafe(0)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
// The document is a *value* derived from resolved ARNs, so it is built in an
|
|
124
|
+
// apply. The QueuePolicy itself stays outside: passing `queue.id` as an Output
|
|
125
|
+
// is what registers the policy -> queue dependency, without which Pulumi has
|
|
126
|
+
// no ordering constraint and can delete the queue first on a replacement,
|
|
127
|
+
// leaving the policy's delete polling a queue that no longer exists.
|
|
128
|
+
let queuePolicyDocument =
|
|
129
|
+
(queue.arn, snsResource.urn)
|
|
130
|
+
->Pulumi.Output.all2
|
|
131
|
+
->Pulumi.Output.apply(((queueArn, snsUrn)) => {
|
|
121
132
|
open PolicyDocument
|
|
122
|
-
|
|
123
|
-
~
|
|
124
|
-
~
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
("aws:SourceArn", ConditionValues([snsUrn])),
|
|
138
|
-
]->Dict.fromArray,
|
|
139
|
-
},
|
|
140
|
-
},
|
|
141
|
-
],
|
|
142
|
-
)
|
|
143
|
-
->PolicyDocument.toJsonString
|
|
144
|
-
->Pulumi.Input.make,
|
|
145
|
-
},
|
|
146
|
-
~opts=Some(opts),
|
|
147
|
-
)
|
|
133
|
+
PolicyDocument.make(
|
|
134
|
+
~id=name ++ "EventLogSubQueuePolicy",
|
|
135
|
+
~statements=[
|
|
136
|
+
{
|
|
137
|
+
sid: "AllowSNSSend",
|
|
138
|
+
principal: Principals({service: PrincipalIds([AWS.SNS.principal])}),
|
|
139
|
+
effect: Allow,
|
|
140
|
+
actions: Actions(["sqs:SendMessage"]),
|
|
141
|
+
resources: Resource(queueArn),
|
|
142
|
+
conditions: {
|
|
143
|
+
arnEquals: [("aws:SourceArn", ConditionValues([snsUrn]))]->Dict.fromArray,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
)->PolicyDocument.toJsonString
|
|
148
148
|
})
|
|
149
|
+
let _queuePolicy = SQS.QueuePolicy.make(
|
|
150
|
+
~name=name ++ "EventLogSubQueuePolicy",
|
|
151
|
+
~args={
|
|
152
|
+
queueUrl: queue.id->Pulumi.Output.asInput,
|
|
153
|
+
policy: queuePolicyDocument->Pulumi.Output.asInput,
|
|
154
|
+
},
|
|
155
|
+
~opts=Some(opts),
|
|
156
|
+
)
|
|
149
157
|
|
|
150
158
|
// SNS → SQS subscription (raw delivery so body IS the event JSON)
|
|
151
159
|
let _subscription = Util_SQS.subscribeToSnsTopic(
|
|
@@ -264,6 +272,12 @@ let make = (
|
|
|
264
272
|
~lambda=lambdaOutput,
|
|
265
273
|
~name=name ++ "EventLogSubEventSourceMapping",
|
|
266
274
|
~queue,
|
|
275
|
+
~tags=AWS.Tags.make(
|
|
276
|
+
~name=name ++ "EventLogSubEventSourceMapping",
|
|
277
|
+
~kind=ReventlessCore.EventTopic.componentType,
|
|
278
|
+
~role=EventSourceMapping,
|
|
279
|
+
~component=name,
|
|
280
|
+
),
|
|
267
281
|
~opts,
|
|
268
282
|
)
|
|
269
283
|
}
|
|
@@ -81,34 +81,33 @@ export async function handler(event) {
|
|
|
81
81
|
function make(name, topicName, eventTopicOutputs, eventsApi, opts) {
|
|
82
82
|
let queue = new (Aws.sqs.Queue)(name + "EventLogSubQueue", {
|
|
83
83
|
redrivePolicy: Util_DeadLetterQueue$ReventlessAws.queue.arn.apply(dlqArn => SQS_Queue$PulumiAws.RedrivePolicy.make(dlqArn, 5)),
|
|
84
|
+
tags: AWS_Tags$ReventlessAws.make(name + "EventLogSubQueue", EventTopic$ReventlessCore.componentType, "EventLogSubscription", undefined, name, undefined, undefined, undefined),
|
|
84
85
|
visibilityTimeoutSeconds: 60,
|
|
85
86
|
sqsManagedSseEnabled: false
|
|
86
87
|
}, opts);
|
|
87
88
|
let snsResource = eventTopicOutputs.resources[0];
|
|
88
|
-
Pulumi.all([
|
|
89
|
+
let queuePolicyDocument = Pulumi.all([
|
|
89
90
|
queue.arn,
|
|
90
|
-
queue.id,
|
|
91
91
|
snsResource.urn
|
|
92
|
-
]).apply(param => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
});
|
|
92
|
+
]).apply(param => PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + "EventLogSubQueuePolicy", [{
|
|
93
|
+
Sid: "AllowSNSSend",
|
|
94
|
+
Principal: {
|
|
95
|
+
Service: [AWS$ReventlessAws.SNS.principal]
|
|
96
|
+
},
|
|
97
|
+
Effect: "Allow",
|
|
98
|
+
Action: ["sqs:SendMessage"],
|
|
99
|
+
Resource: param[0],
|
|
100
|
+
Condition: {
|
|
101
|
+
ArnEquals: Object.fromEntries([[
|
|
102
|
+
"aws:SourceArn",
|
|
103
|
+
[param[1]]
|
|
104
|
+
]])
|
|
105
|
+
}
|
|
106
|
+
}])));
|
|
107
|
+
new (Aws.sqs.QueuePolicy)(name + "EventLogSubQueuePolicy", {
|
|
108
|
+
policy: queuePolicyDocument,
|
|
109
|
+
queueUrl: queue.id
|
|
110
|
+
}, opts);
|
|
112
111
|
Util_SQS$ReventlessAws.subscribeToSnsTopic(queue, name + "EventLogSub", name, snsResource, opts);
|
|
113
112
|
let lambdaRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "EventLogSubRole", Pulumi.output(AWS$ReventlessAws.Lambda.principal), AWS_Tags$ReventlessAws.make(name + "EventLogSubRole", EventTopic$ReventlessCore.componentType, "Identity", undefined, name, undefined, undefined, undefined), opts);
|
|
114
113
|
Pulumi.all([
|
|
@@ -174,7 +173,7 @@ function make(name, topicName, eventTopicOutputs, eventsApi, opts) {
|
|
|
174
173
|
sourceCodeHash: sourceCodeHash
|
|
175
174
|
}, opts);
|
|
176
175
|
let lambdaOutput = Pulumi.output(lambda);
|
|
177
|
-
Util_EventSourceMapping$ReventlessAws.subscribeSqs(lambdaOutput, name + "EventLogSubEventSourceMapping", queue, undefined, opts);
|
|
176
|
+
Util_EventSourceMapping$ReventlessAws.subscribeSqs(lambdaOutput, name + "EventLogSubEventSourceMapping", queue, undefined, AWS_Tags$ReventlessAws.make(name + "EventLogSubEventSourceMapping", EventTopic$ReventlessCore.componentType, "EventSourceMapping", undefined, name, undefined, undefined, undefined), opts);
|
|
178
177
|
}
|
|
179
178
|
|
|
180
179
|
export {
|
|
@@ -51,6 +51,14 @@ let makeQueue = (
|
|
|
51
51
|
)
|
|
52
52
|
->Pulumi.Output.asInput,
|
|
53
53
|
sqsManagedSseEnabled: false->Pulumi.Input.make,
|
|
54
|
+
// Plugin substrate: one shared feed per plugin ("AllReadModelsFeed"), not a
|
|
55
|
+
// resource any single component owns.
|
|
56
|
+
tags: AWS.Tags.make(
|
|
57
|
+
~name,
|
|
58
|
+
~kind=ReventlessCore.ComponentType.Plugin,
|
|
59
|
+
~role=EventCollector,
|
|
60
|
+
~scope=Plugin,
|
|
61
|
+
),
|
|
54
62
|
},
|
|
55
63
|
~opts,
|
|
56
64
|
)
|
|
@@ -69,7 +77,18 @@ let connect = (
|
|
|
69
77
|
~lambdaRole: PulumiAws.IAM.Role.t,
|
|
70
78
|
~opts: Pulumi.CustomResourceOptions.t,
|
|
71
79
|
) => {
|
|
72
|
-
let _esm = Util_EventSourceMapping.subscribeSqs(
|
|
80
|
+
let _esm = Util_EventSourceMapping.subscribeSqs(
|
|
81
|
+
~lambda,
|
|
82
|
+
~name,
|
|
83
|
+
~queue,
|
|
84
|
+
~tags=AWS.Tags.make(
|
|
85
|
+
~name,
|
|
86
|
+
~kind=ReventlessCore.ComponentType.Plugin,
|
|
87
|
+
~role=EventSourceMapping,
|
|
88
|
+
~scope=Plugin,
|
|
89
|
+
),
|
|
90
|
+
~opts,
|
|
91
|
+
)
|
|
73
92
|
let _policy = queue.arn->Pulumi.Output.apply(queueArn => {
|
|
74
93
|
open PulumiAws.PolicyDocument
|
|
75
94
|
PulumiAws.IAM.RolePolicy.make(
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Aws from "@pulumi/aws";
|
|
4
4
|
import * as SQS_Queue$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/SQS/SQS_Queue.res.mjs";
|
|
5
|
+
import * as AWS_Tags$ReventlessAws from "../AWS_Tags.res.mjs";
|
|
5
6
|
import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
|
|
6
7
|
import * as Util_DeadLetterQueue$ReventlessAws from "../../util/Util_DeadLetterQueue.res.mjs";
|
|
7
8
|
import * as Util_EventSourceMapping$ReventlessAws from "../../util/Util_EventSourceMapping.res.mjs";
|
|
@@ -15,6 +16,7 @@ function getFeedQueues() {
|
|
|
15
16
|
function makeQueue(name, scope, includeClassic, includeDcb, opts) {
|
|
16
17
|
let queue = new (Aws.sqs.Queue)(name, {
|
|
17
18
|
redrivePolicy: Util_DeadLetterQueue$ReventlessAws.queue.arn.apply(dlqArn => SQS_Queue$PulumiAws.RedrivePolicy.make(dlqArn, 5)),
|
|
19
|
+
tags: AWS_Tags$ReventlessAws.make(name, "Plugin", "EventCollector", "Plugin", undefined, undefined, undefined, undefined),
|
|
18
20
|
visibilityTimeoutSeconds: 120,
|
|
19
21
|
sqsManagedSseEnabled: false
|
|
20
22
|
}, opts);
|
|
@@ -29,7 +31,7 @@ function makeQueue(name, scope, includeClassic, includeDcb, opts) {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
function connect(name, queue, lambda, lambdaRole, opts) {
|
|
32
|
-
Util_EventSourceMapping$ReventlessAws.subscribeSqs(lambda, name, queue, undefined, opts);
|
|
34
|
+
Util_EventSourceMapping$ReventlessAws.subscribeSqs(lambda, name, queue, undefined, AWS_Tags$ReventlessAws.make(name, "Plugin", "EventSourceMapping", "Plugin", undefined, undefined, undefined, undefined), opts);
|
|
33
35
|
queue.arn.apply(queueArn => new (Aws.iam.RolePolicy)(name + `Receive`, {
|
|
34
36
|
policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + `ReceivePolicy`, [{
|
|
35
37
|
Sid: "AllowReceiveFeedQueue",
|
|
@@ -430,7 +430,11 @@ const importFromAsset = (() => {
|
|
|
430
430
|
async function buildHandler() {
|
|
431
431
|
const config = parseHandlerConfig(process.env["HANDLER_CONFIG"]);
|
|
432
432
|
const pluginDefinition = loadPluginDefinition();
|
|
433
|
-
|
|
433
|
+
// Stack name, not the function name — see PluginExtensionPointEntryPoint for
|
|
434
|
+
// why the disconnect schedule's rule prefix must not carry a content hash.
|
|
435
|
+
// Both entry points instantiate the same admin Plugin EP module, so they have
|
|
436
|
+
// to derive this identically.
|
|
437
|
+
const environment = process.env["Environment"] || "unknown";
|
|
434
438
|
|
|
435
439
|
// runtimeOps carries only what the admin EP mapping's callHandler actually
|
|
436
440
|
// needs: sendMessageToChannel for ForwardCommand. Cross-plugin SNS
|
|
@@ -496,7 +500,7 @@ async function buildHandler() {
|
|
|
496
500
|
// Admin's Plugin EP: invoke the Make functor with runtime ops.
|
|
497
501
|
const epModule = mappingsMod.Make({
|
|
498
502
|
runtimeOps,
|
|
499
|
-
environment
|
|
503
|
+
environment,
|
|
500
504
|
// Connect-driven schema self-heal retired (Phase 4b) — the ApiSchemaPush
|
|
501
505
|
// SideEffect on ApiFragmentRegistry events is the single schema writer.
|
|
502
506
|
updateApiSchema: undefined,
|
|
@@ -37,10 +37,17 @@ function buildHandler() {
|
|
|
37
37
|
// Instantiate Plugin EP mapping. updateApiSchema and manageSubscriptions are
|
|
38
38
|
// admin-only hooks; this Lambda only handles incoming commands (Heartbeat,
|
|
39
39
|
// ForwardCommand) so they stay undefined here.
|
|
40
|
-
|
|
40
|
+
//
|
|
41
|
+
// `environment` prefixes the disconnect schedule's EventBridge rule name, so it
|
|
42
|
+
// must be stable across deploys and unique per stack. The stack name is both;
|
|
43
|
+
// AWS_LAMBDA_FUNCTION_NAME is neither — it carries a content hash, so replacing
|
|
44
|
+
// this Lambda would orphan every outstanding rule the previous generation
|
|
45
|
+
// created. EventCollectorEntryPoint instantiates the same EP module and must
|
|
46
|
+
// agree, or one Lambda creates rules the other cannot delete.
|
|
47
|
+
const environment = process.env["Environment"] || "unknown";
|
|
41
48
|
const pluginModule = pluginEPPluginMake({
|
|
42
49
|
runtimeOps,
|
|
43
|
-
environment
|
|
50
|
+
environment,
|
|
44
51
|
updateApiSchema: undefined,
|
|
45
52
|
manageSubscriptions: undefined,
|
|
46
53
|
});
|
|
@@ -27,15 +27,21 @@ let make: ReventlessCore.Runtime.environmentMaker<'event, context, 'result, part
|
|
|
27
27
|
let opts =
|
|
28
28
|
opts->Option.map(ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions)
|
|
29
29
|
|
|
30
|
+
// Legacy path (see above): no component kind reaches here because nothing calls
|
|
31
|
+
// it, so it is attributed to the Platform rather than to an invented domain kind.
|
|
32
|
+
let tags = AWS.Tags.make(~name, ~kind=ReventlessCore.ComponentType.Platform, ~role=Runtime, ~scope=Platform)
|
|
33
|
+
|
|
30
34
|
let lambdaRole = IAM.Role.makeWithDefaultPolicy(
|
|
31
35
|
~name,
|
|
32
36
|
~servicePrincipal=AWS.Lambda.principal->Pulumi.Output.make,
|
|
37
|
+
~tags=AWS.Tags.make(
|
|
38
|
+
~name,
|
|
39
|
+
~kind=ReventlessCore.ComponentType.Platform,
|
|
40
|
+
~role=Identity,
|
|
41
|
+
~scope=Platform,
|
|
42
|
+
),
|
|
33
43
|
~opts?,
|
|
34
44
|
)
|
|
35
|
-
|
|
36
|
-
// Legacy path (see above): no component kind reaches here because nothing calls
|
|
37
|
-
// it, so it is attributed to the Platform rather than to an invented domain kind.
|
|
38
|
-
let tags = AWS.Tags.make(~name, ~kind=ReventlessCore.ComponentType.Platform, ~role=Runtime, ~scope=Platform)
|
|
39
45
|
let lambda =
|
|
40
46
|
handler->Pulumi.Output.apply(handler =>
|
|
41
47
|
Lambda.CallbackFunction.make(
|
|
@@ -29,8 +29,8 @@ function make(name, handler, memorySizeOpt, timeoutOpt, opts) {
|
|
|
29
29
|
let memorySize = memorySizeOpt !== undefined ? memorySizeOpt : 1024;
|
|
30
30
|
let timeout = timeoutOpt !== undefined ? timeoutOpt : 30;
|
|
31
31
|
let opts$1 = Stdlib_Option.map(opts, Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions);
|
|
32
|
-
let lambdaRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name, Pulumi.output(AWS$ReventlessAws.Lambda.principal), undefined, opts$1);
|
|
33
32
|
let tags = AWS_Tags$ReventlessAws.make(name, "Platform", "Runtime", "Platform", undefined, undefined, undefined, undefined);
|
|
33
|
+
let lambdaRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name, Pulumi.output(AWS$ReventlessAws.Lambda.principal), AWS_Tags$ReventlessAws.make(name, "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
|
|
34
34
|
let lambda = handler.apply(handler => new (Aws.lambda.CallbackFunction)(name, Lambda$PulumiAws.CallbackFunction.Args.make(handler, lambdaRole, undefined, undefined, undefined, memorySize, timeout, undefined, undefined, undefined, tags, undefined), opts$1 !== undefined ? Primitive_option.valFromOption(opts$1) : undefined));
|
|
35
35
|
return {
|
|
36
36
|
parts: {
|
|
@@ -410,14 +410,21 @@ let finish = (
|
|
|
410
410
|
// isolates the bad record across those retries instead of redelivering
|
|
411
411
|
// the whole batch each time.
|
|
412
412
|
entries->Array.forEach(entry => {
|
|
413
|
+
let esmName = entry.topicName ++ "Stream2" ++ name ++ "StateTopic"
|
|
413
414
|
let _esm = EventSourceMapping.make(
|
|
414
|
-
~name=
|
|
415
|
+
~name=esmName,
|
|
415
416
|
~args={
|
|
416
417
|
EventSourceMapping.functionName: lambda.arn->Pulumi.Output.asInput,
|
|
417
418
|
eventSourceArn: entry.streamArn->Pulumi.Output.asInput,
|
|
418
419
|
startingPosition: LATEST,
|
|
419
420
|
maximumRetryAttempts: 3,
|
|
420
421
|
bisectBatchOnFunctionError: true,
|
|
422
|
+
tags: AWS.Tags.make(
|
|
423
|
+
~name=esmName,
|
|
424
|
+
~kind=ReventlessCore.QueryDb.componentType,
|
|
425
|
+
~role=EventSourceMapping,
|
|
426
|
+
~component=name,
|
|
427
|
+
),
|
|
421
428
|
},
|
|
422
429
|
~opts=Some(opts),
|
|
423
430
|
)
|
|
@@ -284,12 +284,14 @@ function finish(eventsApi, opts) {
|
|
|
284
284
|
sourceCodeHash: sourceCodeHash
|
|
285
285
|
}, opts);
|
|
286
286
|
entries.forEach(entry => {
|
|
287
|
-
|
|
287
|
+
let esmName = entry.topicName + "Stream2" + name + "StateTopic";
|
|
288
|
+
new (Aws.lambda.EventSourceMapping)(esmName, {
|
|
288
289
|
functionName: lambda.arn,
|
|
289
290
|
bisectBatchOnFunctionError: true,
|
|
290
291
|
eventSourceArn: entry.streamArn,
|
|
291
292
|
maximumRetryAttempts: 3,
|
|
292
|
-
startingPosition: "LATEST"
|
|
293
|
+
startingPosition: "LATEST",
|
|
294
|
+
tags: AWS_Tags$ReventlessAws.make(esmName, QueryDb$ReventlessCore.componentType, "EventSourceMapping", undefined, name, undefined, undefined, undefined)
|
|
293
295
|
}, opts);
|
|
294
296
|
});
|
|
295
297
|
registry[key] = [];
|
|
@@ -209,6 +209,12 @@ export const handler = async (event) => {
|
|
|
209
209
|
cpu: "1024"->Pulumi.Input.make,
|
|
210
210
|
requiresCompatibilities: ["FARGATE"],
|
|
211
211
|
networkMode: #awsvpc,
|
|
212
|
+
tags: AWS.Tags.make(
|
|
213
|
+
~name,
|
|
214
|
+
~kind=ReventlessCore.Cloner.componentType,
|
|
215
|
+
~role=DataTransfer,
|
|
216
|
+
~component=name,
|
|
217
|
+
),
|
|
212
218
|
},
|
|
213
219
|
~opts?,
|
|
214
220
|
)
|