@reventlessdev/reventless-aws 3.0.0-alpha.399 → 3.0.0-alpha.401
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 +24 -0
- package/package.json +8 -8
- package/scripts/DeployApp.res +16 -5
- package/scripts/DeployApp.res.mjs +10 -3
- package/scripts/ProvisionAccounts.res +2 -2
- package/scripts/ProvisionAccounts.res.mjs +1 -1
- package/scripts/ProvisionAdmin.res +1 -1
- package/scripts/ProvisionAdmin.res.mjs +1 -1
- package/src/adapter/Identity/IdentityProvider_Cognito_Backend.res +1 -1
- package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +6 -14
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +13 -123
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +8 -74
- package/src/adapter/Runtime/RuntimeEnvironment_Lambda.res +5 -5
- package/src/plugin/runtime/PluginRuntime_Builder.res +13 -49
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +2 -15
- package/tests/EventCollectorEntryPoint_OpsTest.res +27 -32
- package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +28 -46
- package/tests/ProvisionAccountsTest.res +1 -1
- package/tests/ProvisionAccountsTest.res.mjs +1 -1
- package/src/components/ForeignReadModel_Builder.res +0 -19
- package/src/components/ForeignReadModel_Builder.res.mjs +0 -54
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,30 @@
|
|
|
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.401 (2026-10-29)
|
|
7
|
+
|
|
8
|
+
* refactor!: drop `dcbSources`, the cross-plugin DCB subscription nobody declared ([cf3b66d](https://github.com/ReventlessDev/reventless-core/commit/cf3b66d8c1b5d87673197b64eaee174598bb8253))
|
|
9
|
+
* refactor!: a read model hears another plugin only through an Extension and a slice ([15c7cae](https://github.com/ReventlessDev/reventless-core/commit/15c7cae214d5aa04404779fcd87a8429aad1965c))
|
|
10
|
+
|
|
11
|
+
### BREAKING CHANGES
|
|
12
|
+
|
|
13
|
+
* `Reventless.Plugin.extensionDefinition` has no `dcbSources`
|
|
14
|
+
field, and `Platform_PluginExtensions.dcbSources` is gone from the GraphQL
|
|
15
|
+
schema. A query that selects it fails as a whole; drop the selection.
|
|
16
|
+
* `ReventlessInfra.Extension.T.make` and the core
|
|
17
|
+
`Extension_Builder.make` no longer take `~readModelNamesForSourceName` and
|
|
18
|
+
`~publishToReadModels`. Both arguments did nothing; drop them at the call site.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# 3.0.0-alpha.400 (2026-10-25)
|
|
23
|
+
|
|
24
|
+
**Note:** Version bump only for package @reventlessdev/reventless-aws
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
6
30
|
# 3.0.0-alpha.399 (2026-10-25)
|
|
7
31
|
|
|
8
32
|
**Note:** Version bump only for package @reventlessdev/reventless-aws
|
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.401",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -23,18 +23,18 @@
|
|
|
23
23
|
"uuid": "^13.0.0",
|
|
24
24
|
"yaml": "^2.8.3",
|
|
25
25
|
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.36",
|
|
26
|
-
"@reventlessdev/rescript-
|
|
26
|
+
"@reventlessdev/rescript-effect": "1.0.0-alpha.1",
|
|
27
27
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.13",
|
|
28
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.23",
|
|
28
29
|
"@reventlessdev/rescript-pulumi-aws": "3.0.0-alpha.34",
|
|
29
|
-
"@reventlessdev/rescript-effect": "1.0.0-alpha.1",
|
|
30
30
|
"@reventlessdev/rescript-pulumi-pulumi": "3.0.0-alpha.0",
|
|
31
|
-
"@reventlessdev/rescript-web": "1.0.0-alpha.8",
|
|
32
31
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.1",
|
|
33
|
-
"@reventlessdev/
|
|
34
|
-
"@reventlessdev/reventless-
|
|
32
|
+
"@reventlessdev/rescript-web": "1.0.0-alpha.8",
|
|
33
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.311",
|
|
34
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.208",
|
|
35
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.175",
|
|
35
36
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.42",
|
|
36
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
37
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.309"
|
|
37
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.179"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"rescript": "12.3.1",
|
package/scripts/DeployApp.res
CHANGED
|
@@ -61,7 +61,7 @@ Usage: deploy-app up|down [--manifest <path>] [--stack <name>]
|
|
|
61
61
|
Deploy an app to AWS, or remove it again.
|
|
62
62
|
|
|
63
63
|
deploy-app up Deploy the platform and every plugin, bake the component
|
|
64
|
-
manifest, create the accounts in .reventless/
|
|
64
|
+
manifest, create the accounts in .reventless/accounts.<stack>.yaml,
|
|
65
65
|
and print the web address and the sign-ins.
|
|
66
66
|
deploy-app down Remove every stack, and the Lambda layer and SSM parameter
|
|
67
67
|
\`up\` created.
|
|
@@ -495,12 +495,23 @@ let outputString = (outputs: dict<Automation.outputValue>, name: string): option
|
|
|
495
495
|
/** The stack's own accounts file beside the platform, started from the app's
|
|
496
496
|
template, or `None` when the app has no template — then there is nothing to
|
|
497
497
|
create. Per stack because the file records the ids one pool minted: sharing
|
|
498
|
-
`
|
|
499
|
-
|
|
498
|
+
`accounts.yaml` would overwrite another deployment's record of its accounts. A stack deployed
|
|
499
|
+
before the file was renamed keeps its `users.<stack>.yaml` until it is renamed. */
|
|
500
|
+
let accountsFileName = stack => `accounts.${stack}.yaml`
|
|
501
|
+
let oldAccountsFileName = stack => `users.${stack}.yaml`
|
|
500
502
|
|
|
501
503
|
let accountsFile = (~platformDir: string, ~stack: string): option<string> => {
|
|
502
|
-
let file =
|
|
503
|
-
|
|
504
|
+
let file = {
|
|
505
|
+
let current = NodePath.join([platformDir, ".reventless", accountsFileName(stack)])
|
|
506
|
+
let old = NodePath.join([platformDir, ".reventless", oldAccountsFileName(stack)])
|
|
507
|
+
if !NodeFs.existsSync(current) && NodeFs.existsSync(old) {
|
|
508
|
+
Manifest.noticeOldName(old)
|
|
509
|
+
old
|
|
510
|
+
} else {
|
|
511
|
+
current
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
let template = Manifest.templateIn(platformDir)
|
|
504
515
|
switch (NodeFs.existsSync(file), NodeFs.existsSync(template)) {
|
|
505
516
|
| (false, false) => None
|
|
506
517
|
| (true, _) => Some(file)
|
|
@@ -87,7 +87,7 @@ Usage: deploy-app up|down [--manifest <path>] [--stack <name>]
|
|
|
87
87
|
Deploy an app to AWS, or remove it again.
|
|
88
88
|
|
|
89
89
|
deploy-app up Deploy the platform and every plugin, bake the component
|
|
90
|
-
manifest, create the accounts in .reventless/
|
|
90
|
+
manifest, create the accounts in .reventless/accounts.<stack>.yaml,
|
|
91
91
|
and print the web address and the sign-ins.
|
|
92
92
|
deploy-app down Remove every stack, and the Lambda layer and SSM parameter
|
|
93
93
|
\`up\` created.
|
|
@@ -611,12 +611,18 @@ function outputString(outputs, name) {
|
|
|
611
611
|
}
|
|
612
612
|
|
|
613
613
|
function accountsFileName(stack) {
|
|
614
|
+
return `accounts.` + stack + `.yaml`;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function oldAccountsFileName(stack) {
|
|
614
618
|
return `users.` + stack + `.yaml`;
|
|
615
619
|
}
|
|
616
620
|
|
|
617
621
|
function accountsFile(platformDir, stack) {
|
|
618
|
-
let
|
|
619
|
-
let
|
|
622
|
+
let current = Nodepath.join(platformDir, ".reventless", accountsFileName(stack));
|
|
623
|
+
let old = Nodepath.join(platformDir, ".reventless", oldAccountsFileName(stack));
|
|
624
|
+
let file = !Nodefs.existsSync(current) && Nodefs.existsSync(old) ? (AccountsManifest$Reventless.noticeOldName(old), old) : current;
|
|
625
|
+
let template = AccountsManifest$Reventless.templateIn(platformDir);
|
|
620
626
|
let match = Nodefs.existsSync(file);
|
|
621
627
|
let match$1 = Nodefs.existsSync(template);
|
|
622
628
|
if (match) {
|
|
@@ -946,6 +952,7 @@ export {
|
|
|
946
952
|
deploy,
|
|
947
953
|
outputString,
|
|
948
954
|
accountsFileName,
|
|
955
|
+
oldAccountsFileName,
|
|
949
956
|
accountsFile,
|
|
950
957
|
signInLines,
|
|
951
958
|
up,
|
|
@@ -17,7 +17,7 @@ one alone is enough.
|
|
|
17
17
|
pnpm exec provision-accounts
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
It reads `.reventless/
|
|
20
|
+
It reads `.reventless/accounts.yaml`, and knows nothing about any particular
|
|
21
21
|
application's cast: the manifest belongs to whoever is deploying. A demo's four
|
|
22
22
|
roles and a company's twelve staff are the same run. Which pool it writes to is
|
|
23
23
|
[ProvisionProvider]'s question, and usually needs no argument either.
|
|
@@ -76,7 +76,7 @@ Turn a declared cast of accounts into working sign-ins.
|
|
|
76
76
|
created the pool or was handed one.
|
|
77
77
|
--stack <name> Read that output from this stack instead of the selected
|
|
78
78
|
one. The run always names the stack it used.
|
|
79
|
-
--file <path> The manifest. Defaults to .reventless/
|
|
79
|
+
--file <path> The manifest. Defaults to .reventless/accounts.yaml relative
|
|
80
80
|
to the working directory, which is where both platforms
|
|
81
81
|
keep it.
|
|
82
82
|
|
|
@@ -39,7 +39,7 @@ Turn a declared cast of accounts into working sign-ins.
|
|
|
39
39
|
created the pool or was handed one.
|
|
40
40
|
--stack <name> Read that output from this stack instead of the selected
|
|
41
41
|
one. The run always names the stack it used.
|
|
42
|
-
--file <path> The manifest. Defaults to .reventless/
|
|
42
|
+
--file <path> The manifest. Defaults to .reventless/accounts.yaml relative
|
|
43
43
|
to the working directory, which is where both platforms
|
|
44
44
|
keep it.
|
|
45
45
|
|
|
@@ -198,7 +198,7 @@ ${passwordNote}
|
|
|
198
198
|
This script bootstraps the first account only, which is the one that cannot be
|
|
199
199
|
made through a signed-in session because there is no signed-in session yet. The
|
|
200
200
|
rest of the cast is a list rather than a command: declare it in
|
|
201
|
-
.reventless/
|
|
201
|
+
.reventless/accounts.yaml and run \`pnpm exec provision-accounts\`.`
|
|
202
202
|
|
|
203
203
|
type request = {email: string, providerId?: string, stack?: string}
|
|
204
204
|
|
|
@@ -119,7 +119,7 @@ The first administrator is ready.
|
|
|
119
119
|
This script bootstraps the first account only, which is the one that cannot be
|
|
120
120
|
made through a signed-in session because there is no signed-in session yet. The
|
|
121
121
|
rest of the cast is a list rather than a command: declare it in
|
|
122
|
-
.reventless/
|
|
122
|
+
.reventless/accounts.yaml and run \`pnpm exec provision-accounts\`.`;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
function parseRequest(argv) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The identity provider a deployed platform provisions: principals in its
|
|
2
2
|
// Cognito pool, and each new one's domain id in the identity store the pool's
|
|
3
3
|
// pre-token trigger reads. The AWS counterpart of the local platform's
|
|
4
|
-
// `
|
|
4
|
+
// `Capability_IdentityProvider_AccountStore`, and reached the same way — on the
|
|
5
5
|
// capability record a slice's `translate` receives.
|
|
6
6
|
//
|
|
7
7
|
// Pulumi-free so it ships in the slice Lambda. The pool calls go through an
|
|
@@ -42,16 +42,12 @@
|
|
|
42
42
|
// "mappingsModule": string, // dynamic import for the user extension file (`module Mapping`)
|
|
43
43
|
// "delegateModule": string, // dynamic import for the Delegate spec (aggregate / slice)
|
|
44
44
|
// "extensionPointName": string, // service key for incomingExtensionEventHandlers (e.g. "Ordering.Orders")
|
|
45
|
-
// "aggregateNames": string[]
|
|
46
|
-
// "readModelNames": string[] // RMs this extension may enqueue events into (filter into readModelQueueUrls)
|
|
45
|
+
// "aggregateNames": string[] // outgoing service keys + filter into top-level publishToAggregates
|
|
47
46
|
// }
|
|
48
|
-
// ]
|
|
49
|
-
// (publishToAggregates
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
// "readModelNamesForSourceName": { // deploy-side inversion of RM.sourceNames; sourceServiceName → [rmName]
|
|
53
|
-
// "Ordering.Orders": ["ProductDemands"]
|
|
54
|
-
// }
|
|
47
|
+
// ]
|
|
48
|
+
// (publishToAggregates moved to the queueUrls.json asset — one entry per
|
|
49
|
+
// target, so it outgrew the 4KB environment. It maps a target name to the
|
|
50
|
+
// PTA_ env var holding its SQS URL.)
|
|
55
51
|
// }
|
|
56
52
|
//
|
|
57
53
|
// Why this shape and not separate entry points per Lambda: at deploy time both the
|
|
@@ -187,8 +183,6 @@ async function buildHandler() {
|
|
|
187
183
|
const ops = extensionOperationsMake(specMod)(extBuilder.ConnectPluginMappings)({
|
|
188
184
|
publishToAggregates,
|
|
189
185
|
publishToPluginExtensionPoint,
|
|
190
|
-
readModelNamesForSourceName: {},
|
|
191
|
-
publishToReadModels: {},
|
|
192
186
|
queryEngine,
|
|
193
187
|
});
|
|
194
188
|
connectExtensionHandler = {
|
|
@@ -229,12 +223,10 @@ async function buildHandler() {
|
|
|
229
223
|
mappings: [transformedMapping],
|
|
230
224
|
};
|
|
231
225
|
|
|
232
|
-
const dicts = Ops.extensionPublishDicts(
|
|
226
|
+
const dicts = Ops.extensionPublishDicts(queueUrlNames, ext);
|
|
233
227
|
const ops = extensionOperationsMake(epSpec)(mappingsModule)({
|
|
234
228
|
publishToAggregates: dicts.publishToAggregates,
|
|
235
229
|
publishToPluginExtensionPoint,
|
|
236
|
-
readModelNamesForSourceName: dicts.readModelNamesForSourceName,
|
|
237
|
-
publishToReadModels: dicts.publishToReadModels,
|
|
238
230
|
queryEngine,
|
|
239
231
|
});
|
|
240
232
|
|
|
@@ -61,15 +61,6 @@ external scanByTableName: (
|
|
|
61
61
|
let exnMessage = (exn: exn): string =>
|
|
62
62
|
exn->JsExn.fromException->Option.flatMap(JsExn.message)->Option.getOr("unknown error")
|
|
63
63
|
|
|
64
|
-
// ── Absent-field normalization ──────────────────────────────────────────────
|
|
65
|
-
// pluginDefinition's optionals omit the key, so a plain JSON.parse (not a sury
|
|
66
|
-
// parse) yields `undefined` and ReScript reads it as None unaided. Kept because
|
|
67
|
-
// the same records also arrive from stores and peers written when an absent
|
|
68
|
-
// field was `null`, which ReScript's option would misread as Some.
|
|
69
|
-
|
|
70
|
-
external optionAsNullable: option<'a> => Nullable.t<'a> = "%identity"
|
|
71
|
-
let jsOption = (v: option<'a>): option<'a> => v->optionAsNullable->Nullable.toOption
|
|
72
|
-
|
|
73
64
|
// ── HANDLER_CONFIG ──────────────────────────────────────────────────────────
|
|
74
65
|
// Written by PluginRuntime_Builder.forPluginEventCollector; the field-by-field
|
|
75
66
|
// schema is documented in EventCollectorEntryPoint.mjs. Explicitly decoded (no
|
|
@@ -94,7 +85,6 @@ type extensionConfig = {
|
|
|
94
85
|
delegateModule: string,
|
|
95
86
|
extensionPointName: string,
|
|
96
87
|
aggregateNames: array<string>,
|
|
97
|
-
readModelNames: array<string>,
|
|
98
88
|
}
|
|
99
89
|
type handlerConfig = {
|
|
100
90
|
queueUrl: string,
|
|
@@ -108,17 +98,13 @@ type handlerConfig = {
|
|
|
108
98
|
extensionPoints: array<extensionPointConfig>,
|
|
109
99
|
connectExtension?: connectExtensionConfig,
|
|
110
100
|
extensions: array<extensionConfig>,
|
|
111
|
-
readModelNamesForSourceName: dict<array<string>>,
|
|
112
101
|
}
|
|
113
102
|
|
|
114
|
-
// The target→env-var
|
|
103
|
+
// The target→env-var map that used to ride HANDLER_CONFIG, now read from
|
|
115
104
|
// queueUrls.json (PluginRuntime_Builder). Values are env var NAMES, not URLs —
|
|
116
105
|
// the URLs stay in the environment because the builder cannot resolve those
|
|
117
106
|
// Outputs in bulk.
|
|
118
|
-
type queueUrlNames = {
|
|
119
|
-
publishToAggregates: dict<string>,
|
|
120
|
-
readModelQueueUrls: dict<string>,
|
|
121
|
-
}
|
|
107
|
+
type queueUrlNames = {publishToAggregates: dict<string>}
|
|
122
108
|
|
|
123
109
|
let strOf = (obj: dict<JSON.t>, key: string): option<string> =>
|
|
124
110
|
obj->Dict.get(key)->Option.flatMap(JSON.Decode.string)
|
|
@@ -196,7 +182,6 @@ let parseHandlerConfig = (rawJson: string): handlerConfig => {
|
|
|
196
182
|
delegateModule: ext->strOf("delegateModule")->Option.getOr(""),
|
|
197
183
|
extensionPointName,
|
|
198
184
|
aggregateNames: ext->strArrOf("aggregateNames"),
|
|
199
|
-
readModelNames: ext->strArrOf("readModelNames"),
|
|
200
185
|
}
|
|
201
186
|
})
|
|
202
187
|
)
|
|
@@ -211,17 +196,6 @@ let parseHandlerConfig = (rawJson: string): handlerConfig => {
|
|
|
211
196
|
mappingsModule: ce->strOf("mappingsModule")->Option.getOr(""),
|
|
212
197
|
extensionPointName: ce->strOf("extensionPointName")->Option.getOr(""),
|
|
213
198
|
})
|
|
214
|
-
let readModelNamesForSourceName =
|
|
215
|
-
obj
|
|
216
|
-
->Dict.get("readModelNamesForSourceName")
|
|
217
|
-
->Option.flatMap(JSON.Decode.object)
|
|
218
|
-
->Option.getOr(Dict.make())
|
|
219
|
-
->Dict.toArray
|
|
220
|
-
->Array.map(((k, v)) => (
|
|
221
|
-
k,
|
|
222
|
-
v->JSON.Decode.array->Option.getOr([])->Array.filterMap(JSON.Decode.string),
|
|
223
|
-
))
|
|
224
|
-
->Dict.fromArray
|
|
225
199
|
{
|
|
226
200
|
queueUrl: obj->strOf("queueUrl")->Option.getOr(""),
|
|
227
201
|
pluginExtensionPointCmdTopicUrl: obj
|
|
@@ -236,7 +210,6 @@ let parseHandlerConfig = (rawJson: string): handlerConfig => {
|
|
|
236
210
|
extensionPoints,
|
|
237
211
|
?connectExtension,
|
|
238
212
|
extensions,
|
|
239
|
-
readModelNamesForSourceName,
|
|
240
213
|
}
|
|
241
214
|
}
|
|
242
215
|
|
|
@@ -253,7 +226,6 @@ type pluginProjection = {
|
|
|
253
226
|
eventCollector: string,
|
|
254
227
|
extensions: array<Reventless.Plugin.extensionDefinition>,
|
|
255
228
|
extensionPoints: array<Reventless.Plugin.extensionPointDefinition>,
|
|
256
|
-
dcbEventLog?: Reventless.Plugin.dcbEventLogDefinition,
|
|
257
229
|
}
|
|
258
230
|
|
|
259
231
|
let projectPluginRow = (row: JSON.t): option<pluginProjection> =>
|
|
@@ -262,17 +234,6 @@ let projectPluginRow = (row: JSON.t): option<pluginProjection> =>
|
|
|
262
234
|
| Some(obj) =>
|
|
263
235
|
switch (obj->strOf("id"), obj->strOf("status")) {
|
|
264
236
|
| (Some(id), Some(status)) =>
|
|
265
|
-
let dcbEventLog =
|
|
266
|
-
obj
|
|
267
|
-
->Dict.get("dcbEventLog")
|
|
268
|
-
->Option.flatMap(JSON.Decode.object)
|
|
269
|
-
->Option.flatMap(d =>
|
|
270
|
-
switch (d->strOf("name"), d->strOf("eventTopicArn")) {
|
|
271
|
-
| (Some(name), Some(eventTopicArn)) =>
|
|
272
|
-
Some(({name, eventTopicArn}: Reventless.Plugin.dcbEventLogDefinition))
|
|
273
|
-
| _ => None
|
|
274
|
-
}
|
|
275
|
-
)
|
|
276
237
|
let extensions =
|
|
277
238
|
obj
|
|
278
239
|
->Dict.get("extensions")
|
|
@@ -288,7 +249,6 @@ let projectPluginRow = (row: JSON.t): option<pluginProjection> =>
|
|
|
288
249
|
(extensionPointName): Reventless.Plugin.extensionDefinition => {
|
|
289
250
|
name: eo->strOf("name")->Option.getOr(""),
|
|
290
251
|
extensionPointName,
|
|
291
|
-
dcbSources: eo->strArrOf("dcbSources"),
|
|
292
252
|
},
|
|
293
253
|
)
|
|
294
254
|
)
|
|
@@ -323,30 +283,26 @@ let projectPluginRow = (row: JSON.t): option<pluginProjection> =>
|
|
|
323
283
|
eventCollector: obj->strOf("eventCollector")->Option.getOr(""),
|
|
324
284
|
extensions,
|
|
325
285
|
extensionPoints,
|
|
326
|
-
?dcbEventLog,
|
|
327
286
|
})
|
|
328
287
|
| _ => None
|
|
329
288
|
}
|
|
330
289
|
}
|
|
331
290
|
|
|
332
291
|
// A pluginDefinition viewed as a projection (the manage logic reads only the
|
|
333
|
-
// shared subset; `status` is never read for the acting plugin).
|
|
334
|
-
// fields stay normalized: a definition arriving over the wire, rather than from
|
|
335
|
-
// loadPluginDefinition's decode, may still carry null for an absent field.
|
|
292
|
+
// shared subset; `status` is never read for the acting plugin).
|
|
336
293
|
let projectionOfDefinition = (d: Reventless.Plugin.pluginDefinition): pluginProjection => {
|
|
337
294
|
id: d.id,
|
|
338
295
|
status: "",
|
|
339
296
|
eventCollector: d.eventCollector,
|
|
340
297
|
extensions: d.extensions,
|
|
341
298
|
extensionPoints: d.extensionPoints,
|
|
342
|
-
dcbEventLog: ?(d.dcbEventLog->jsOption),
|
|
343
299
|
}
|
|
344
300
|
|
|
345
301
|
// ── Cross-plugin SNS subscription manager ───────────────────────────────────
|
|
346
302
|
// When DoConnectPlugin / DoDisconnectPlugin fires on the admin EP mapping, scan
|
|
347
303
|
// the Plugin RM for currently-Connected peers and wire (or tear down) SNS
|
|
348
304
|
// subscriptions between this plugin's EventCollector queue and peer plugins'
|
|
349
|
-
// EP
|
|
305
|
+
// EP event topics — in both directions. subscribeQueueToTopic is idempotent
|
|
350
306
|
// (lists existing subscriptions first); unsubscribeQueueFromTopic is
|
|
351
307
|
// best-effort. Failures are logged but do not throw — admin's RM projection
|
|
352
308
|
// still proceeds and cold-start reconciliation re-attempts missed work.
|
|
@@ -453,50 +409,6 @@ let makeManageSubscriptions = (tableName: string): option<
|
|
|
453
409
|
}
|
|
454
410
|
}
|
|
455
411
|
}
|
|
456
|
-
// DCB sources this plugin's extensions consume: owning peer's DCB
|
|
457
|
-
// EventTopic → this EC queue.
|
|
458
|
-
for i in 0 to pluginDef.extensions->Array.length - 1 {
|
|
459
|
-
let ext = pluginDef.extensions->Array.getUnsafe(i)
|
|
460
|
-
for j in 0 to ext.dcbSources->Array.length - 1 {
|
|
461
|
-
let dcbSourceName = ext.dcbSources->Array.getUnsafe(j)
|
|
462
|
-
switch peers->Array.find(p =>
|
|
463
|
-
switch p.dcbEventLog {
|
|
464
|
-
| Some(l) => l.name == dcbSourceName
|
|
465
|
-
| None => false
|
|
466
|
-
}
|
|
467
|
-
) {
|
|
468
|
-
| Some(peer) =>
|
|
469
|
-
switch peer.dcbEventLog {
|
|
470
|
-
| Some(l) =>
|
|
471
|
-
await op(
|
|
472
|
-
pluginDef.eventCollector,
|
|
473
|
-
l.eventTopicArn,
|
|
474
|
-
`dcb ${dcbSourceName} -> ${pluginDef.id}`,
|
|
475
|
-
)
|
|
476
|
-
| None => ()
|
|
477
|
-
}
|
|
478
|
-
| None => ()
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
// If THIS plugin has a DCB EventLog: its topic → each consuming
|
|
483
|
-
// peer's EC queue.
|
|
484
|
-
switch pluginDef.dcbEventLog {
|
|
485
|
-
| Some(myDcb) =>
|
|
486
|
-
for j in 0 to peers->Array.length - 1 {
|
|
487
|
-
let peer = peers->Array.getUnsafe(j)
|
|
488
|
-
let consuming =
|
|
489
|
-
peer.extensions->Array.some(e => e.dcbSources->Array.includes(myDcb.name))
|
|
490
|
-
if consuming {
|
|
491
|
-
await op(
|
|
492
|
-
peer.eventCollector,
|
|
493
|
-
myDcb.eventTopicArn,
|
|
494
|
-
`dcb ${myDcb.name} -> ${peer.id}`,
|
|
495
|
-
)
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
| None => ()
|
|
499
|
-
}
|
|
500
412
|
}
|
|
501
413
|
},
|
|
502
414
|
)
|
|
@@ -630,20 +542,12 @@ let buildPublishToAggregates = (map: dict<string>): dict<
|
|
|
630
542
|
))
|
|
631
543
|
->Dict.fromArray
|
|
632
544
|
|
|
633
|
-
// Per-extension publish
|
|
634
|
-
//
|
|
635
|
-
//
|
|
636
|
-
type extensionDicts = {
|
|
637
|
-
publishToAggregates: dict<ReventlessCore.CommandTopic.publishJsons>,
|
|
638
|
-
publishToReadModels: dict<ReventlessCore.CommandTopic.publishJsons>,
|
|
639
|
-
readModelNamesForSourceName: dict<array<string>>,
|
|
640
|
-
}
|
|
545
|
+
// Per-extension publish dict, filtered to the extension's declared aggregate
|
|
546
|
+
// names; entries with no env-var mapping or an empty URL are skipped (same as
|
|
547
|
+
// the former JS `continue`s).
|
|
548
|
+
type extensionDicts = {publishToAggregates: dict<ReventlessCore.CommandTopic.publishJsons>}
|
|
641
549
|
|
|
642
|
-
let extensionPublishDicts = (
|
|
643
|
-
config: handlerConfig,
|
|
644
|
-
names: queueUrlNames,
|
|
645
|
-
ext: extensionConfig,
|
|
646
|
-
): extensionDicts => {
|
|
550
|
+
let extensionPublishDicts = (names: queueUrlNames, ext: extensionConfig): extensionDicts => {
|
|
647
551
|
let resolve = (names: array<string>, envVarNames: dict<string>) =>
|
|
648
552
|
names
|
|
649
553
|
->Array.filterMap(name =>
|
|
@@ -654,18 +558,7 @@ let extensionPublishDicts = (
|
|
|
654
558
|
->Option.map(queueUrl => (name, makePublishJsons(queueUrl)))
|
|
655
559
|
)
|
|
656
560
|
->Dict.fromArray
|
|
657
|
-
{
|
|
658
|
-
publishToAggregates: resolve(ext.aggregateNames, names.publishToAggregates),
|
|
659
|
-
publishToReadModels: resolve(ext.readModelNames, names.readModelQueueUrls),
|
|
660
|
-
// Scoped to this extension's EP — Extension_Operations uses this dict to
|
|
661
|
-
// decide which RMs receive each incoming event.
|
|
662
|
-
readModelNamesForSourceName: Dict.fromArray([
|
|
663
|
-
(
|
|
664
|
-
ext.extensionPointName,
|
|
665
|
-
config.readModelNamesForSourceName->Dict.get(ext.extensionPointName)->Option.getOr([]),
|
|
666
|
-
),
|
|
667
|
-
]),
|
|
668
|
-
}
|
|
561
|
+
{publishToAggregates: resolve(ext.aggregateNames, names.publishToAggregates)}
|
|
669
562
|
}
|
|
670
563
|
|
|
671
564
|
let warnSkippedExtension = (ext: extensionConfig) =>
|
|
@@ -702,8 +595,8 @@ let loadPluginDefinition = (): Reventless.Plugin.pluginDefinition =>
|
|
|
702
595
|
)
|
|
703
596
|
}
|
|
704
597
|
|
|
705
|
-
// The target→env-var
|
|
706
|
-
// nowhere writes empty
|
|
598
|
+
// The target→env-var map, read once at cold start. A plugin that publishes
|
|
599
|
+
// nowhere writes an empty map rather than omitting the file, so a read failure is
|
|
707
600
|
// a genuinely broken archive and not an empty-map case worth guessing at.
|
|
708
601
|
let loadQueueUrlNames = (): queueUrlNames =>
|
|
709
602
|
try {
|
|
@@ -713,10 +606,7 @@ let loadQueueUrlNames = (): queueUrlNames =>
|
|
|
713
606
|
->JSON.parseOrThrow
|
|
714
607
|
->JSON.Decode.object
|
|
715
608
|
->Option.getOr(Dict.make())
|
|
716
|
-
{
|
|
717
|
-
publishToAggregates: obj->strDictOf("publishToAggregates"),
|
|
718
|
-
readModelQueueUrls: obj->strDictOf("readModelQueueUrls"),
|
|
719
|
-
}
|
|
609
|
+
{publishToAggregates: obj->strDictOf("publishToAggregates")}
|
|
720
610
|
} catch {
|
|
721
611
|
| exn =>
|
|
722
612
|
JsError.throwWithMessage("Failed to load queueUrls.json from asset zip: " ++ exnMessage(exn))
|
|
@@ -7,7 +7,6 @@ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
|
7
7
|
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
8
8
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
9
9
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
10
|
-
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
11
10
|
import * as Plugin$Reventless from "@reventlessdev/reventless-spec/src/components/Plugin.res.mjs";
|
|
12
11
|
import * as SNS_Helpers$AwsSdk from "@reventlessdev/rescript-aws-sdk/src/SNS_Helpers.res.mjs";
|
|
13
12
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
@@ -64,14 +63,6 @@ function exnMessage(exn) {
|
|
|
64
63
|
return Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_JsExn.fromException(exn), Stdlib_JsExn.message), "unknown error");
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
function jsOption(v) {
|
|
68
|
-
if (v == null) {
|
|
69
|
-
return;
|
|
70
|
-
} else {
|
|
71
|
-
return Primitive_option.some(v);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
66
|
function strOf(obj, key) {
|
|
76
67
|
return Stdlib_Option.flatMap(obj[key], Stdlib_JSON.Decode.string);
|
|
77
68
|
}
|
|
@@ -135,8 +126,7 @@ function parseHandlerConfig(rawJson) {
|
|
|
135
126
|
mappingsModule: Stdlib_Option.getOr(strOf(ext, "mappingsModule"), ""),
|
|
136
127
|
delegateModule: Stdlib_Option.getOr(strOf(ext, "delegateModule"), ""),
|
|
137
128
|
extensionPointName: extensionPointName,
|
|
138
|
-
aggregateNames: strArrOf(ext, "aggregateNames")
|
|
139
|
-
readModelNames: strArrOf(ext, "readModelNames")
|
|
129
|
+
aggregateNames: strArrOf(ext, "aggregateNames")
|
|
140
130
|
};
|
|
141
131
|
})) : Stdlib_JsError.throwWithMessage("HANDLER_CONFIG.extensions must be an array");
|
|
142
132
|
let connectExtension = Stdlib_Option.map(Stdlib_Option.flatMap(obj["connectExtension"], Stdlib_JSON.Decode.object), ce => ({
|
|
@@ -144,10 +134,6 @@ function parseHandlerConfig(rawJson) {
|
|
|
144
134
|
mappingsModule: Stdlib_Option.getOr(strOf(ce, "mappingsModule"), ""),
|
|
145
135
|
extensionPointName: Stdlib_Option.getOr(strOf(ce, "extensionPointName"), "")
|
|
146
136
|
}));
|
|
147
|
-
let readModelNamesForSourceName = Object.fromEntries(Object.entries(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["readModelNamesForSourceName"], Stdlib_JSON.Decode.object), {})).map(param => [
|
|
148
|
-
param[0],
|
|
149
|
-
Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_JSON.Decode.array(param[1]), []), Stdlib_JSON.Decode.string)
|
|
150
|
-
]));
|
|
151
137
|
return {
|
|
152
138
|
queueUrl: Stdlib_Option.getOr(strOf(obj, "queueUrl"), ""),
|
|
153
139
|
pluginExtensionPointCmdTopicUrl: Stdlib_Option.getOr(strOf(obj, "pluginExtensionPointCmdTopicUrl"), ""),
|
|
@@ -159,8 +145,7 @@ function parseHandlerConfig(rawJson) {
|
|
|
159
145
|
schedulerQueueName: Stdlib_Option.getOr(strOf(obj, "schedulerQueueName"), ""),
|
|
160
146
|
extensionPoints: extensionPoints,
|
|
161
147
|
connectExtension: connectExtension,
|
|
162
|
-
extensions: extensions
|
|
163
|
-
readModelNamesForSourceName: readModelNamesForSourceName
|
|
148
|
+
extensions: extensions
|
|
164
149
|
};
|
|
165
150
|
}
|
|
166
151
|
|
|
@@ -177,20 +162,9 @@ function projectPluginRow(row) {
|
|
|
177
162
|
if (match$1 === undefined) {
|
|
178
163
|
return;
|
|
179
164
|
}
|
|
180
|
-
let dcbEventLog = Stdlib_Option.flatMap(Stdlib_Option.flatMap(obj["dcbEventLog"], Stdlib_JSON.Decode.object), d => {
|
|
181
|
-
let match = strOf(d, "name");
|
|
182
|
-
let match$1 = strOf(d, "eventTopicArn");
|
|
183
|
-
if (match !== undefined && match$1 !== undefined) {
|
|
184
|
-
return {
|
|
185
|
-
name: match,
|
|
186
|
-
eventTopicArn: match$1
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
165
|
let extensions = Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["extensions"], Stdlib_JSON.Decode.array), []), e => Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(e), eo => Stdlib_Option.map(strOf(eo, "extensionPointName"), extensionPointName => ({
|
|
191
166
|
name: Stdlib_Option.getOr(strOf(eo, "name"), ""),
|
|
192
|
-
extensionPointName: extensionPointName
|
|
193
|
-
dcbSources: strArrOf(eo, "dcbSources")
|
|
167
|
+
extensionPointName: extensionPointName
|
|
194
168
|
}))));
|
|
195
169
|
let extensionPoints = Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.flatMap(obj["extensionPoints"], Stdlib_JSON.Decode.array), []), ep => Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(ep), epo => {
|
|
196
170
|
let match = strOf(epo, "name");
|
|
@@ -208,8 +182,7 @@ function projectPluginRow(row) {
|
|
|
208
182
|
status: match$1,
|
|
209
183
|
eventCollector: Stdlib_Option.getOr(strOf(obj, "eventCollector"), ""),
|
|
210
184
|
extensions: extensions,
|
|
211
|
-
extensionPoints: extensionPoints
|
|
212
|
-
dcbEventLog: dcbEventLog
|
|
185
|
+
extensionPoints: extensionPoints
|
|
213
186
|
};
|
|
214
187
|
}
|
|
215
188
|
|
|
@@ -219,8 +192,7 @@ function projectionOfDefinition(d) {
|
|
|
219
192
|
status: "",
|
|
220
193
|
eventCollector: d.eventCollector,
|
|
221
194
|
extensions: d.extensions,
|
|
222
|
-
extensionPoints: d.extensionPoints
|
|
223
|
-
dcbEventLog: Primitive_option.fromNullable(d.dcbEventLog)
|
|
195
|
+
extensionPoints: d.extensionPoints
|
|
224
196
|
};
|
|
225
197
|
}
|
|
226
198
|
|
|
@@ -334,37 +306,6 @@ function makeManageSubscriptions(tableName) {
|
|
|
334
306
|
}
|
|
335
307
|
}
|
|
336
308
|
}
|
|
337
|
-
for (let i$2 = 0, i_finish$2 = pluginDef.extensions.length; i$2 < i_finish$2; ++i$2) {
|
|
338
|
-
let ext$1 = pluginDef.extensions[i$2];
|
|
339
|
-
for (let j$2 = 0, j_finish$2 = ext$1.dcbSources.length; j$2 < j_finish$2; ++j$2) {
|
|
340
|
-
let dcbSourceName = ext$1.dcbSources[j$2];
|
|
341
|
-
let peer$2 = peers.find(p => {
|
|
342
|
-
let l = p.dcbEventLog;
|
|
343
|
-
if (l !== undefined) {
|
|
344
|
-
return l.name === dcbSourceName;
|
|
345
|
-
} else {
|
|
346
|
-
return false;
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
if (peer$2 !== undefined) {
|
|
350
|
-
let l = peer$2.dcbEventLog;
|
|
351
|
-
if (l !== undefined) {
|
|
352
|
-
await op(pluginDef.eventCollector, l.eventTopicArn, `dcb ` + dcbSourceName + ` -> ` + pluginDef.id);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
let myDcb = pluginDef.dcbEventLog;
|
|
358
|
-
if (myDcb === undefined) {
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
for (let j$3 = 0, j_finish$3 = peers.length; j$3 < j_finish$3; ++j$3) {
|
|
362
|
-
let peer$3 = peers[j$3];
|
|
363
|
-
let consuming = peer$3.extensions.some(e => e.dcbSources.includes(myDcb.name));
|
|
364
|
-
if (consuming) {
|
|
365
|
-
await op(peer$3.eventCollector, myDcb.eventTopicArn, `dcb ` + myDcb.name + ` -> ` + peer$3.id);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
309
|
};
|
|
369
310
|
}
|
|
370
311
|
}
|
|
@@ -494,18 +435,13 @@ function buildPublishToAggregates(map) {
|
|
|
494
435
|
]));
|
|
495
436
|
}
|
|
496
437
|
|
|
497
|
-
function extensionPublishDicts(
|
|
438
|
+
function extensionPublishDicts(names, ext) {
|
|
498
439
|
let resolve = (names, envVarNames) => Object.fromEntries(Stdlib_Array.filterMap(names, name => Stdlib_Option.map(Stdlib_Option.filter(Stdlib_Option.flatMap(envVarNames[name], envVarName => process.env[envVarName]), queueUrl => queueUrl !== ""), queueUrl => [
|
|
499
440
|
name,
|
|
500
441
|
makePublishJsons(queueUrl)
|
|
501
442
|
])));
|
|
502
443
|
return {
|
|
503
|
-
publishToAggregates: resolve(ext.aggregateNames, names.publishToAggregates)
|
|
504
|
-
publishToReadModels: resolve(ext.readModelNames, names.readModelQueueUrls),
|
|
505
|
-
readModelNamesForSourceName: Object.fromEntries([[
|
|
506
|
-
ext.extensionPointName,
|
|
507
|
-
Stdlib_Option.getOr(config.readModelNamesForSourceName[ext.extensionPointName], [])
|
|
508
|
-
]])
|
|
444
|
+
publishToAggregates: resolve(ext.aggregateNames, names.publishToAggregates)
|
|
509
445
|
};
|
|
510
446
|
}
|
|
511
447
|
|
|
@@ -532,8 +468,7 @@ function loadQueueUrlNames() {
|
|
|
532
468
|
try {
|
|
533
469
|
let obj = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(Nodefs.readFileSync(assetPath("queueUrls.json"), "utf8"))), {});
|
|
534
470
|
return {
|
|
535
|
-
publishToAggregates: strDictOf(obj, "publishToAggregates")
|
|
536
|
-
readModelQueueUrls: strDictOf(obj, "readModelQueueUrls")
|
|
471
|
+
publishToAggregates: strDictOf(obj, "publishToAggregates")
|
|
537
472
|
};
|
|
538
473
|
} catch (raw_exn) {
|
|
539
474
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
@@ -632,7 +567,6 @@ export {
|
|
|
632
567
|
logError,
|
|
633
568
|
scanByTableName,
|
|
634
569
|
exnMessage,
|
|
635
|
-
jsOption,
|
|
636
570
|
strOf,
|
|
637
571
|
strArrOf,
|
|
638
572
|
strDictOf,
|
|
@@ -15,11 +15,11 @@ let additionalIamPolicies: array<iamPolicy> = []
|
|
|
15
15
|
|
|
16
16
|
// CallbackFunction path — the last serialized-closure Lambda builder on AWS.
|
|
17
17
|
//
|
|
18
|
-
// REACHABLE, despite most deployments going through makeFromCodeAsset.
|
|
18
|
+
// REACHABLE, despite most deployments going through makeFromCodeAsset. Two
|
|
19
19
|
// component builders instantiate ReventlessCore.EventCollectorRuntime_Builder_
|
|
20
|
-
// PerEventCollector.Make over this module —
|
|
21
|
-
//
|
|
22
|
-
//
|
|
20
|
+
// PerEventCollector.Make over this module — SideEffectHandler_PerSideEffectHandler
|
|
21
|
+
// and Task_Builder_PerBucket — and that functor's forEventCollector calls `make`
|
|
22
|
+
// below. The live route is a Task whose
|
|
23
23
|
// spec returns `Task.sideEffects`: Task_Builder then constructs a
|
|
24
24
|
// SideEffectHandler, which provisions its collector Lambda here.
|
|
25
25
|
//
|
|
@@ -29,7 +29,7 @@ let additionalIamPolicies: array<iamPolicy> = []
|
|
|
29
29
|
// where an ordering-aws deploy came up four Lambdas short). Where it does
|
|
30
30
|
// serialize, it then resolves @aws-sdk against whatever the layer and the runtime
|
|
31
31
|
// each supply, carrying the cold-start SDK-skew failure compiled entry points
|
|
32
|
-
// were introduced to avoid. Converting these
|
|
32
|
+
// were introduced to avoid. Converting these two builders to a compiled entry
|
|
33
33
|
// point is the fix; until then a side-effect-bearing Task is the exposed surface.
|
|
34
34
|
//
|
|
35
35
|
// `make` cannot simply be dropped: Runtime.Environment requires environmentMaker
|
|
@@ -346,8 +346,6 @@ module Make = (
|
|
|
346
346
|
extensions: [],
|
|
347
347
|
pluginExtensionPointCmdTopicUrl: Pulumi.Output.make(""),
|
|
348
348
|
publishToAggregates: Dict.make(),
|
|
349
|
-
readModelQueueUrls: Dict.make(),
|
|
350
|
-
readModelNamesForSourceName: Dict.make(),
|
|
351
349
|
}
|
|
352
350
|
}
|
|
353
351
|
|
|
@@ -427,18 +425,6 @@ module Make = (
|
|
|
427
425
|
envVars->Dict.set(envVarName, queueUrlOutput->Pulumi.Output.asInput)
|
|
428
426
|
})
|
|
429
427
|
|
|
430
|
-
// Same indirection for ReadModel EventCollector SQS URLs — only populated
|
|
431
|
-
// when user extensions enqueue directly into RMs (Extension_Operations
|
|
432
|
-
// publishToReadModels path). Empty for the admin Lambda and for the
|
|
433
|
-
// current Orders_Extension fixture (its RM is fed via the slice EventLog →
|
|
434
|
-
// RM EventColl subscription chain instead).
|
|
435
|
-
context.readModelQueueUrls
|
|
436
|
-
->Dict.toArray
|
|
437
|
-
->Array.forEach(((_rmName, queueUrlOutput)) => {
|
|
438
|
-
let envVarName = `PRM_${_rmName}_QUEUE_URL`
|
|
439
|
-
envVars->Dict.set(envVarName, queueUrlOutput->Pulumi.Output.asInput)
|
|
440
|
-
})
|
|
441
|
-
|
|
442
428
|
// Outgoing arrays first — resolve EP eventTopicArns, then bundle everything
|
|
443
429
|
// into one Output.apply that builds the JSON.
|
|
444
430
|
let epEventTopicArnsOutput =
|
|
@@ -523,8 +509,7 @@ module Make = (
|
|
|
523
509
|
// User-declared extensions — one entry per merged extension group.
|
|
524
510
|
// Each entry carries enough metadata for the bundled handler to
|
|
525
511
|
// dynamic-import the spec / mapping modules and filter the top-level
|
|
526
|
-
// publishToAggregates
|
|
527
|
-
// this extension actually uses.
|
|
512
|
+
// publishToAggregates map down to the subset this extension actually uses.
|
|
528
513
|
// Sort by extension name so the serialized JSON is stable across deploys.
|
|
529
514
|
let extensionsArr =
|
|
530
515
|
context.extensions
|
|
@@ -540,43 +525,26 @@ module Make = (
|
|
|
540
525
|
"aggregateNames",
|
|
541
526
|
ext.aggregateNames->Array.map(JSON.Encode.string)->JSON.Encode.array,
|
|
542
527
|
)
|
|
543
|
-
entryDict->Dict.set(
|
|
544
|
-
"readModelNames",
|
|
545
|
-
ext.readModelNames->Array.map(JSON.Encode.string)->JSON.Encode.array,
|
|
546
|
-
)
|
|
547
528
|
JSON.Encode.object(entryDict)
|
|
548
529
|
})
|
|
549
530
|
dict->Dict.set("extensions", JSON.Encode.array(extensionsArr))
|
|
550
531
|
|
|
551
|
-
// publishToAggregates
|
|
552
|
-
//
|
|
553
|
-
//
|
|
554
|
-
//
|
|
555
|
-
//
|
|
556
|
-
//
|
|
557
|
-
//
|
|
558
|
-
|
|
559
|
-
let rmNamesForSourceDict = Dict.make()
|
|
560
|
-
context.readModelNamesForSourceName
|
|
561
|
-
->Dict.toArray
|
|
562
|
-
->Array.forEach(((sourceName, rmNames)) =>
|
|
563
|
-
rmNamesForSourceDict->Dict.set(
|
|
564
|
-
sourceName,
|
|
565
|
-
rmNames->Array.map(JSON.Encode.string)->JSON.Encode.array,
|
|
566
|
-
)
|
|
567
|
-
)
|
|
568
|
-
dict->Dict.set("readModelNamesForSourceName", JSON.Encode.object(rmNamesForSourceDict))
|
|
532
|
+
// publishToAggregates lives in queueUrls.json (see the asset bundle below).
|
|
533
|
+
// It is one entry per target, so it grows with the plugin, and together with
|
|
534
|
+
// the PTA_ vars it names it carried the catalog EventCollector past Lambda's
|
|
535
|
+
// 4KB environment ceiling. Only the map moves: it is built from
|
|
536
|
+
// Dict.keysToArray, so it holds no Pulumi Output and can be serialized here.
|
|
537
|
+
// The URLs themselves cannot follow — see the note on the aggregate walk
|
|
538
|
+
// further down.
|
|
569
539
|
|
|
570
540
|
let json = JSON.Encode.object(dict)->JSON.stringify
|
|
571
|
-
// The PTA_
|
|
572
|
-
//
|
|
573
|
-
//
|
|
574
|
-
let queueUrlKeys =
|
|
541
|
+
// The PTA_ vars are the rest of this Lambda's environment and the half
|
|
542
|
+
// that grows with the plugin. Their names are known here; their values
|
|
543
|
+
// are still Outputs, so the budget check estimates those.
|
|
544
|
+
let queueUrlKeys =
|
|
575
545
|
context.publishToAggregates
|
|
576
546
|
->Dict.keysToArray
|
|
577
|
-
->Array.map(n => `PTA_${n}_QUEUE_URL`)
|
|
578
|
-
context.readModelQueueUrls->Dict.keysToArray->Array.map(n => `PRM_${n}_QUEUE_URL`),
|
|
579
|
-
)
|
|
547
|
+
->Array.map(n => `PTA_${n}_QUEUE_URL`)
|
|
580
548
|
Util_LambdaEnvBudget.check(
|
|
581
549
|
~lambdaName=name,
|
|
582
550
|
~handlerConfigJson=json,
|
|
@@ -660,10 +628,6 @@ module Make = (
|
|
|
660
628
|
"publishToAggregates",
|
|
661
629
|
nameMap(context.publishToAggregates->Dict.keysToArray, "PTA"),
|
|
662
630
|
)
|
|
663
|
-
queueUrlsDict->Dict.set(
|
|
664
|
-
"readModelQueueUrls",
|
|
665
|
-
nameMap(context.readModelQueueUrls->Dict.keysToArray, "PRM"),
|
|
666
|
-
)
|
|
667
631
|
extraStringAssets->Dict.set(
|
|
668
632
|
"queueUrls.json",
|
|
669
633
|
JSON.Encode.object(queueUrlsDict)->JSON.stringify,
|
|
@@ -268,9 +268,7 @@ function Make(EventCollectorChannel) {
|
|
|
268
268
|
}],
|
|
269
269
|
extensions: [],
|
|
270
270
|
pluginExtensionPointCmdTopicUrl: Pulumi.output(""),
|
|
271
|
-
publishToAggregates: {}
|
|
272
|
-
readModelQueueUrls: {},
|
|
273
|
-
readModelNamesForSourceName: {}
|
|
271
|
+
publishToAggregates: {}
|
|
274
272
|
};
|
|
275
273
|
};
|
|
276
274
|
let forPluginEventCollector = (param, eventTopics, resources, memorySizeOpt, timeoutOpt, eventCollector) => {
|
|
@@ -303,10 +301,6 @@ function Make(EventCollectorChannel) {
|
|
|
303
301
|
let envVarName = `PTA_` + param[0] + `_QUEUE_URL`;
|
|
304
302
|
envVars[envVarName] = param[1];
|
|
305
303
|
});
|
|
306
|
-
Object.entries(context.readModelQueueUrls).forEach(param => {
|
|
307
|
-
let envVarName = `PRM_` + param[0] + `_QUEUE_URL`;
|
|
308
|
-
envVars[envVarName] = param[1];
|
|
309
|
-
});
|
|
310
304
|
let epEventTopicArnsOutput = Pulumi.all(context.extensionPoints.map(ep => ep.eventTopicArn));
|
|
311
305
|
let handlerConfigJson = Pulumi.all([
|
|
312
306
|
queue.id,
|
|
@@ -371,17 +365,11 @@ function Make(EventCollectorChannel) {
|
|
|
371
365
|
entryDict["delegateModule"] = ext.delegateModule;
|
|
372
366
|
entryDict["extensionPointName"] = ext.extensionPointName;
|
|
373
367
|
entryDict["aggregateNames"] = ext.aggregateNames.map(prim => prim);
|
|
374
|
-
entryDict["readModelNames"] = ext.readModelNames.map(prim => prim);
|
|
375
368
|
return entryDict;
|
|
376
369
|
});
|
|
377
370
|
dict["extensions"] = extensionsArr;
|
|
378
|
-
let rmNamesForSourceDict = {};
|
|
379
|
-
Object.entries(context.readModelNamesForSourceName).forEach(param => {
|
|
380
|
-
rmNamesForSourceDict[param[0]] = param[1].map(prim => prim);
|
|
381
|
-
});
|
|
382
|
-
dict["readModelNamesForSourceName"] = rmNamesForSourceDict;
|
|
383
371
|
let json = JSON.stringify(dict);
|
|
384
|
-
let queueUrlKeys = Object.keys(context.publishToAggregates).map(n => `PTA_` + n + `_QUEUE_URL`)
|
|
372
|
+
let queueUrlKeys = Object.keys(context.publishToAggregates).map(n => `PTA_` + n + `_QUEUE_URL`);
|
|
385
373
|
Util_LambdaEnvBudget$ReventlessAws.check(name, json, queueUrlKeys);
|
|
386
374
|
return json;
|
|
387
375
|
});
|
|
@@ -428,7 +416,6 @@ function Make(EventCollectorChannel) {
|
|
|
428
416
|
};
|
|
429
417
|
let queueUrlsDict = {};
|
|
430
418
|
queueUrlsDict["publishToAggregates"] = nameMap(Object.keys(context.publishToAggregates), "PTA");
|
|
431
|
-
queueUrlsDict["readModelQueueUrls"] = nameMap(Object.keys(context.readModelQueueUrls), "PRM");
|
|
432
419
|
extraStringAssets["queueUrls.json"] = JSON.stringify(queueUrlsDict);
|
|
433
420
|
extraStringAssets["uiFragments.json"] = param[1];
|
|
434
421
|
return Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/EventCollectorEntryPoint.mjs", packageDirs, extraStringAssets, undefined);
|
|
@@ -32,9 +32,10 @@ describe("EventCollectorEntryPoint_Ops.projectPluginRow", () => {
|
|
|
32
32
|
expect(p.eventCollector)->toBe("") // defaulted when absent
|
|
33
33
|
expect(p.extensions->Array.length)->toBe(0)
|
|
34
34
|
expect(p.extensionPoints->Array.length)->toBe(0)
|
|
35
|
-
expect(p.dcbEventLog->Option.isNone)->toBe(true)
|
|
36
35
|
})
|
|
37
36
|
|
|
37
|
+
// A row written before `dcbSources` left the extension definition still
|
|
38
|
+
// carries the key; the projection reads past it.
|
|
38
39
|
testSync("decodes extensions, dropping entries without extensionPointName", () => {
|
|
39
40
|
let row = obj([
|
|
40
41
|
("id", str("Ordering@2")),
|
|
@@ -46,7 +47,7 @@ describe("EventCollectorEntryPoint_Ops.projectPluginRow", () => {
|
|
|
46
47
|
obj([
|
|
47
48
|
("name", str("OrdersExt")),
|
|
48
49
|
("extensionPointName", str("Catalog.Products")),
|
|
49
|
-
("dcbSources", JSON.Encode.array([
|
|
50
|
+
("dcbSources", JSON.Encode.array([])),
|
|
50
51
|
]),
|
|
51
52
|
obj([("name", str("Malformed"))]), // no extensionPointName -> dropped
|
|
52
53
|
]),
|
|
@@ -58,11 +59,9 @@ describe("EventCollectorEntryPoint_Ops.projectPluginRow", () => {
|
|
|
58
59
|
let e = p.extensions->Array.getUnsafe(0)
|
|
59
60
|
expect(e.name)->toBe("OrdersExt")
|
|
60
61
|
expect(e.extensionPointName)->toBe("Catalog.Products")
|
|
61
|
-
// Non-string dcbSources elements are filtered out.
|
|
62
|
-
expect(e.dcbSources)->toEqual(["prod"])
|
|
63
62
|
})
|
|
64
63
|
|
|
65
|
-
testSync("decodes extensionPoints
|
|
64
|
+
testSync("decodes extensionPoints; drops EPs without name/eventTopic", () => {
|
|
66
65
|
let row = obj([
|
|
67
66
|
("id", str("Catalog@1")),
|
|
68
67
|
("status", str("Connected")),
|
|
@@ -73,6 +72,7 @@ describe("EventCollectorEntryPoint_Ops.projectPluginRow", () => {
|
|
|
73
72
|
obj([("name", str("NoTopic"))]), // no eventTopic -> dropped
|
|
74
73
|
]),
|
|
75
74
|
),
|
|
75
|
+
// Still on older rows; the subscription manager no longer reads it.
|
|
76
76
|
("dcbEventLog", obj([("name", str("catalog-dcb")), ("eventTopicArn", str("dcb-arn"))])),
|
|
77
77
|
])
|
|
78
78
|
let p = project(row)->Option.getOrThrow
|
|
@@ -81,18 +81,6 @@ describe("EventCollectorEntryPoint_Ops.projectPluginRow", () => {
|
|
|
81
81
|
expect(ep.name)->toBe("Catalog.Products")
|
|
82
82
|
expect(ep.commandTopic)->toBe("") // defaulted
|
|
83
83
|
expect(ep.eventTopic)->toBe("t-arn")
|
|
84
|
-
let dcb = p.dcbEventLog->Option.getOrThrow
|
|
85
|
-
expect(dcb.name)->toBe("catalog-dcb")
|
|
86
|
-
expect(dcb.eventTopicArn)->toBe("dcb-arn")
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
testSync("drops a dcbEventLog missing eventTopicArn", () => {
|
|
90
|
-
let row = obj([
|
|
91
|
-
("id", str("Catalog@1")),
|
|
92
|
-
("status", str("Connected")),
|
|
93
|
-
("dcbEventLog", obj([("name", str("catalog-dcb"))])),
|
|
94
|
-
])
|
|
95
|
-
expect((project(row)->Option.getOrThrow).dcbEventLog->Option.isNone)->toBe(true)
|
|
96
84
|
})
|
|
97
85
|
})
|
|
98
86
|
|
|
@@ -146,7 +134,7 @@ describe("EventCollectorEntryPoint_Ops.parseHandlerConfig", () => {
|
|
|
146
134
|
let c = EventCollectorEntryPoint_Ops.parseHandlerConfig(minimalConfig([]))
|
|
147
135
|
expect(c.queueUrl)->toBe("q-url")
|
|
148
136
|
expect(c.connectExtension->Option.isNone)->toBe(true) // absent → None
|
|
149
|
-
expect(c.
|
|
137
|
+
expect(c.extensions->Array.length)->toBe(0)
|
|
150
138
|
})
|
|
151
139
|
|
|
152
140
|
testSync("connectExtension: JSON null (admin) maps to None; a record decodes", () => {
|
|
@@ -177,7 +165,7 @@ describe("EventCollectorEntryPoint_Ops.parseHandlerConfig", () => {
|
|
|
177
165
|
(
|
|
178
166
|
"extensions",
|
|
179
167
|
JSON.Encode.array([
|
|
180
|
-
// no name, no module specifiers, no aggregate
|
|
168
|
+
// no name, no module specifiers, no aggregate list
|
|
181
169
|
obj([("extensionPointName", str("Ordering.Orders"))]),
|
|
182
170
|
]),
|
|
183
171
|
),
|
|
@@ -187,10 +175,9 @@ describe("EventCollectorEntryPoint_Ops.parseHandlerConfig", () => {
|
|
|
187
175
|
expect(ext.name)->toBe("Ordering.Orders") // name falls back to the EP name
|
|
188
176
|
expect(ext.specModule)->toBe("")
|
|
189
177
|
expect(ext.aggregateNames)->toEqual([])
|
|
190
|
-
expect(ext.readModelNames)->toEqual([])
|
|
191
178
|
})
|
|
192
179
|
|
|
193
|
-
testSync("decodes extensionPoints entries
|
|
180
|
+
testSync("decodes extensionPoints entries", () => {
|
|
194
181
|
let c = EventCollectorEntryPoint_Ops.parseHandlerConfig(
|
|
195
182
|
minimalConfig([
|
|
196
183
|
(
|
|
@@ -204,30 +191,38 @@ describe("EventCollectorEntryPoint_Ops.parseHandlerConfig", () => {
|
|
|
204
191
|
]),
|
|
205
192
|
]),
|
|
206
193
|
),
|
|
207
|
-
(
|
|
208
|
-
"readModelNamesForSourceName",
|
|
209
|
-
obj([("Ordering.Orders", JSON.Encode.array([str("ProductDemands")]))]),
|
|
210
|
-
),
|
|
211
194
|
]),
|
|
212
195
|
)
|
|
213
196
|
let ep = c.extensionPoints->Array.getUnsafe(0)
|
|
214
197
|
expect(ep.eventTopicArn)->toBe("arn:ep")
|
|
215
198
|
expect(ep.aggregateNames)->toEqual(["Plugin"])
|
|
216
|
-
expect(c.readModelNamesForSourceName->Dict.get("Ordering.Orders"))->toEqual(
|
|
217
|
-
Some(["ProductDemands"]),
|
|
218
|
-
)
|
|
219
199
|
})
|
|
220
200
|
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
|
|
201
|
+
// Keys HANDLER_CONFIG no longer carries — the target→env-var maps that left for
|
|
202
|
+
// queueUrls.json, and the read-model forwarding index with the per-extension
|
|
203
|
+
// `readModelNames` — must parse rather than trip the required-field check: that
|
|
204
|
+
// is what a Lambda running an older archive sends.
|
|
205
|
+
testSync("ignores keys an older HANDLER_CONFIG still carries", () => {
|
|
225
206
|
let c = EventCollectorEntryPoint_Ops.parseHandlerConfig(
|
|
226
207
|
minimalConfig([
|
|
227
208
|
("publishToAggregates", obj([("Plugin", str("PTA_Plugin_QUEUE_URL"))])),
|
|
228
209
|
("readModelQueueUrls", obj([("ProductDemands", str("PRM_ProductDemands_QUEUE_URL"))])),
|
|
210
|
+
(
|
|
211
|
+
"readModelNamesForSourceName",
|
|
212
|
+
obj([("Ordering.Orders", JSON.Encode.array([str("ProductDemands")]))]),
|
|
213
|
+
),
|
|
214
|
+
(
|
|
215
|
+
"extensions",
|
|
216
|
+
JSON.Encode.array([
|
|
217
|
+
obj([
|
|
218
|
+
("extensionPointName", str("Ordering.Orders")),
|
|
219
|
+
("readModelNames", JSON.Encode.array([str("ProductDemands")])),
|
|
220
|
+
]),
|
|
221
|
+
]),
|
|
222
|
+
),
|
|
229
223
|
]),
|
|
230
224
|
)
|
|
231
225
|
expect(c.queueUrl)->toBe("q-url")
|
|
226
|
+
expect((c.extensions->Array.getUnsafe(0)).extensionPointName)->toBe("Ordering.Orders")
|
|
232
227
|
})
|
|
233
228
|
})
|
|
@@ -43,7 +43,6 @@ globalThis.describe("EventCollectorEntryPoint_Ops.projectPluginRow", () => {
|
|
|
43
43
|
globalThis.expect(p.eventCollector).toBe("");
|
|
44
44
|
globalThis.expect(p.extensions.length).toBe(0);
|
|
45
45
|
globalThis.expect(p.extensionPoints.length).toBe(0);
|
|
46
|
-
globalThis.expect(Stdlib_Option.isNone(p.dcbEventLog)).toBe(true);
|
|
47
46
|
});
|
|
48
47
|
globalThis.test("decodes extensions, dropping entries without extensionPointName", () => {
|
|
49
48
|
let row = Object.fromEntries([
|
|
@@ -73,10 +72,7 @@ globalThis.describe("EventCollectorEntryPoint_Ops.projectPluginRow", () => {
|
|
|
73
72
|
],
|
|
74
73
|
[
|
|
75
74
|
"dcbSources",
|
|
76
|
-
[
|
|
77
|
-
"prod",
|
|
78
|
-
7
|
|
79
|
-
]
|
|
75
|
+
[]
|
|
80
76
|
]
|
|
81
77
|
]),
|
|
82
78
|
Object.fromEntries([[
|
|
@@ -92,9 +88,8 @@ globalThis.describe("EventCollectorEntryPoint_Ops.projectPluginRow", () => {
|
|
|
92
88
|
let e = p.extensions[0];
|
|
93
89
|
globalThis.expect(e.name).toBe("OrdersExt");
|
|
94
90
|
globalThis.expect(e.extensionPointName).toBe("Catalog.Products");
|
|
95
|
-
globalThis.expect(e.dcbSources).toEqual(["prod"]);
|
|
96
91
|
});
|
|
97
|
-
globalThis.test("decodes extensionPoints
|
|
92
|
+
globalThis.test("decodes extensionPoints; drops EPs without name/eventTopic", () => {
|
|
98
93
|
let row = Object.fromEntries([
|
|
99
94
|
[
|
|
100
95
|
"id",
|
|
@@ -143,29 +138,6 @@ globalThis.describe("EventCollectorEntryPoint_Ops.projectPluginRow", () => {
|
|
|
143
138
|
globalThis.expect(ep.name).toBe("Catalog.Products");
|
|
144
139
|
globalThis.expect(ep.commandTopic).toBe("");
|
|
145
140
|
globalThis.expect(ep.eventTopic).toBe("t-arn");
|
|
146
|
-
let dcb = Stdlib_Option.getOrThrow(p.dcbEventLog, undefined);
|
|
147
|
-
globalThis.expect(dcb.name).toBe("catalog-dcb");
|
|
148
|
-
globalThis.expect(dcb.eventTopicArn).toBe("dcb-arn");
|
|
149
|
-
});
|
|
150
|
-
globalThis.test("drops a dcbEventLog missing eventTopicArn", () => {
|
|
151
|
-
let row = Object.fromEntries([
|
|
152
|
-
[
|
|
153
|
-
"id",
|
|
154
|
-
"Catalog@1"
|
|
155
|
-
],
|
|
156
|
-
[
|
|
157
|
-
"status",
|
|
158
|
-
"Connected"
|
|
159
|
-
],
|
|
160
|
-
[
|
|
161
|
-
"dcbEventLog",
|
|
162
|
-
Object.fromEntries([[
|
|
163
|
-
"name",
|
|
164
|
-
"catalog-dcb"
|
|
165
|
-
]])
|
|
166
|
-
]
|
|
167
|
-
]);
|
|
168
|
-
globalThis.expect(Stdlib_Option.isNone(Stdlib_Option.getOrThrow(EventCollectorEntryPoint_Ops$ReventlessAws.projectPluginRow(row), undefined).dcbEventLog)).toBe(true);
|
|
169
141
|
});
|
|
170
142
|
});
|
|
171
143
|
|
|
@@ -234,7 +206,7 @@ globalThis.describe("EventCollectorEntryPoint_Ops.parseHandlerConfig", () => {
|
|
|
234
206
|
let c = EventCollectorEntryPoint_Ops$ReventlessAws.parseHandlerConfig(minimalConfig([]));
|
|
235
207
|
globalThis.expect(c.queueUrl).toBe("q-url");
|
|
236
208
|
globalThis.expect(Stdlib_Option.isNone(c.connectExtension)).toBe(true);
|
|
237
|
-
globalThis.expect(
|
|
209
|
+
globalThis.expect(c.extensions.length).toBe(0);
|
|
238
210
|
});
|
|
239
211
|
globalThis.test("connectExtension: JSON null (admin) maps to None; a record decodes", () => {
|
|
240
212
|
let asNull = EventCollectorEntryPoint_Ops$ReventlessAws.parseHandlerConfig(minimalConfig([[
|
|
@@ -274,11 +246,9 @@ globalThis.describe("EventCollectorEntryPoint_Ops.parseHandlerConfig", () => {
|
|
|
274
246
|
globalThis.expect(ext.name).toBe("Ordering.Orders");
|
|
275
247
|
globalThis.expect(ext.specModule).toBe("");
|
|
276
248
|
globalThis.expect(ext.aggregateNames).toEqual([]);
|
|
277
|
-
globalThis.expect(ext.readModelNames).toEqual([]);
|
|
278
249
|
});
|
|
279
|
-
globalThis.test("decodes extensionPoints entries
|
|
280
|
-
let c = EventCollectorEntryPoint_Ops$ReventlessAws.parseHandlerConfig(minimalConfig([
|
|
281
|
-
[
|
|
250
|
+
globalThis.test("decodes extensionPoints entries", () => {
|
|
251
|
+
let c = EventCollectorEntryPoint_Ops$ReventlessAws.parseHandlerConfig(minimalConfig([[
|
|
282
252
|
"extensionPoints",
|
|
283
253
|
[Object.fromEntries([
|
|
284
254
|
[
|
|
@@ -298,21 +268,12 @@ globalThis.describe("EventCollectorEntryPoint_Ops.parseHandlerConfig", () => {
|
|
|
298
268
|
["Plugin"]
|
|
299
269
|
]
|
|
300
270
|
])]
|
|
301
|
-
]
|
|
302
|
-
[
|
|
303
|
-
"readModelNamesForSourceName",
|
|
304
|
-
Object.fromEntries([[
|
|
305
|
-
"Ordering.Orders",
|
|
306
|
-
["ProductDemands"]
|
|
307
|
-
]])
|
|
308
|
-
]
|
|
309
|
-
]));
|
|
271
|
+
]]));
|
|
310
272
|
let ep = c.extensionPoints[0];
|
|
311
273
|
globalThis.expect(ep.eventTopicArn).toBe("arn:ep");
|
|
312
274
|
globalThis.expect(ep.aggregateNames).toEqual(["Plugin"]);
|
|
313
|
-
globalThis.expect(c.readModelNamesForSourceName["Ordering.Orders"]).toEqual(["ProductDemands"]);
|
|
314
275
|
});
|
|
315
|
-
globalThis.test("ignores
|
|
276
|
+
globalThis.test("ignores keys an older HANDLER_CONFIG still carries", () => {
|
|
316
277
|
let c = EventCollectorEntryPoint_Ops$ReventlessAws.parseHandlerConfig(minimalConfig([
|
|
317
278
|
[
|
|
318
279
|
"publishToAggregates",
|
|
@@ -327,9 +288,30 @@ globalThis.describe("EventCollectorEntryPoint_Ops.parseHandlerConfig", () => {
|
|
|
327
288
|
"ProductDemands",
|
|
328
289
|
"PRM_ProductDemands_QUEUE_URL"
|
|
329
290
|
]])
|
|
291
|
+
],
|
|
292
|
+
[
|
|
293
|
+
"readModelNamesForSourceName",
|
|
294
|
+
Object.fromEntries([[
|
|
295
|
+
"Ordering.Orders",
|
|
296
|
+
["ProductDemands"]
|
|
297
|
+
]])
|
|
298
|
+
],
|
|
299
|
+
[
|
|
300
|
+
"extensions",
|
|
301
|
+
[Object.fromEntries([
|
|
302
|
+
[
|
|
303
|
+
"extensionPointName",
|
|
304
|
+
"Ordering.Orders"
|
|
305
|
+
],
|
|
306
|
+
[
|
|
307
|
+
"readModelNames",
|
|
308
|
+
["ProductDemands"]
|
|
309
|
+
]
|
|
310
|
+
])]
|
|
330
311
|
]
|
|
331
312
|
]));
|
|
332
313
|
globalThis.expect(c.queueUrl).toBe("q-url");
|
|
314
|
+
globalThis.expect(c.extensions[0].extensionPointName).toBe("Ordering.Orders");
|
|
333
315
|
});
|
|
334
316
|
});
|
|
335
317
|
|
|
@@ -147,7 +147,7 @@ describe("ProvisionAccounts.provision", () => {
|
|
|
147
147
|
|
|
148
148
|
test("a refused pool leaves the manifest untouched", async () => {
|
|
149
149
|
let dir = NodeFs.mkdtempSync(NodePath.join([NodeOs.tmpdir(), "provision-accounts-"]))
|
|
150
|
-
let file = NodePath.join([dir, "
|
|
150
|
+
let file = NodePath.join([dir, "accounts.yaml"])
|
|
151
151
|
NodeFs.writeFileSync(file, manifest)
|
|
152
152
|
let refuse = async (~providerId as _, ~usernames as _) => Error("pool signs in on email")
|
|
153
153
|
let outcome = await Provision.provision(
|
|
@@ -147,7 +147,7 @@ globalThis.describe("ProvisionAccounts.provision", () => {
|
|
|
147
147
|
`;
|
|
148
148
|
globalThis.test("a refused pool leaves the manifest untouched", async () => {
|
|
149
149
|
let dir = Nodefs.mkdtempSync(Nodepath.join(Nodeos.tmpdir(), "provision-accounts-"));
|
|
150
|
-
let file = Nodepath.join(dir, "
|
|
150
|
+
let file = Nodepath.join(dir, "accounts.yaml");
|
|
151
151
|
Nodefs.writeFileSync(file, manifest, "utf8");
|
|
152
152
|
let refuse = async (param, param$1) => ({
|
|
153
153
|
TAG: "Error",
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
module EventCollectorChannel = EventCollectorChannel.SQS
|
|
2
|
-
module RuntimeEnvironment = RuntimeEnvironment.Lambda
|
|
3
|
-
module EventCollectorRuntimeBuilder = ReventlessCore.EventCollectorRuntime_Builder_PerEventCollector.Make(
|
|
4
|
-
RuntimeEnvironment,
|
|
5
|
-
EventCollectorChannel,
|
|
6
|
-
)
|
|
7
|
-
|
|
8
|
-
module Make = (
|
|
9
|
-
Spec: Reventless.ReadModel.Spec,
|
|
10
|
-
Mappings: Reventless.Projection.Mappings with module Target := Spec,
|
|
11
|
-
) => ReventlessCore.ReadModel_Builder.Make(
|
|
12
|
-
Spec,
|
|
13
|
-
Mappings,
|
|
14
|
-
RuntimeEnvironment,
|
|
15
|
-
QueryDbStorage.DynamoDb,
|
|
16
|
-
QueryDbResolvers.AppSync,
|
|
17
|
-
EventCollectorChannel,
|
|
18
|
-
EventCollectorRuntimeBuilder,
|
|
19
|
-
)
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as ReadModel_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/ReadModel/ReadModel_Builder.res.mjs";
|
|
4
|
-
import * as QueryDbStorage_DynamoDb$ReventlessAws from "../adapter/QueryDb/QueryDbStorage_DynamoDb.res.mjs";
|
|
5
|
-
import * as QueryDbResolvers_AppSync$ReventlessAws from "../adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs";
|
|
6
|
-
import * as EventCollectorChannel_SQS$ReventlessAws from "../adapter/EventCollector/EventCollectorChannel_SQS.res.mjs";
|
|
7
|
-
import * as RuntimeEnvironment_Lambda$ReventlessAws from "../adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
|
|
8
|
-
import * as EventCollectorRuntime_Builder_PerEventCollector$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Runtime/EventCollectorRuntime_Builder_PerEventCollector.res.mjs";
|
|
9
|
-
|
|
10
|
-
let EventCollectorRuntimeBuilder = EventCollectorRuntime_Builder_PerEventCollector$ReventlessCore.Make({
|
|
11
|
-
make: RuntimeEnvironment_Lambda$ReventlessAws.make,
|
|
12
|
-
groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
|
|
13
|
-
extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
|
|
14
|
-
extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
|
|
15
|
-
extractSentTimestamp: RuntimeEnvironment_Lambda$ReventlessAws.extractSentTimestamp,
|
|
16
|
-
extractRetryCount: RuntimeEnvironment_Lambda$ReventlessAws.extractRetryCount,
|
|
17
|
-
asEventHandler: prim => prim,
|
|
18
|
-
asEffectHandler: prim => prim
|
|
19
|
-
})({
|
|
20
|
-
make: EventCollectorChannel_SQS$ReventlessAws.make,
|
|
21
|
-
connect: EventCollectorChannel_SQS$ReventlessAws.connect
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
function Make(Spec) {
|
|
25
|
-
return Mappings => ReadModel_Builder$ReventlessCore.Make(Spec)(Mappings)({
|
|
26
|
-
make: RuntimeEnvironment_Lambda$ReventlessAws.make,
|
|
27
|
-
groupBySource: RuntimeEnvironment_Lambda$ReventlessAws.groupBySource,
|
|
28
|
-
extractCorrelationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCorrelationId,
|
|
29
|
-
extractCausationId: RuntimeEnvironment_Lambda$ReventlessAws.extractCausationId,
|
|
30
|
-
extractSentTimestamp: RuntimeEnvironment_Lambda$ReventlessAws.extractSentTimestamp,
|
|
31
|
-
extractRetryCount: RuntimeEnvironment_Lambda$ReventlessAws.extractRetryCount,
|
|
32
|
-
asEventHandler: prim => prim,
|
|
33
|
-
asEffectHandler: prim => prim
|
|
34
|
-
})({
|
|
35
|
-
make: QueryDbStorage_DynamoDb$ReventlessAws.make
|
|
36
|
-
})({
|
|
37
|
-
make: QueryDbResolvers_AppSync$ReventlessAws.make
|
|
38
|
-
})({
|
|
39
|
-
make: EventCollectorChannel_SQS$ReventlessAws.make,
|
|
40
|
-
connect: EventCollectorChannel_SQS$ReventlessAws.connect
|
|
41
|
-
})(EventCollectorRuntimeBuilder);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let EventCollectorChannel;
|
|
45
|
-
|
|
46
|
-
let RuntimeEnvironment;
|
|
47
|
-
|
|
48
|
-
export {
|
|
49
|
-
EventCollectorChannel,
|
|
50
|
-
RuntimeEnvironment,
|
|
51
|
-
EventCollectorRuntimeBuilder,
|
|
52
|
-
Make,
|
|
53
|
-
}
|
|
54
|
-
/* EventCollectorRuntimeBuilder Not a pure module */
|