@reventlessdev/reventless-aws 3.0.0-alpha.201 → 3.0.0-alpha.202
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 +7 -0
- package/package.json +5 -5
- package/src/Platform.res +123 -25
- package/src/Platform.res.mjs +209 -102
- package/src/adapter/Api/ApiFragmentDeregistration.res +5 -4
- package/src/adapter/Api/ApiFragmentDeregistration.res.mjs +2 -2
- package/src/adapter/Api/ApiSchemaPush.res +54 -0
- package/src/adapter/Api/ApiSchemaPush.res.mjs +37 -0
- package/src/adapter/Runtime/ApiSchemaPush_Runtime.mjs +197 -0
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +47 -0
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs +34 -0
- package/src/components/SideEffectHandler_Single.res +8 -0
- package/src/components/SideEffectHandler_Single.res.mjs +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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.202 (2026-07-13)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **admin:** ApiFragmentRegistry slice→singleton-aggregate + reactive schema-push SideEffect ([cf70903](https://github.com/ReventlessDev/reventless-core/commit/cf709033f0d822248ebbcfe3fe6b4019b3c1e8a0)), closes [#4](https://github.com/ReventlessDev/reventless-core/issues/4)
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.201 (2026-07-13)
|
|
7
14
|
|
|
8
15
|
### Bug Fixes
|
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.202",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.27",
|
|
12
12
|
"@reventlessdev/rescript-aws-sdk": "2.2.0-alpha.21",
|
|
13
13
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.7",
|
|
14
|
-
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.15",
|
|
15
14
|
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.50",
|
|
15
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.15",
|
|
16
16
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.15",
|
|
17
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
17
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.161",
|
|
18
18
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.98",
|
|
19
|
-
"@reventlessdev/reventless-
|
|
19
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.25",
|
|
20
20
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.76",
|
|
21
|
-
"@reventlessdev/reventless-
|
|
21
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.26"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"rescript": "^12.3.0",
|
package/src/Platform.res
CHANGED
|
@@ -113,9 +113,9 @@ module MakeWithConfig = (
|
|
|
113
113
|
// Lambda (DynamoDB scan) — keep it off the Postgres selection like the other
|
|
114
114
|
// admin stores.
|
|
115
115
|
QueryDbBackend.exempt(ReventlessCore.UiFragments.name)
|
|
116
|
-
// The ApiFragments
|
|
117
|
-
//
|
|
118
|
-
QueryDbBackend.exempt(ReventlessCore.
|
|
116
|
+
// The ApiFragments read model backs the Platform_ApiFragments status query
|
|
117
|
+
// (DynamoDB scan) and the schema-push SideEffect — admin store, off Postgres too.
|
|
118
|
+
QueryDbBackend.exempt(ReventlessCore.ApiFragmentsReadModelSpec.name)
|
|
119
119
|
})
|
|
120
120
|
type api = Types.AppSync.api
|
|
121
121
|
type role = Types.AppSync.role
|
|
@@ -585,14 +585,8 @@ module MakeWithConfig = (
|
|
|
585
585
|
ReventlessCore.UiFragments,
|
|
586
586
|
ReventlessCore.UiFragments_Projection,
|
|
587
587
|
)
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
ReventlessCore.ApiFragmentRegistry_Behavior,
|
|
591
|
-
)
|
|
592
|
-
module ApiFragmentsViewSlice = StateViewSlice.Make(
|
|
593
|
-
ReventlessCore.ApiFragments,
|
|
594
|
-
ReventlessCore.ApiFragments_Projection,
|
|
595
|
-
)
|
|
588
|
+
// The API-fragment registry is a SINGLETON AGGREGATE now (not a DCB slice) — see
|
|
589
|
+
// ApiFragmentRegistryAggregate / ApiFragmentsReadModel below.
|
|
596
590
|
|
|
597
591
|
// Empty base fragment — no types, no mutations, no queries.
|
|
598
592
|
// Used by the plugin Api in split mode so plugin schema has no core fields.
|
|
@@ -779,8 +773,10 @@ module MakeWithConfig = (
|
|
|
779
773
|
->Pulumi.Output.flatMap(endpoint => {
|
|
780
774
|
let run = async () => {
|
|
781
775
|
let at = Date.make()->Date.toISOString
|
|
776
|
+
// The registry is a SINGLETON aggregate — the mutation `id` arg is the fixed
|
|
777
|
+
// constant, `pluginId` (a payload field) carries the plugin name.
|
|
782
778
|
let variables = {
|
|
783
|
-
"id":
|
|
779
|
+
"id": "registry",
|
|
784
780
|
"pluginId": name,
|
|
785
781
|
"fragment": {"encoded": fragment.encoded, "protocol": fragment.protocol},
|
|
786
782
|
"apiTarget": Util_AppSync_Caller.graphqlEnum(apiTargetName),
|
|
@@ -794,14 +790,14 @@ module MakeWithConfig = (
|
|
|
794
790
|
let result = await Util_AppSync_Caller.sendMutation(
|
|
795
791
|
~endpoint,
|
|
796
792
|
~region,
|
|
797
|
-
~mutation="
|
|
793
|
+
~mutation="Platform_ApiFragmentRegistry_RegisterApiFragment",
|
|
798
794
|
~selection,
|
|
799
795
|
~variables,
|
|
800
796
|
)
|
|
801
797
|
let outcome =
|
|
802
798
|
result
|
|
803
799
|
->Option.flatMap(JSON.Decode.object)
|
|
804
|
-
->Option.flatMap(d => d->Dict.get("
|
|
800
|
+
->Option.flatMap(d => d->Dict.get("Platform_ApiFragmentRegistry_RegisterApiFragment"))
|
|
805
801
|
->Option.flatMap(JSON.Decode.object)
|
|
806
802
|
switch outcome {
|
|
807
803
|
| Some(cmd) =>
|
|
@@ -1480,6 +1476,43 @@ module MakeWithConfig = (
|
|
|
1480
1476
|
PluginReadModelMappings,
|
|
1481
1477
|
)
|
|
1482
1478
|
|
|
1479
|
+
// Admin-internal ApiFragmentRegistry singleton aggregate — the platform API-schema
|
|
1480
|
+
// fragment registry (docs/plans/event-sourced-fragment-registries.md). Threaded into
|
|
1481
|
+
// Admin.construct's ~aggregates so registerAdminAggregateMutations wires
|
|
1482
|
+
// Platform_ApiFragmentRegistry_RegisterApiFragment / _DeregisterApiFragment via the
|
|
1483
|
+
// standard CommandGenerator auto-flow (RecordApiFragmentPush carries `@noApi`). IAM
|
|
1484
|
+
// dual-auth for the SigV4 deploy caller comes from AdminApi.systemCallerFieldNames.
|
|
1485
|
+
module ApiFragmentRegistryAggregate: (
|
|
1486
|
+
ReventlessInfra.Aggregate.T with type api = Types.AppSync.api
|
|
1487
|
+
) = Aggregate_Builder_Single.Make(
|
|
1488
|
+
ReventlessCore.ApiFragmentRegistrySpec,
|
|
1489
|
+
ReventlessCore.ApiFragmentRegistryBehavior,
|
|
1490
|
+
ReventlessInfra.NoEventMappings.Make(ReventlessCore.ApiFragmentRegistrySpec),
|
|
1491
|
+
)
|
|
1492
|
+
|
|
1493
|
+
// Admin ApiFragments read model — per-plugin status rows off the aggregate's event
|
|
1494
|
+
// topic (DynamoDB stream), backing the Platform_ApiFragments status query.
|
|
1495
|
+
module ApiFragmentsReadModelMappings: Reventless.Projection.Mappings
|
|
1496
|
+
with module Target := ReventlessCore.ApiFragmentsReadModelSpec = {
|
|
1497
|
+
module M = ReventlessCore.ApiFragmentsProjection.Mappings
|
|
1498
|
+
module type Mapping = M.Mapping
|
|
1499
|
+
let moduleUrl: string = ReventlessCore.ApiFragmentsProjection.moduleUrl
|
|
1500
|
+
let mappings: array<module(Mapping)> = ReventlessCore.ApiFragmentsProjection.mappings
|
|
1501
|
+
}
|
|
1502
|
+
module ApiFragmentsReadModel = ReadModel_Builder_Single_Stream.Make(
|
|
1503
|
+
ReventlessCore.ApiFragmentsReadModelSpec,
|
|
1504
|
+
ApiFragmentsReadModelMappings,
|
|
1505
|
+
)
|
|
1506
|
+
|
|
1507
|
+
// Admin SideEffectHandler hosting the reactive schema-push side effect (ApiSchemaPush).
|
|
1508
|
+
// Subscribes to the ApiFragmentRegistry aggregate's DynamoDB stream (single-shard for a
|
|
1509
|
+
// singleton → naturally serialized, no concurrent StartSchemaCreation) and, on each
|
|
1510
|
+
// ApiSchemaComputed, stitches + pushes the schema per target API, then records the
|
|
1511
|
+
// outcome via RecordApiFragmentPush. Wired in deployPlatform (the staged path where the
|
|
1512
|
+
// deploy caller fires RegisterApiFragment); makePlatform builds the schema directly and
|
|
1513
|
+
// never triggers it.
|
|
1514
|
+
module AdminApiSchemaPushHandler = SideEffectHandler_Single.Make()
|
|
1515
|
+
|
|
1483
1516
|
module type PluginMaker = {
|
|
1484
1517
|
let make: unit => Plugin.component
|
|
1485
1518
|
}
|
|
@@ -1649,14 +1682,14 @@ module MakeWithConfig = (
|
|
|
1649
1682
|
let admin = Admin.construct(
|
|
1650
1683
|
~version,
|
|
1651
1684
|
~extensionPoints=[],
|
|
1652
|
-
~aggregates=[module(PluginAggregate)],
|
|
1653
|
-
~readModels=[module(PluginReadModel)],
|
|
1685
|
+
~aggregates=[module(PluginAggregate), module(ApiFragmentRegistryAggregate)],
|
|
1686
|
+
~readModels=[module(PluginReadModel), module(ApiFragmentsReadModel)],
|
|
1654
1687
|
~scheduler,
|
|
1655
1688
|
~resourceNaming=Util_ResourceNaming.operations,
|
|
1656
1689
|
~api=platformApi,
|
|
1657
1690
|
~apiRole=platformApiRole,
|
|
1658
|
-
~stateChangeSlices=[module(UiFragmentRegistrySlice)
|
|
1659
|
-
~stateViewSlices=[module(UiFragmentsViewSlice)
|
|
1691
|
+
~stateChangeSlices=[module(UiFragmentRegistrySlice)],
|
|
1692
|
+
~stateViewSlices=[module(UiFragmentsViewSlice)],
|
|
1660
1693
|
~automationSlices=[],
|
|
1661
1694
|
~outboundTranslationSlices=[],
|
|
1662
1695
|
~inboundTranslationSlices=[],
|
|
@@ -1704,7 +1737,7 @@ module MakeWithConfig = (
|
|
|
1704
1737
|
// StateViewSlice table and returns the push-status row per plugin (the deploy
|
|
1705
1738
|
// waiter polls this). The query field is in the pushed admin base but was
|
|
1706
1739
|
// unresolved on AWS until now.
|
|
1707
|
-
switch admin.
|
|
1740
|
+
switch admin.readModelsOutputs->Dict.get("ApiFragments") {
|
|
1708
1741
|
| Some(rm) =>
|
|
1709
1742
|
switch rm.queryDb.resources->Array.get(0) {
|
|
1710
1743
|
| Some(r) =>
|
|
@@ -1899,19 +1932,84 @@ module MakeWithConfig = (
|
|
|
1899
1932
|
let admin = Admin.construct(
|
|
1900
1933
|
~version,
|
|
1901
1934
|
~extensionPoints=[module(PluginExtensionPoint)],
|
|
1902
|
-
~aggregates=[module(PluginAggregate)],
|
|
1903
|
-
~readModels=[module(PluginReadModel)],
|
|
1935
|
+
~aggregates=[module(PluginAggregate), module(ApiFragmentRegistryAggregate)],
|
|
1936
|
+
~readModels=[module(PluginReadModel), module(ApiFragmentsReadModel)],
|
|
1904
1937
|
~scheduler,
|
|
1905
1938
|
~resourceNaming=Util_ResourceNaming.operations,
|
|
1906
1939
|
~api=platformApi,
|
|
1907
1940
|
~apiRole=platformApiRole,
|
|
1908
|
-
~stateChangeSlices=[module(UiFragmentRegistrySlice)
|
|
1909
|
-
~stateViewSlices=[module(UiFragmentsViewSlice)
|
|
1941
|
+
~stateChangeSlices=[module(UiFragmentRegistrySlice)],
|
|
1942
|
+
~stateViewSlices=[module(UiFragmentsViewSlice)],
|
|
1910
1943
|
~automationSlices=[],
|
|
1911
1944
|
~outboundTranslationSlices=[],
|
|
1912
1945
|
~inboundTranslationSlices=[],
|
|
1913
1946
|
)
|
|
1914
1947
|
|
|
1948
|
+
// Reactive schema-push SideEffect (docs/plans/event-sourced-fragment-registries.md §
|
|
1949
|
+
// Reactive writer design). Host ApiSchemaPush on an admin SideEffectHandler subscribed to
|
|
1950
|
+
// the ApiFragmentRegistry aggregate's event topic (its DynamoDB stream). Deploy-derived
|
|
1951
|
+
// config the runtime-pure push reads at invocation time is injected as Lambda env
|
|
1952
|
+
// (~extraEnvVars); ~targets grants SQS send to the aggregate command topic (for the
|
|
1953
|
+
// RecordApiFragmentPush write-back). Only in deployPlatform (the staged path where the
|
|
1954
|
+
// deploy caller fires RegisterApiFragment); makePlatform pushes the schema directly.
|
|
1955
|
+
let apiSchemaPushEventTopics = ReventlessCore.Aggregate.allEventTopics(admin.aggregatesOutputs)
|
|
1956
|
+
let apiSchemaPushCmdTopics = ReventlessCore.Aggregate.allCommandTopics(admin.aggregatesOutputs)
|
|
1957
|
+
let apiSchemaPushCmdTopicUrl =
|
|
1958
|
+
admin.aggregatesOutputs
|
|
1959
|
+
->Dict.get(ReventlessCore.ApiFragmentRegistrySpec.name)
|
|
1960
|
+
->Option.map(agg =>
|
|
1961
|
+
agg.commandTopic->Pulumi.Output.flatMap(ct =>
|
|
1962
|
+
switch ct.resources->Array.get(0) {
|
|
1963
|
+
| Some(r) => r.id
|
|
1964
|
+
| None => Pulumi.Output.make("")
|
|
1965
|
+
}
|
|
1966
|
+
)
|
|
1967
|
+
)
|
|
1968
|
+
->Option.getOr(Pulumi.Output.make(""))
|
|
1969
|
+
let apiSchemaPushEnv = Dict.fromArray([
|
|
1970
|
+
("API_SCHEMA_PUSH_DOMAIN_API_ID", domainApiId->Pulumi.Output.asInput),
|
|
1971
|
+
(
|
|
1972
|
+
"API_SCHEMA_PUSH_PLATFORM_API_ID",
|
|
1973
|
+
platformApi->Pulumi.Output.flatMap(api => api.id)->Pulumi.Output.asInput,
|
|
1974
|
+
),
|
|
1975
|
+
(
|
|
1976
|
+
"API_SCHEMA_PUSH_SPLIT_API",
|
|
1977
|
+
Pulumi.Output.make(Config.splitApi ? "true" : "false")->Pulumi.Output.asInput,
|
|
1978
|
+
),
|
|
1979
|
+
(
|
|
1980
|
+
"API_SCHEMA_PUSH_CLONER",
|
|
1981
|
+
Pulumi.Output.make(Config.cloner ? "true" : "false")->Pulumi.Output.asInput,
|
|
1982
|
+
),
|
|
1983
|
+
("API_SCHEMA_PUSH_CMD_TOPIC_URL", apiSchemaPushCmdTopicUrl->Pulumi.Output.asInput),
|
|
1984
|
+
])
|
|
1985
|
+
// ApiSchemaPush ignores the injected queryEngine (it self-wires push + write-back from
|
|
1986
|
+
// env); an empty one satisfies the SideEffectHandler contract. `scheduler`/`queryEngine`
|
|
1987
|
+
// are Outputs, so make() runs inside their apply; finish() (which builds the shared
|
|
1988
|
+
// Lambda from the registered handlers) is registered on apiSchemaPushCmdTopics
|
|
1989
|
+
// immediately after make() so it runs AFTER make's own deferred forEventCollector apply
|
|
1990
|
+
// on that same Output (Pulumi runs same-Output apply callbacks in registration order).
|
|
1991
|
+
let apiSchemaPushQueryEngine = QueryEngine.DynamoDb.make(Dict.make())
|
|
1992
|
+
let _ =
|
|
1993
|
+
(scheduler, apiSchemaPushQueryEngine)
|
|
1994
|
+
->Pulumi.Output.all2
|
|
1995
|
+
->Pulumi.Output.apply(((sched, qe)) => {
|
|
1996
|
+
let _ = AdminApiSchemaPushHandler.make(
|
|
1997
|
+
~name="AdminApiSchemaPush",
|
|
1998
|
+
~sideEffects=[module(ApiSchemaPush)],
|
|
1999
|
+
~allEventTopics=apiSchemaPushEventTopics,
|
|
2000
|
+
~allCommandTopics=apiSchemaPushCmdTopics,
|
|
2001
|
+
~targets=[ReventlessCore.ApiFragmentRegistrySpec.name],
|
|
2002
|
+
~queryEngine=qe,
|
|
2003
|
+
~scheduler=sched,
|
|
2004
|
+
~resourceNaming=Util_ResourceNaming.operations,
|
|
2005
|
+
~extraEnvVars=apiSchemaPushEnv,
|
|
2006
|
+
~opts={},
|
|
2007
|
+
)
|
|
2008
|
+
let _ = apiSchemaPushCmdTopics->Pulumi.Output.apply(_ =>
|
|
2009
|
+
SideEffectHandlerRuntime_Builder_Single.finish()
|
|
2010
|
+
)
|
|
2011
|
+
})
|
|
2012
|
+
|
|
1915
2013
|
// Extract Plugin RM table name as Output.t<string>.
|
|
1916
2014
|
// IMPORTANT: Do NOT use option<Pulumi.Output.t<…>> — Pulumi Outputs use property
|
|
1917
2015
|
// lifting, which breaks ReScript's internal option encoding (BS_PRIVATE_NESTED_SOME_NONE).
|
|
@@ -1991,7 +2089,7 @@ module MakeWithConfig = (
|
|
|
1991
2089
|
// so the consumer's `tableOutput->Pulumi.Output.apply` crashes with
|
|
1992
2090
|
// "apply is not a function". A `Some(r.name)` LITERAL compiles unboxed (bare
|
|
1993
2091
|
// r.name), preserving the Output — the same dodge `pluginReadModelTableName` uses.
|
|
1994
|
-
~apiFragmentRegistryTableName=?switch admin.
|
|
2092
|
+
~apiFragmentRegistryTableName=?switch admin.readModelsOutputs
|
|
1995
2093
|
->Dict.get("ApiFragments")
|
|
1996
2094
|
->Option.flatMap(rm => rm.queryDb.resources->Array.get(0)) {
|
|
1997
2095
|
| Some(r) => Some(r.name)
|
|
@@ -2040,7 +2138,7 @@ module MakeWithConfig = (
|
|
|
2040
2138
|
// StateViewSlice table and returns the push-status row per plugin (the deploy
|
|
2041
2139
|
// waiter polls this). The query field is in the pushed admin base but was
|
|
2042
2140
|
// unresolved on AWS until now.
|
|
2043
|
-
switch admin.
|
|
2141
|
+
switch admin.readModelsOutputs->Dict.get("ApiFragments") {
|
|
2044
2142
|
| Some(rm) =>
|
|
2045
2143
|
switch rm.queryDb.resources->Array.get(0) {
|
|
2046
2144
|
| Some(r) =>
|
package/src/Platform.res.mjs
CHANGED
|
@@ -31,7 +31,7 @@ import * as Api_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/
|
|
|
31
31
|
import * as Plugin_Stack$ReventlessAws from "./plugin/stack/Plugin_Stack.res.mjs";
|
|
32
32
|
import * as UiFragments$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments.res.mjs";
|
|
33
33
|
import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/util/Util_Pulumi.res.mjs";
|
|
34
|
-
import * as
|
|
34
|
+
import * as ApiSchemaPush$ReventlessAws from "./adapter/Api/ApiSchemaPush.res.mjs";
|
|
35
35
|
import * as Util_DynamoDb$ReventlessAws from "./util/Util_DynamoDb.res.mjs";
|
|
36
36
|
import * as Platform_Casts$ReventlessAws from "./Platform_Casts.res.mjs";
|
|
37
37
|
import * as Platform_Stack$ReventlessAws from "./Platform_Stack.res.mjs";
|
|
@@ -57,7 +57,6 @@ import * as StateTopic_AppSync$ReventlessAws from "./adapter/StateTopic/StateTop
|
|
|
57
57
|
import * as UiFragmentRegistry$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry.res.mjs";
|
|
58
58
|
import * as Util_AppSync_Caller$ReventlessAws from "./util/Util_AppSync_Caller.res.mjs";
|
|
59
59
|
import * as Util_ResourceNaming$ReventlessAws from "./util/Util_ResourceNaming.res.mjs";
|
|
60
|
-
import * as ApiFragmentRegistry$ReventlessCore from "@reventlessdev/reventless-core/src/admin/ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry.res.mjs";
|
|
61
60
|
import * as ClonerRunner_Fargate$ReventlessAws from "./plugin/cloner/ClonerRunner_Fargate.res.mjs";
|
|
62
61
|
import * as QueryEngine_DynamoDb$ReventlessAws from "./adapter/QueryEngine/QueryEngine_DynamoDb.res.mjs";
|
|
63
62
|
import * as PluginRuntime_Builder$ReventlessAws from "./plugin/runtime/PluginRuntime_Builder.res.mjs";
|
|
@@ -65,26 +64,29 @@ import * as PluginsReadModelSpec$ReventlessCore from "@reventlessdev/reventless-
|
|
|
65
64
|
import * as ExtensionPoint_Builder$ReventlessAws from "./components/ExtensionPoint_Builder.res.mjs";
|
|
66
65
|
import * as StateViewSlice_Builder$ReventlessAws from "./components/StateViewSlice_Builder.res.mjs";
|
|
67
66
|
import * as Task_Builder_PerBucket$ReventlessAws from "./components/Task_Builder_PerBucket.res.mjs";
|
|
67
|
+
import * as ApiFragmentsProjection$ReventlessCore from "@reventlessdev/reventless-core/src/admin/ApiFragmentRegistry/ApiFragmentsProjection.res.mjs";
|
|
68
68
|
import * as AutomationSlice_Builder$ReventlessAws from "./components/AutomationSlice_Builder.res.mjs";
|
|
69
69
|
import * as EventTopicPublisher_SNS$ReventlessAws from "./adapter/EventTopic/EventTopicPublisher_SNS.res.mjs";
|
|
70
70
|
import * as ExtensionPointMapping$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/ExtensionPointMapping.res.mjs";
|
|
71
71
|
import * as PgQueryResolver_Builder$ReventlessAws from "./adapter/QueryDb/PgQueryResolver_Builder.res.mjs";
|
|
72
72
|
import * as UiFragments_Projection$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateViewSlice/UiFragments_Projection.res.mjs";
|
|
73
73
|
import * as Aggregate_Builder_Single$ReventlessAws from "./components/Aggregate_Builder_Single.res.mjs";
|
|
74
|
-
import * as
|
|
74
|
+
import * as ApiFragmentRegistrySpec$ReventlessCore from "@reventlessdev/reventless-core/src/admin/ApiFragmentRegistry/ApiFragmentRegistrySpec.res.mjs";
|
|
75
75
|
import * as ReadModel_Builder_Single$ReventlessAws from "./components/ReadModel_Builder_Single.res.mjs";
|
|
76
|
+
import * as SideEffectHandler_Single$ReventlessAws from "./components/SideEffectHandler_Single.res.mjs";
|
|
76
77
|
import * as StateChangeSlice_Builder$ReventlessAws from "./components/StateChangeSlice_Builder.res.mjs";
|
|
77
78
|
import * as ApiFragmentDeregistration$ReventlessAws from "./adapter/Api/ApiFragmentDeregistration.res.mjs";
|
|
78
79
|
import * as EventCollectorChannel_SQS$ReventlessAws from "./adapter/EventCollector/EventCollectorChannel_SQS.res.mjs";
|
|
79
80
|
import * as PgChangeFeedRelay_Builder$ReventlessAws from "./adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs";
|
|
80
81
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
|
|
82
|
+
import * as ApiFragmentsReadModelSpec$ReventlessCore from "@reventlessdev/reventless-core/src/admin/ApiFragmentRegistry/ApiFragmentsReadModelSpec.res.mjs";
|
|
81
83
|
import * as PluginExtensionPointSpec$ReventlessInfra from "@reventlessdev/reventless-infra/src/types/PluginExtensionPointSpec.res.mjs";
|
|
82
84
|
import * as Platform_UIFragments_Lambda$ReventlessAws from "./adapter/Api/Platform_UIFragments_Lambda.res.mjs";
|
|
85
|
+
import * as ApiFragmentRegistryBehavior$ReventlessCore from "@reventlessdev/reventless-core/src/admin/ApiFragmentRegistry/ApiFragmentRegistryBehavior.res.mjs";
|
|
83
86
|
import * as CommandTopicChannel_SQS_Sync$ReventlessAws from "./adapter/CommandTopic/CommandTopicChannel_SQS_Sync.res.mjs";
|
|
84
87
|
import * as EventLogSubscription_AppSync$ReventlessAws from "./adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs";
|
|
85
88
|
import * as Platform_ApiFragments_Lambda$ReventlessAws from "./adapter/Api/Platform_ApiFragments_Lambda.res.mjs";
|
|
86
89
|
import * as UiFragmentRegistry_Behavior$ReventlessCore from "@reventlessdev/reventless-core/src/admin/UiFragmentRegistry/StateChangeSlice/UiFragmentRegistry_Behavior.res.mjs";
|
|
87
|
-
import * as ApiFragmentRegistry_Behavior$ReventlessCore from "@reventlessdev/reventless-core/src/admin/ApiFragmentRegistry/StateChangeSlice/ApiFragmentRegistry_Behavior.res.mjs";
|
|
88
90
|
import * as CommandTopicChannel_SQS_Async$ReventlessAws from "./adapter/CommandTopic/CommandTopicChannel_SQS_Async.res.mjs";
|
|
89
91
|
import * as Plugin_ExtensionPoint_Builder$ReventlessAws from "./plugin/stack/Plugin_ExtensionPoint_Builder.res.mjs";
|
|
90
92
|
import * as QueryDbStorage_DynamoDbStream$ReventlessAws from "./adapter/QueryDb/QueryDbStorage_DynamoDbStream.res.mjs";
|
|
@@ -104,6 +106,7 @@ import * as Platform_ComponentDefinitions_Lambda$ReventlessAws from "./adapter/A
|
|
|
104
106
|
import * as StateViewSliceRuntime_Builder_Single$ReventlessAws from "./adapter/Runtime/StateViewSliceRuntime_Builder_Single.res.mjs";
|
|
105
107
|
import * as AggregateRuntime_Builder_Single_Async$ReventlessAws from "./adapter/Runtime/AggregateRuntime_Builder_Single_Async.res.mjs";
|
|
106
108
|
import * as AutomationSliceRuntime_Builder_Single$ReventlessAws from "./adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs";
|
|
109
|
+
import * as SideEffectHandlerRuntime_Builder_Single$ReventlessAws from "./adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res.mjs";
|
|
107
110
|
|
|
108
111
|
let log = Logger$ReventlessCore.fromEnv();
|
|
109
112
|
|
|
@@ -154,7 +157,7 @@ function MakeWithConfig(Config) {
|
|
|
154
157
|
});
|
|
155
158
|
QueryDbBackend$ReventlessAws.exempt(PluginsReadModelSpec$ReventlessCore.name);
|
|
156
159
|
QueryDbBackend$ReventlessAws.exempt(UiFragments$ReventlessCore.name);
|
|
157
|
-
QueryDbBackend$ReventlessAws.exempt(
|
|
160
|
+
QueryDbBackend$ReventlessAws.exempt(ApiFragmentsReadModelSpec$ReventlessCore.name);
|
|
158
161
|
});
|
|
159
162
|
let currentDeployTarget = {
|
|
160
163
|
contents: "Domain"
|
|
@@ -459,36 +462,6 @@ function MakeWithConfig(Config) {
|
|
|
459
462
|
project: UiFragments_Projection$ReventlessCore.project,
|
|
460
463
|
moduleUrl: UiFragments_Projection$ReventlessCore.moduleUrl
|
|
461
464
|
});
|
|
462
|
-
let Spec$1 = {
|
|
463
|
-
name: ApiFragmentRegistry$ReventlessCore.name,
|
|
464
|
-
moduleUrl: ApiFragmentRegistry$ReventlessCore.moduleUrl,
|
|
465
|
-
Id: Id$Reventless.$$String,
|
|
466
|
-
consumedEventSchema: ApiFragmentRegistry$ReventlessCore.consumedEventSchema,
|
|
467
|
-
errorSchema: ApiFragmentRegistry$ReventlessCore.errorSchema,
|
|
468
|
-
eventSchema: ApiFragmentRegistry$ReventlessCore.eventSchema,
|
|
469
|
-
commandSchema: ApiFragmentRegistry$ReventlessCore.commandSchema,
|
|
470
|
-
commandAuthorization: ApiFragmentRegistry$ReventlessCore.commandAuthorization,
|
|
471
|
-
readConsistency: ApiFragmentRegistry$ReventlessCore.readConsistency
|
|
472
|
-
};
|
|
473
|
-
let ApiFragmentRegistrySlice = StateChangeSlice_Builder$ReventlessAws.Make(Spec$1)({
|
|
474
|
-
initialState: undefined,
|
|
475
|
-
evolve: ApiFragmentRegistry_Behavior$ReventlessCore.evolve,
|
|
476
|
-
decide: ApiFragmentRegistry_Behavior$ReventlessCore.decide,
|
|
477
|
-
moduleUrl: ApiFragmentRegistry_Behavior$ReventlessCore.moduleUrl
|
|
478
|
-
});
|
|
479
|
-
let ApiFragmentsViewSlice = Make$7({
|
|
480
|
-
name: ApiFragments$ReventlessCore.name,
|
|
481
|
-
moduleUrl: ApiFragments$ReventlessCore.moduleUrl,
|
|
482
|
-
stateSchema: ApiFragments$ReventlessCore.stateSchema,
|
|
483
|
-
consumedEventSchema: ApiFragments$ReventlessCore.consumedEventSchema,
|
|
484
|
-
config: ApiFragments$ReventlessCore.config,
|
|
485
|
-
subIdConfig: undefined,
|
|
486
|
-
authorization: ApiFragments$ReventlessCore.authorization,
|
|
487
|
-
visibility: ApiFragments$ReventlessCore.visibility
|
|
488
|
-
})({
|
|
489
|
-
project: ApiFragments_Projection$ReventlessCore.project,
|
|
490
|
-
moduleUrl: ApiFragments_Projection$ReventlessCore.moduleUrl
|
|
491
|
-
});
|
|
492
465
|
let emptyBaseFragment = GraphQL_Stitcher$ReventlessCore.encode({
|
|
493
466
|
types: [],
|
|
494
467
|
mutations: [],
|
|
@@ -629,7 +602,7 @@ function MakeWithConfig(Config) {
|
|
|
629
602
|
let run = async () => {
|
|
630
603
|
let at = new Date().toISOString();
|
|
631
604
|
let variables = {
|
|
632
|
-
id:
|
|
605
|
+
id: "registry",
|
|
633
606
|
pluginId: name,
|
|
634
607
|
fragment: {
|
|
635
608
|
encoded: pluginFragment.encoded,
|
|
@@ -639,8 +612,8 @@ function MakeWithConfig(Config) {
|
|
|
639
612
|
at: at
|
|
640
613
|
};
|
|
641
614
|
log.info("registerFragmentViaApi", undefined, `Registering API fragment for ` + name + ` (` + apiTargetName + `) via ` + endpoint);
|
|
642
|
-
let result = await Util_AppSync_Caller$ReventlessAws.sendMutation(endpoint, region, "
|
|
643
|
-
let outcome = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(result, Stdlib_JSON.Decode.object), d => d["
|
|
615
|
+
let result = await Util_AppSync_Caller$ReventlessAws.sendMutation(endpoint, region, "Platform_ApiFragmentRegistry_RegisterApiFragment", "{ __typename ... on CommandAccepted { eventCount } ... on CommandRejected { errorCode errorDetail } }", variables);
|
|
616
|
+
let outcome = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(result, Stdlib_JSON.Decode.object), d => d["Platform_ApiFragmentRegistry_RegisterApiFragment"]), Stdlib_JSON.Decode.object);
|
|
644
617
|
if (outcome === undefined) {
|
|
645
618
|
return await waitForApiFragmentPush(endpoint, region, name, at);
|
|
646
619
|
}
|
|
@@ -987,6 +960,40 @@ function MakeWithConfig(Config) {
|
|
|
987
960
|
authorization: PluginsReadModelSpec$ReventlessCore.authorization,
|
|
988
961
|
visibility: PluginsReadModelSpec$ReventlessCore.visibility
|
|
989
962
|
})(PluginReadModelMappings);
|
|
963
|
+
let ApiFragmentRegistryAggregate = Aggregate_Builder_Single$ReventlessAws.Make({
|
|
964
|
+
Id: Id$Reventless.$$String,
|
|
965
|
+
name: ApiFragmentRegistrySpec$ReventlessCore.name,
|
|
966
|
+
eventSchema: ApiFragmentRegistrySpec$ReventlessCore.eventSchema,
|
|
967
|
+
errorSchema: ApiFragmentRegistrySpec$ReventlessCore.errorSchema,
|
|
968
|
+
commandSchema: ApiFragmentRegistrySpec$ReventlessCore.commandSchema,
|
|
969
|
+
moduleUrl: ApiFragmentRegistrySpec$ReventlessCore.moduleUrl,
|
|
970
|
+
commandAuthorization: ApiFragmentRegistrySpec$ReventlessCore.commandAuthorization
|
|
971
|
+
})({
|
|
972
|
+
initialState: ApiFragmentRegistryBehavior$ReventlessCore.initialState,
|
|
973
|
+
evolve: ApiFragmentRegistryBehavior$ReventlessCore.evolve,
|
|
974
|
+
decide: ApiFragmentRegistryBehavior$ReventlessCore.decide,
|
|
975
|
+
snapshot: undefined,
|
|
976
|
+
moduleUrl: ApiFragmentRegistryBehavior$ReventlessCore.moduleUrl
|
|
977
|
+
})(NoEventMappings$ReventlessInfra.Make({
|
|
978
|
+
name: ApiFragmentRegistrySpec$ReventlessCore.name,
|
|
979
|
+
Id: Id$Reventless.$$String,
|
|
980
|
+
commandSchema: ApiFragmentRegistrySpec$ReventlessCore.commandSchema
|
|
981
|
+
}));
|
|
982
|
+
let ApiFragmentsReadModelMappings = {
|
|
983
|
+
moduleUrl: ApiFragmentsProjection$ReventlessCore.moduleUrl,
|
|
984
|
+
mappings: ApiFragmentsProjection$ReventlessCore.mappings
|
|
985
|
+
};
|
|
986
|
+
let ApiFragmentsReadModel = ReadModel_Builder_Single_Stream$ReventlessAws.Make({
|
|
987
|
+
Id: Id$Reventless.$$String,
|
|
988
|
+
name: ApiFragmentsReadModelSpec$ReventlessCore.name,
|
|
989
|
+
moduleUrl: ApiFragmentsReadModelSpec$ReventlessCore.moduleUrl,
|
|
990
|
+
stateSchema: ApiFragmentsReadModelSpec$ReventlessCore.stateSchema,
|
|
991
|
+
config: ApiFragmentsReadModelSpec$ReventlessCore.config,
|
|
992
|
+
subIdConfig: undefined,
|
|
993
|
+
authorization: ApiFragmentsReadModelSpec$ReventlessCore.authorization,
|
|
994
|
+
visibility: ApiFragmentsReadModelSpec$ReventlessCore.visibility
|
|
995
|
+
})(ApiFragmentsReadModelMappings);
|
|
996
|
+
let AdminApiSchemaPushHandler = SideEffectHandler_Single$ReventlessAws.Make({});
|
|
990
997
|
let makeScheduler = () => {
|
|
991
998
|
let component = Scheduler$ReventlessAws.make(undefined);
|
|
992
999
|
let outputs = Component$ReventlessCore.outputs(component);
|
|
@@ -1118,13 +1125,13 @@ function MakeWithConfig(Config) {
|
|
|
1118
1125
|
};
|
|
1119
1126
|
}
|
|
1120
1127
|
}
|
|
1121
|
-
let admin = Admin.construct(version, [], [
|
|
1122
|
-
|
|
1123
|
-
|
|
1128
|
+
let admin = Admin.construct(version, [], [
|
|
1129
|
+
PluginAggregate,
|
|
1130
|
+
ApiFragmentRegistryAggregate
|
|
1124
1131
|
], [
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
], [], [], []);
|
|
1132
|
+
PluginReadModel,
|
|
1133
|
+
ApiFragmentsReadModel
|
|
1134
|
+
], scheduler, Util_ResourceNaming$ReventlessAws.operations, platformApi, platformApiRole, [UiFragmentRegistrySlice], [UiFragmentsViewSlice], [], [], []);
|
|
1128
1135
|
let pluginRm = admin.readModelsOutputs["Plugins"];
|
|
1129
1136
|
if (pluginRm !== undefined) {
|
|
1130
1137
|
let r = pluginRm.queryDb.resources[0];
|
|
@@ -1140,7 +1147,7 @@ function MakeWithConfig(Config) {
|
|
|
1140
1147
|
Platform_UIFragments_Lambda$ReventlessAws.make(platformApi, r$1.name, admin.adminSchemaPushed, {});
|
|
1141
1148
|
}
|
|
1142
1149
|
}
|
|
1143
|
-
let rm$1 = admin.
|
|
1150
|
+
let rm$1 = admin.readModelsOutputs["ApiFragments"];
|
|
1144
1151
|
if (rm$1 !== undefined) {
|
|
1145
1152
|
let r$2 = rm$1.queryDb.resources[0];
|
|
1146
1153
|
if (r$2 !== undefined) {
|
|
@@ -1233,13 +1240,61 @@ function MakeWithConfig(Config) {
|
|
|
1233
1240
|
updateApiSchema: updateApiSchema,
|
|
1234
1241
|
manageSubscriptions: undefined
|
|
1235
1242
|
});
|
|
1236
|
-
let admin = Admin.construct(version, [PluginExtensionPoint], [
|
|
1237
|
-
|
|
1238
|
-
|
|
1243
|
+
let admin = Admin.construct(version, [PluginExtensionPoint], [
|
|
1244
|
+
PluginAggregate,
|
|
1245
|
+
ApiFragmentRegistryAggregate
|
|
1239
1246
|
], [
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
], [], [], []);
|
|
1247
|
+
PluginReadModel,
|
|
1248
|
+
ApiFragmentsReadModel
|
|
1249
|
+
], scheduler, Util_ResourceNaming$ReventlessAws.operations, platformApi, platformApiRole, [UiFragmentRegistrySlice], [UiFragmentsViewSlice], [], [], []);
|
|
1250
|
+
let apiSchemaPushEventTopics = Aggregate$ReventlessCore.allEventTopics(admin.aggregatesOutputs);
|
|
1251
|
+
let apiSchemaPushCmdTopics = Aggregate$ReventlessCore.allCommandTopics(admin.aggregatesOutputs);
|
|
1252
|
+
let apiSchemaPushCmdTopicUrl = Stdlib_Option.getOr(Stdlib_Option.map(admin.aggregatesOutputs[ApiFragmentRegistrySpec$ReventlessCore.name], agg => Output$Pulumi.flatMap(agg.commandTopic, ct => {
|
|
1253
|
+
let r = ct.resources[0];
|
|
1254
|
+
if (r !== undefined) {
|
|
1255
|
+
return r.id;
|
|
1256
|
+
} else {
|
|
1257
|
+
return Pulumi.output("");
|
|
1258
|
+
}
|
|
1259
|
+
})), Pulumi.output(""));
|
|
1260
|
+
let apiSchemaPushEnv = Object.fromEntries([
|
|
1261
|
+
[
|
|
1262
|
+
"API_SCHEMA_PUSH_DOMAIN_API_ID",
|
|
1263
|
+
domainApiId
|
|
1264
|
+
],
|
|
1265
|
+
[
|
|
1266
|
+
"API_SCHEMA_PUSH_PLATFORM_API_ID",
|
|
1267
|
+
Output$Pulumi.flatMap(platformApi, api => api.id)
|
|
1268
|
+
],
|
|
1269
|
+
[
|
|
1270
|
+
"API_SCHEMA_PUSH_SPLIT_API",
|
|
1271
|
+
Pulumi.output(Config.splitApi ? "true" : "false")
|
|
1272
|
+
],
|
|
1273
|
+
[
|
|
1274
|
+
"API_SCHEMA_PUSH_CLONER",
|
|
1275
|
+
Pulumi.output(Config.cloner ? "true" : "false")
|
|
1276
|
+
],
|
|
1277
|
+
[
|
|
1278
|
+
"API_SCHEMA_PUSH_CMD_TOPIC_URL",
|
|
1279
|
+
apiSchemaPushCmdTopicUrl
|
|
1280
|
+
]
|
|
1281
|
+
]);
|
|
1282
|
+
let apiSchemaPushQueryEngine = QueryEngine_DynamoDb$ReventlessAws.make({});
|
|
1283
|
+
Pulumi.all([
|
|
1284
|
+
scheduler,
|
|
1285
|
+
apiSchemaPushQueryEngine
|
|
1286
|
+
]).apply(param => {
|
|
1287
|
+
AdminApiSchemaPushHandler.make("AdminApiSchemaPush", [{
|
|
1288
|
+
Source: {
|
|
1289
|
+
name: ApiFragmentRegistrySpec$ReventlessCore.name,
|
|
1290
|
+
Id: Id$Reventless.$$String,
|
|
1291
|
+
eventSchema: ApiFragmentRegistrySpec$ReventlessCore.eventSchema
|
|
1292
|
+
},
|
|
1293
|
+
moduleUrl: ApiSchemaPush$ReventlessAws.moduleUrl,
|
|
1294
|
+
execute: ApiSchemaPush$ReventlessAws.execute
|
|
1295
|
+
}], apiSchemaPushEventTopics, apiSchemaPushCmdTopics, [ApiFragmentRegistrySpec$ReventlessCore.name], param[1], param[0], Util_ResourceNaming$ReventlessAws.operations, undefined, undefined, apiSchemaPushEnv, {});
|
|
1296
|
+
apiSchemaPushCmdTopics.apply(param => SideEffectHandlerRuntime_Builder_Single$ReventlessAws.finish());
|
|
1297
|
+
});
|
|
1243
1298
|
let pluginRm = admin.readModelsOutputs["Plugins"];
|
|
1244
1299
|
let pluginReadModelTableName;
|
|
1245
1300
|
if (pluginRm !== undefined) {
|
|
@@ -1268,7 +1323,7 @@ function MakeWithConfig(Config) {
|
|
|
1268
1323
|
return Pulumi.output("NOT_AVAILABLE");
|
|
1269
1324
|
}
|
|
1270
1325
|
});
|
|
1271
|
-
let r$1 = Stdlib_Option.flatMap(admin.
|
|
1326
|
+
let r$1 = Stdlib_Option.flatMap(admin.readModelsOutputs["ApiFragments"], rm => rm.queryDb.resources[0]);
|
|
1272
1327
|
PluginRuntime_Builder$ReventlessAws.registerConfig(pluginEpEventTopicArn, pluginReadModelTableName, pluginSchemaPersistenceTable.name, hooks_schedulerRoleUrn.contents, undefined, undefined, domainApiId, Config.cloner, Output$Pulumi.flatMap(platformApi, api => api.id), r$1 !== undefined ? r$1.name : undefined, AutomationSliceRuntime_Builder_Single$ReventlessAws.getDcbQueueUrl(), Config.splitApi, undefined);
|
|
1273
1328
|
if (pluginReadModelTableName !== undefined) {
|
|
1274
1329
|
AggregateRuntime_Builder_Single$ReventlessAws.setPluginReadModelTable(pluginReadModelTableName);
|
|
@@ -1281,7 +1336,7 @@ function MakeWithConfig(Config) {
|
|
|
1281
1336
|
Platform_UIFragments_Lambda$ReventlessAws.make(platformApi, r$2.name, admin.adminSchemaPushed, {});
|
|
1282
1337
|
}
|
|
1283
1338
|
}
|
|
1284
|
-
let rm$1 = admin.
|
|
1339
|
+
let rm$1 = admin.readModelsOutputs["ApiFragments"];
|
|
1285
1340
|
if (rm$1 !== undefined) {
|
|
1286
1341
|
let r$3 = rm$1.queryDb.resources[0];
|
|
1287
1342
|
if (r$3 !== undefined) {
|
|
@@ -1544,7 +1599,7 @@ function Make($star) {
|
|
|
1544
1599
|
});
|
|
1545
1600
|
QueryDbBackend$ReventlessAws.exempt(PluginsReadModelSpec$ReventlessCore.name);
|
|
1546
1601
|
QueryDbBackend$ReventlessAws.exempt(UiFragments$ReventlessCore.name);
|
|
1547
|
-
QueryDbBackend$ReventlessAws.exempt(
|
|
1602
|
+
QueryDbBackend$ReventlessAws.exempt(ApiFragmentsReadModelSpec$ReventlessCore.name);
|
|
1548
1603
|
});
|
|
1549
1604
|
let currentDeployTarget = {
|
|
1550
1605
|
contents: "Domain"
|
|
@@ -1849,36 +1904,6 @@ function Make($star) {
|
|
|
1849
1904
|
project: UiFragments_Projection$ReventlessCore.project,
|
|
1850
1905
|
moduleUrl: UiFragments_Projection$ReventlessCore.moduleUrl
|
|
1851
1906
|
});
|
|
1852
|
-
let Spec$1 = {
|
|
1853
|
-
name: ApiFragmentRegistry$ReventlessCore.name,
|
|
1854
|
-
moduleUrl: ApiFragmentRegistry$ReventlessCore.moduleUrl,
|
|
1855
|
-
Id: Id$Reventless.$$String,
|
|
1856
|
-
consumedEventSchema: ApiFragmentRegistry$ReventlessCore.consumedEventSchema,
|
|
1857
|
-
errorSchema: ApiFragmentRegistry$ReventlessCore.errorSchema,
|
|
1858
|
-
eventSchema: ApiFragmentRegistry$ReventlessCore.eventSchema,
|
|
1859
|
-
commandSchema: ApiFragmentRegistry$ReventlessCore.commandSchema,
|
|
1860
|
-
commandAuthorization: ApiFragmentRegistry$ReventlessCore.commandAuthorization,
|
|
1861
|
-
readConsistency: ApiFragmentRegistry$ReventlessCore.readConsistency
|
|
1862
|
-
};
|
|
1863
|
-
let ApiFragmentRegistrySlice = StateChangeSlice_Builder$ReventlessAws.Make(Spec$1)({
|
|
1864
|
-
initialState: undefined,
|
|
1865
|
-
evolve: ApiFragmentRegistry_Behavior$ReventlessCore.evolve,
|
|
1866
|
-
decide: ApiFragmentRegistry_Behavior$ReventlessCore.decide,
|
|
1867
|
-
moduleUrl: ApiFragmentRegistry_Behavior$ReventlessCore.moduleUrl
|
|
1868
|
-
});
|
|
1869
|
-
let ApiFragmentsViewSlice = Make$8({
|
|
1870
|
-
name: ApiFragments$ReventlessCore.name,
|
|
1871
|
-
moduleUrl: ApiFragments$ReventlessCore.moduleUrl,
|
|
1872
|
-
stateSchema: ApiFragments$ReventlessCore.stateSchema,
|
|
1873
|
-
consumedEventSchema: ApiFragments$ReventlessCore.consumedEventSchema,
|
|
1874
|
-
config: ApiFragments$ReventlessCore.config,
|
|
1875
|
-
subIdConfig: undefined,
|
|
1876
|
-
authorization: ApiFragments$ReventlessCore.authorization,
|
|
1877
|
-
visibility: ApiFragments$ReventlessCore.visibility
|
|
1878
|
-
})({
|
|
1879
|
-
project: ApiFragments_Projection$ReventlessCore.project,
|
|
1880
|
-
moduleUrl: ApiFragments_Projection$ReventlessCore.moduleUrl
|
|
1881
|
-
});
|
|
1882
1907
|
let emptyBaseFragment = GraphQL_Stitcher$ReventlessCore.encode({
|
|
1883
1908
|
types: [],
|
|
1884
1909
|
mutations: [],
|
|
@@ -2018,7 +2043,7 @@ function Make($star) {
|
|
|
2018
2043
|
let run = async () => {
|
|
2019
2044
|
let at = new Date().toISOString();
|
|
2020
2045
|
let variables = {
|
|
2021
|
-
id:
|
|
2046
|
+
id: "registry",
|
|
2022
2047
|
pluginId: name,
|
|
2023
2048
|
fragment: {
|
|
2024
2049
|
encoded: pluginFragment.encoded,
|
|
@@ -2028,8 +2053,8 @@ function Make($star) {
|
|
|
2028
2053
|
at: at
|
|
2029
2054
|
};
|
|
2030
2055
|
log.info("registerFragmentViaApi", undefined, `Registering API fragment for ` + name + ` (` + apiTargetName + `) via ` + endpoint);
|
|
2031
|
-
let result = await Util_AppSync_Caller$ReventlessAws.sendMutation(endpoint, region, "
|
|
2032
|
-
let outcome = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(result, Stdlib_JSON.Decode.object), d => d["
|
|
2056
|
+
let result = await Util_AppSync_Caller$ReventlessAws.sendMutation(endpoint, region, "Platform_ApiFragmentRegistry_RegisterApiFragment", "{ __typename ... on CommandAccepted { eventCount } ... on CommandRejected { errorCode errorDetail } }", variables);
|
|
2057
|
+
let outcome = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_Option.flatMap(result, Stdlib_JSON.Decode.object), d => d["Platform_ApiFragmentRegistry_RegisterApiFragment"]), Stdlib_JSON.Decode.object);
|
|
2033
2058
|
if (outcome === undefined) {
|
|
2034
2059
|
return await waitForApiFragmentPush(endpoint, region, name, at);
|
|
2035
2060
|
}
|
|
@@ -2372,6 +2397,40 @@ function Make($star) {
|
|
|
2372
2397
|
authorization: PluginsReadModelSpec$ReventlessCore.authorization,
|
|
2373
2398
|
visibility: PluginsReadModelSpec$ReventlessCore.visibility
|
|
2374
2399
|
})(PluginReadModelMappings);
|
|
2400
|
+
let ApiFragmentRegistryAggregate = Aggregate_Builder_Single$ReventlessAws.Make({
|
|
2401
|
+
Id: Id$Reventless.$$String,
|
|
2402
|
+
name: ApiFragmentRegistrySpec$ReventlessCore.name,
|
|
2403
|
+
eventSchema: ApiFragmentRegistrySpec$ReventlessCore.eventSchema,
|
|
2404
|
+
errorSchema: ApiFragmentRegistrySpec$ReventlessCore.errorSchema,
|
|
2405
|
+
commandSchema: ApiFragmentRegistrySpec$ReventlessCore.commandSchema,
|
|
2406
|
+
moduleUrl: ApiFragmentRegistrySpec$ReventlessCore.moduleUrl,
|
|
2407
|
+
commandAuthorization: ApiFragmentRegistrySpec$ReventlessCore.commandAuthorization
|
|
2408
|
+
})({
|
|
2409
|
+
initialState: ApiFragmentRegistryBehavior$ReventlessCore.initialState,
|
|
2410
|
+
evolve: ApiFragmentRegistryBehavior$ReventlessCore.evolve,
|
|
2411
|
+
decide: ApiFragmentRegistryBehavior$ReventlessCore.decide,
|
|
2412
|
+
snapshot: undefined,
|
|
2413
|
+
moduleUrl: ApiFragmentRegistryBehavior$ReventlessCore.moduleUrl
|
|
2414
|
+
})(NoEventMappings$ReventlessInfra.Make({
|
|
2415
|
+
name: ApiFragmentRegistrySpec$ReventlessCore.name,
|
|
2416
|
+
Id: Id$Reventless.$$String,
|
|
2417
|
+
commandSchema: ApiFragmentRegistrySpec$ReventlessCore.commandSchema
|
|
2418
|
+
}));
|
|
2419
|
+
let ApiFragmentsReadModelMappings = {
|
|
2420
|
+
moduleUrl: ApiFragmentsProjection$ReventlessCore.moduleUrl,
|
|
2421
|
+
mappings: ApiFragmentsProjection$ReventlessCore.mappings
|
|
2422
|
+
};
|
|
2423
|
+
let ApiFragmentsReadModel = ReadModel_Builder_Single_Stream$ReventlessAws.Make({
|
|
2424
|
+
Id: Id$Reventless.$$String,
|
|
2425
|
+
name: ApiFragmentsReadModelSpec$ReventlessCore.name,
|
|
2426
|
+
moduleUrl: ApiFragmentsReadModelSpec$ReventlessCore.moduleUrl,
|
|
2427
|
+
stateSchema: ApiFragmentsReadModelSpec$ReventlessCore.stateSchema,
|
|
2428
|
+
config: ApiFragmentsReadModelSpec$ReventlessCore.config,
|
|
2429
|
+
subIdConfig: undefined,
|
|
2430
|
+
authorization: ApiFragmentsReadModelSpec$ReventlessCore.authorization,
|
|
2431
|
+
visibility: ApiFragmentsReadModelSpec$ReventlessCore.visibility
|
|
2432
|
+
})(ApiFragmentsReadModelMappings);
|
|
2433
|
+
let AdminApiSchemaPushHandler = SideEffectHandler_Single$ReventlessAws.Make({});
|
|
2375
2434
|
let makeScheduler = () => {
|
|
2376
2435
|
let component = Scheduler$ReventlessAws.make(undefined);
|
|
2377
2436
|
let outputs = Component$ReventlessCore.outputs(component);
|
|
@@ -2498,13 +2557,13 @@ function Make($star) {
|
|
|
2498
2557
|
platformApiRole: platformApiRole
|
|
2499
2558
|
};
|
|
2500
2559
|
}
|
|
2501
|
-
let admin = Admin.construct(version, [], [
|
|
2502
|
-
|
|
2503
|
-
|
|
2560
|
+
let admin = Admin.construct(version, [], [
|
|
2561
|
+
PluginAggregate,
|
|
2562
|
+
ApiFragmentRegistryAggregate
|
|
2504
2563
|
], [
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
], [], [], []);
|
|
2564
|
+
PluginReadModel,
|
|
2565
|
+
ApiFragmentsReadModel
|
|
2566
|
+
], scheduler, Util_ResourceNaming$ReventlessAws.operations, platformApi, platformApiRole, [UiFragmentRegistrySlice], [UiFragmentsViewSlice], [], [], []);
|
|
2508
2567
|
let pluginRm = admin.readModelsOutputs["Plugins"];
|
|
2509
2568
|
if (pluginRm !== undefined) {
|
|
2510
2569
|
let r = pluginRm.queryDb.resources[0];
|
|
@@ -2520,7 +2579,7 @@ function Make($star) {
|
|
|
2520
2579
|
Platform_UIFragments_Lambda$ReventlessAws.make(platformApi, r$1.name, admin.adminSchemaPushed, {});
|
|
2521
2580
|
}
|
|
2522
2581
|
}
|
|
2523
|
-
let rm$1 = admin.
|
|
2582
|
+
let rm$1 = admin.readModelsOutputs["ApiFragments"];
|
|
2524
2583
|
if (rm$1 !== undefined) {
|
|
2525
2584
|
let r$2 = rm$1.queryDb.resources[0];
|
|
2526
2585
|
if (r$2 !== undefined) {
|
|
@@ -2601,13 +2660,61 @@ function Make($star) {
|
|
|
2601
2660
|
updateApiSchema: updateApiSchema,
|
|
2602
2661
|
manageSubscriptions: undefined
|
|
2603
2662
|
});
|
|
2604
|
-
let admin = Admin.construct(version, [PluginExtensionPoint], [
|
|
2605
|
-
|
|
2606
|
-
|
|
2663
|
+
let admin = Admin.construct(version, [PluginExtensionPoint], [
|
|
2664
|
+
PluginAggregate,
|
|
2665
|
+
ApiFragmentRegistryAggregate
|
|
2607
2666
|
], [
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
], [], [], []);
|
|
2667
|
+
PluginReadModel,
|
|
2668
|
+
ApiFragmentsReadModel
|
|
2669
|
+
], scheduler, Util_ResourceNaming$ReventlessAws.operations, platformApi, platformApiRole, [UiFragmentRegistrySlice], [UiFragmentsViewSlice], [], [], []);
|
|
2670
|
+
let apiSchemaPushEventTopics = Aggregate$ReventlessCore.allEventTopics(admin.aggregatesOutputs);
|
|
2671
|
+
let apiSchemaPushCmdTopics = Aggregate$ReventlessCore.allCommandTopics(admin.aggregatesOutputs);
|
|
2672
|
+
let apiSchemaPushCmdTopicUrl = Stdlib_Option.getOr(Stdlib_Option.map(admin.aggregatesOutputs[ApiFragmentRegistrySpec$ReventlessCore.name], agg => Output$Pulumi.flatMap(agg.commandTopic, ct => {
|
|
2673
|
+
let r = ct.resources[0];
|
|
2674
|
+
if (r !== undefined) {
|
|
2675
|
+
return r.id;
|
|
2676
|
+
} else {
|
|
2677
|
+
return Pulumi.output("");
|
|
2678
|
+
}
|
|
2679
|
+
})), Pulumi.output(""));
|
|
2680
|
+
let apiSchemaPushEnv = Object.fromEntries([
|
|
2681
|
+
[
|
|
2682
|
+
"API_SCHEMA_PUSH_DOMAIN_API_ID",
|
|
2683
|
+
domainApiId
|
|
2684
|
+
],
|
|
2685
|
+
[
|
|
2686
|
+
"API_SCHEMA_PUSH_PLATFORM_API_ID",
|
|
2687
|
+
Output$Pulumi.flatMap(platformApi, api => api.id)
|
|
2688
|
+
],
|
|
2689
|
+
[
|
|
2690
|
+
"API_SCHEMA_PUSH_SPLIT_API",
|
|
2691
|
+
Pulumi.output("true")
|
|
2692
|
+
],
|
|
2693
|
+
[
|
|
2694
|
+
"API_SCHEMA_PUSH_CLONER",
|
|
2695
|
+
Pulumi.output("false")
|
|
2696
|
+
],
|
|
2697
|
+
[
|
|
2698
|
+
"API_SCHEMA_PUSH_CMD_TOPIC_URL",
|
|
2699
|
+
apiSchemaPushCmdTopicUrl
|
|
2700
|
+
]
|
|
2701
|
+
]);
|
|
2702
|
+
let apiSchemaPushQueryEngine = QueryEngine_DynamoDb$ReventlessAws.make({});
|
|
2703
|
+
Pulumi.all([
|
|
2704
|
+
scheduler,
|
|
2705
|
+
apiSchemaPushQueryEngine
|
|
2706
|
+
]).apply(param => {
|
|
2707
|
+
AdminApiSchemaPushHandler.make("AdminApiSchemaPush", [{
|
|
2708
|
+
Source: {
|
|
2709
|
+
name: ApiFragmentRegistrySpec$ReventlessCore.name,
|
|
2710
|
+
Id: Id$Reventless.$$String,
|
|
2711
|
+
eventSchema: ApiFragmentRegistrySpec$ReventlessCore.eventSchema
|
|
2712
|
+
},
|
|
2713
|
+
moduleUrl: ApiSchemaPush$ReventlessAws.moduleUrl,
|
|
2714
|
+
execute: ApiSchemaPush$ReventlessAws.execute
|
|
2715
|
+
}], apiSchemaPushEventTopics, apiSchemaPushCmdTopics, [ApiFragmentRegistrySpec$ReventlessCore.name], param[1], param[0], Util_ResourceNaming$ReventlessAws.operations, undefined, undefined, apiSchemaPushEnv, {});
|
|
2716
|
+
apiSchemaPushCmdTopics.apply(param => SideEffectHandlerRuntime_Builder_Single$ReventlessAws.finish());
|
|
2717
|
+
});
|
|
2611
2718
|
let pluginRm = admin.readModelsOutputs["Plugins"];
|
|
2612
2719
|
let pluginReadModelTableName;
|
|
2613
2720
|
if (pluginRm !== undefined) {
|
|
@@ -2636,7 +2743,7 @@ function Make($star) {
|
|
|
2636
2743
|
return Pulumi.output("NOT_AVAILABLE");
|
|
2637
2744
|
}
|
|
2638
2745
|
});
|
|
2639
|
-
let r$1 = Stdlib_Option.flatMap(admin.
|
|
2746
|
+
let r$1 = Stdlib_Option.flatMap(admin.readModelsOutputs["ApiFragments"], rm => rm.queryDb.resources[0]);
|
|
2640
2747
|
PluginRuntime_Builder$ReventlessAws.registerConfig(pluginEpEventTopicArn, pluginReadModelTableName, pluginSchemaPersistenceTable.name, hooks_schedulerRoleUrn.contents, undefined, undefined, domainApiId, false, Output$Pulumi.flatMap(platformApi, api => api.id), r$1 !== undefined ? r$1.name : undefined, AutomationSliceRuntime_Builder_Single$ReventlessAws.getDcbQueueUrl(), true, undefined);
|
|
2641
2748
|
if (pluginReadModelTableName !== undefined) {
|
|
2642
2749
|
AggregateRuntime_Builder_Single$ReventlessAws.setPluginReadModelTable(pluginReadModelTableName);
|
|
@@ -2649,7 +2756,7 @@ function Make($star) {
|
|
|
2649
2756
|
Platform_UIFragments_Lambda$ReventlessAws.make(platformApi, r$2.name, admin.adminSchemaPushed, {});
|
|
2650
2757
|
}
|
|
2651
2758
|
}
|
|
2652
|
-
let rm$1 = admin.
|
|
2759
|
+
let rm$1 = admin.readModelsOutputs["ApiFragments"];
|
|
2653
2760
|
if (rm$1 !== undefined) {
|
|
2654
2761
|
let r$3 = rm$1.queryDb.resources[0];
|
|
2655
2762
|
if (r$3 !== undefined) {
|
|
@@ -44,7 +44,7 @@ let decodeId = (id: string): option<providerInputs> =>
|
|
|
44
44
|
let sendDeregister: (JSON.t, string, string) => promise<unit> = %raw(`
|
|
45
45
|
async function (variables, endpoint, region) {
|
|
46
46
|
const mod = await import("@reventlessdev/reventless-aws/src/util/Util_AppSync_Caller.res.mjs");
|
|
47
|
-
await mod.sendMutation(endpoint, region, "
|
|
47
|
+
await mod.sendMutation(endpoint, region, "Platform_ApiFragmentRegistry_DeregisterApiFragment", "{ __typename }", variables);
|
|
48
48
|
}
|
|
49
49
|
`)
|
|
50
50
|
|
|
@@ -75,9 +75,10 @@ let delete_ = async (id: string, props: providerInputs): unit => {
|
|
|
75
75
|
}
|
|
76
76
|
let variables =
|
|
77
77
|
Dict.fromArray([
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
(
|
|
78
|
+
// Platform_ApiFragmentRegistry_DeregisterApiFragment(id: ID!, pluginId: ID!) — the
|
|
79
|
+
// registry is a SINGLETON aggregate, so `id` is the fixed constant; `pluginId`
|
|
80
|
+
// (payload) names the plugin whose fragment is removed.
|
|
81
|
+
("id", JSON.Encode.string("registry")),
|
|
81
82
|
("pluginId", JSON.Encode.string(carrier.pluginId)),
|
|
82
83
|
])->JSON.Encode.object
|
|
83
84
|
// Best-effort: on a full teardown the platform API may already be gone, and a
|
|
@@ -27,7 +27,7 @@ function decodeId(id) {
|
|
|
27
27
|
|
|
28
28
|
let sendDeregister = (async function (variables, endpoint, region) {
|
|
29
29
|
const mod = await import("@reventlessdev/reventless-aws/src/util/Util_AppSync_Caller.res.mjs");
|
|
30
|
-
await mod.sendMutation(endpoint, region, "
|
|
30
|
+
await mod.sendMutation(endpoint, region, "Platform_ApiFragmentRegistry_DeregisterApiFragment", "{ __typename }", variables);
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
let errMessage = ((e) => (e && e.message) ? String(e.message) : String(e));
|
|
@@ -59,7 +59,7 @@ async function delete_(id, props) {
|
|
|
59
59
|
let variables = Object.fromEntries([
|
|
60
60
|
[
|
|
61
61
|
"id",
|
|
62
|
-
|
|
62
|
+
"registry"
|
|
63
63
|
],
|
|
64
64
|
[
|
|
65
65
|
"pluginId",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// ApiSchemaPush — the admin reactive schema-push SideEffect
|
|
2
|
+
// (docs/plans/event-sourced-fragment-registries.md § Reactive writer design).
|
|
3
|
+
//
|
|
4
|
+
// Source = the ApiFragmentRegistry singleton aggregate. On every ApiSchemaComputed
|
|
5
|
+
// { snapshot } (emitted by the aggregate behaviour alongside each ApiFragment* fact,
|
|
6
|
+
// carrying the WHOLE consistent per-plugin fragment set), this hands the snapshot to
|
|
7
|
+
// the runtime-pure push engine (ApiSchemaPush_Runtime.mjs), which stitches one
|
|
8
|
+
// AWS-decorated schema per target API, pushes each behind the shrink guard, and writes
|
|
9
|
+
// the outcome back via RecordApiFragmentPush.
|
|
10
|
+
//
|
|
11
|
+
// The generic SideEffect.T context (`queryEngine`) is unused: this bespoke platform
|
|
12
|
+
// side effect self-acquires its config (API ids, split flag, command-topic URL) from
|
|
13
|
+
// Lambda env injected by the admin SideEffectHandler (~extraEnvVars). The push module
|
|
14
|
+
// is loaded via dynamic import so no @aws-sdk / provider code is statically captured
|
|
15
|
+
// beyond what the SideEffectHandler Lambda already bundles (runtime-purity — see
|
|
16
|
+
// reference_pulumi_leaks_into_lambda_runtime_graph).
|
|
17
|
+
module Source = ReventlessCore.ApiFragmentRegistrySpec
|
|
18
|
+
|
|
19
|
+
let moduleUrl: string = %raw(`import.meta.url`)
|
|
20
|
+
|
|
21
|
+
type jsEntry = {pluginId: string, encoded: string, protocol: string, apiTarget: string}
|
|
22
|
+
|
|
23
|
+
let pushApiSchema: array<jsEntry> => promise<unit> = %raw(`
|
|
24
|
+
async function (snapshot) {
|
|
25
|
+
const mod = await import("@reventlessdev/reventless-aws/src/adapter/Runtime/ApiSchemaPush_Runtime.mjs");
|
|
26
|
+
await mod.pushApiSchema(snapshot);
|
|
27
|
+
}
|
|
28
|
+
`)
|
|
29
|
+
|
|
30
|
+
let execute = async (
|
|
31
|
+
_id: Source.Id.t,
|
|
32
|
+
_meta: Reventless.Message.meta,
|
|
33
|
+
event: Source.event,
|
|
34
|
+
_queryEngine: Reventless.QueryEngine.operations,
|
|
35
|
+
) =>
|
|
36
|
+
switch event {
|
|
37
|
+
| ApiSchemaComputed({snapshot}) =>
|
|
38
|
+
let jsSnapshot = snapshot->Array.map((e: Source.fragmentSnapshotEntry) => {
|
|
39
|
+
pluginId: e.pluginId,
|
|
40
|
+
encoded: e.encoded,
|
|
41
|
+
protocol: e.protocol,
|
|
42
|
+
apiTarget: switch e.apiTarget {
|
|
43
|
+
| Reventless.Plugin.Domain => "Domain"
|
|
44
|
+
| Reventless.Plugin.Platform => "Platform"
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
await pushApiSchema(jsSnapshot)
|
|
48
|
+
// Fact events are handled via the ApiSchemaComputed echo (which carries the
|
|
49
|
+
// consistent snapshot); the write-back and lifecycle facts are no-ops here.
|
|
50
|
+
| ApiFragmentRegistered(_)
|
|
51
|
+
| ApiFragmentUpdated(_)
|
|
52
|
+
| ApiFragmentDeregistered(_)
|
|
53
|
+
| ApiFragmentPushRecorded(_) => ()
|
|
54
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
let moduleUrl = import.meta.url;
|
|
5
|
+
|
|
6
|
+
let pushApiSchema = (async function (snapshot) {
|
|
7
|
+
const mod = await import("@reventlessdev/reventless-aws/src/adapter/Runtime/ApiSchemaPush_Runtime.mjs");
|
|
8
|
+
await mod.pushApiSchema(snapshot);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
async function execute(_id, _meta, event, _queryEngine) {
|
|
12
|
+
if (event.TAG !== "ApiSchemaComputed") {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
let jsSnapshot = event.snapshot.map(e => {
|
|
16
|
+
let match = e.apiTarget;
|
|
17
|
+
let tmp;
|
|
18
|
+
tmp = match === "Domain" ? "Domain" : "Platform";
|
|
19
|
+
return {
|
|
20
|
+
pluginId: e.pluginId,
|
|
21
|
+
encoded: e.encoded,
|
|
22
|
+
protocol: e.protocol,
|
|
23
|
+
apiTarget: tmp
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
return await pushApiSchema(jsSnapshot);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let Source;
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
Source,
|
|
33
|
+
moduleUrl,
|
|
34
|
+
pushApiSchema,
|
|
35
|
+
execute,
|
|
36
|
+
}
|
|
37
|
+
/* moduleUrl Not a pure module */
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// Runtime-pure push engine for the admin ApiSchemaPush SideEffect
|
|
2
|
+
// (docs/plans/event-sourced-fragment-registries.md § Reactive writer design).
|
|
3
|
+
//
|
|
4
|
+
// The ApiFragmentRegistry singleton aggregate's behaviour emits ApiSchemaComputed
|
|
5
|
+
// { snapshot } — the WHOLE per-plugin fragment set folded from the aggregate's own
|
|
6
|
+
// consistent single-partition state after each change. The ApiSchemaPush SideEffect
|
|
7
|
+
// (compiled from ApiSchemaPush.res) subscribes to that event via the aggregate's
|
|
8
|
+
// DynamoDB stream (single-shard for a singleton → naturally serialized, no concurrent
|
|
9
|
+
// StartSchemaCreation), and hands the snapshot here.
|
|
10
|
+
//
|
|
11
|
+
// This module stitches one AWS-decorated schema per target API from the snapshot
|
|
12
|
+
// (identical decoration to the deploy path via the runtime-pure
|
|
13
|
+
// AppSync_SdlDecorate.planAwsPushes), pushes each behind the catastrophic-shrink
|
|
14
|
+
// guard, and writes the outcome back with RecordApiFragmentPush onto the
|
|
15
|
+
// ApiFragmentRegistry aggregate's command topic so the deploy waiter can poll
|
|
16
|
+
// Platform_ApiFragments.
|
|
17
|
+
//
|
|
18
|
+
// Runtime-purity discipline: NO @pulumi imports (this loads in the SideEffectHandler
|
|
19
|
+
// Lambda — see reference_pulumi_leaks_into_lambda_runtime_graph). All config is read
|
|
20
|
+
// from env at invocation time; the AppSync push uses the AppSync SDK directly.
|
|
21
|
+
|
|
22
|
+
import { makeQueueRef, log } from "./HandlerFactoryHelpers.mjs";
|
|
23
|
+
import { publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
|
|
24
|
+
import { planAwsPushes } from "@reventlessdev/reventless-aws/src/components/Api/AppSync_SdlDecorate.res.mjs";
|
|
25
|
+
import {
|
|
26
|
+
countRootTypeFields,
|
|
27
|
+
isCatastrophicSchemaShrink,
|
|
28
|
+
} from "@reventlessdev/reventless-core/src/components/Api/GraphQL_Stitcher.res.mjs";
|
|
29
|
+
import {
|
|
30
|
+
baseFragment as adminBaseFragment,
|
|
31
|
+
systemCallerFieldNames,
|
|
32
|
+
} from "@reventlessdev/reventless-core/src/admin/AdminApi.res.mjs";
|
|
33
|
+
|
|
34
|
+
const COMP = "apiSchemaPush";
|
|
35
|
+
|
|
36
|
+
// ── AppSync push primitives (mirror AdminEventCollectorEntryPoint.mjs) ──────────
|
|
37
|
+
|
|
38
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
39
|
+
|
|
40
|
+
// Push the SDL. Bounded retry on the AppSync API-level lock ("Schema is currently being
|
|
41
|
+
// altered" / ConcurrentModification) — the legacy connect-driven mkUpdateApiSchema still
|
|
42
|
+
// pushes until Plan Phase 4, and a standalone-service push can also contend. The lock
|
|
43
|
+
// clears in seconds, so this is NOT the plan's rejected lagging-read retry (that had no
|
|
44
|
+
// ceiling). The singleton aggregate's single-shard stream already serializes THIS writer
|
|
45
|
+
// against itself; this only covers cross-writer contention.
|
|
46
|
+
async function updateAppSyncSchema(apiId, sdl) {
|
|
47
|
+
const { AppSyncClient, StartSchemaCreationCommand } = await import("@aws-sdk/client-appsync");
|
|
48
|
+
const client = new AppSyncClient({});
|
|
49
|
+
const maxAttempts = 6;
|
|
50
|
+
for (let attempt = 1; ; attempt++) {
|
|
51
|
+
try {
|
|
52
|
+
await client.send(new StartSchemaCreationCommand({ apiId, definition: sdl }));
|
|
53
|
+
return;
|
|
54
|
+
} catch (e) {
|
|
55
|
+
const msg = (e && e.message) || String(e);
|
|
56
|
+
const locked = /being altered|ConcurrentModification|currently in the process/i.test(msg);
|
|
57
|
+
if (!locked || attempt >= maxAttempts) throw e;
|
|
58
|
+
const backoff = Math.min(1000 * 2 ** (attempt - 1), 8000);
|
|
59
|
+
log.warn(`schema push contended (${msg}) — retry ${attempt}/${maxAttempts} in ${backoff}ms`, { comp: COMP });
|
|
60
|
+
await sleep(backoff);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Current live schema as SDL for the shrink guard. "" (not error) when the API has
|
|
66
|
+
// no schema yet or introspection fails — the caller treats "" as "no baseline".
|
|
67
|
+
async function getCurrentSchemaSdl(apiId) {
|
|
68
|
+
try {
|
|
69
|
+
const { AppSyncClient, GetIntrospectionSchemaCommand } = await import("@aws-sdk/client-appsync");
|
|
70
|
+
const client = new AppSyncClient({});
|
|
71
|
+
const resp = await client.send(new GetIntrospectionSchemaCommand({ apiId, format: "SDL" }));
|
|
72
|
+
if (!resp || !resp.schema) return "";
|
|
73
|
+
return Buffer.from(resp.schema).toString("utf-8");
|
|
74
|
+
} catch (e) {
|
|
75
|
+
log.warn(`could not introspect current schema (${(e && e.message) || e}) — skipping shrink guard`, { comp: COMP });
|
|
76
|
+
return "";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function parseShrinkThreshold(raw) {
|
|
81
|
+
const n = raw ? Number(raw) : NaN;
|
|
82
|
+
return Number.isFinite(n) && n > 0 && n < 1 ? n : 0.5;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Emit the SchemaShrinkRejected CloudWatch metric via EMF (raw console.log — must
|
|
86
|
+
// stay unwrapped so CloudWatch's EMF auto-detect fires).
|
|
87
|
+
function emitShrinkRejectionMetric(apiId, currentRootFields, newRootFields) {
|
|
88
|
+
try {
|
|
89
|
+
// eslint-disable-next-line no-console
|
|
90
|
+
console.log(
|
|
91
|
+
JSON.stringify({
|
|
92
|
+
_aws: {
|
|
93
|
+
Timestamp: Date.now(),
|
|
94
|
+
CloudWatchMetrics: [
|
|
95
|
+
{
|
|
96
|
+
Namespace: "Reventless/Runtime",
|
|
97
|
+
Dimensions: [["ApiId"]],
|
|
98
|
+
Metrics: [{ Name: "SchemaShrinkRejected", Unit: "Count" }],
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
ApiId: apiId,
|
|
103
|
+
SchemaShrinkRejected: 1,
|
|
104
|
+
currentRootFields,
|
|
105
|
+
newRootFields,
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
} catch {
|
|
109
|
+
// metric emission is best-effort
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ── Config (deploy-derived, injected as Lambda env by the admin SideEffectHandler) ──
|
|
114
|
+
|
|
115
|
+
function readConfig() {
|
|
116
|
+
const na = (v) => (v && v !== "NOT_AVAILABLE" ? v : "");
|
|
117
|
+
const domainApiId = na(process.env["API_SCHEMA_PUSH_DOMAIN_API_ID"]);
|
|
118
|
+
const platformApiId = na(process.env["API_SCHEMA_PUSH_PLATFORM_API_ID"]) || domainApiId;
|
|
119
|
+
return {
|
|
120
|
+
domainApiId,
|
|
121
|
+
platformApiId,
|
|
122
|
+
splitApi: process.env["API_SCHEMA_PUSH_SPLIT_API"] === "true",
|
|
123
|
+
clonerEnabled: process.env["API_SCHEMA_PUSH_CLONER"] === "true",
|
|
124
|
+
cmdTopicUrl: na(process.env["API_SCHEMA_PUSH_CMD_TOPIC_URL"]),
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Write RecordApiFragmentPush back per plugin onto the ApiFragmentRegistry aggregate's
|
|
129
|
+
// command topic (FIFO, singleton id="registry"). @noApi + idempotent — a no-op in the
|
|
130
|
+
// aggregate behaviour if the plugin was deregistered in the meantime.
|
|
131
|
+
async function recordPushOutcomes(cmdTopicUrl, pluginIds, ok, message) {
|
|
132
|
+
if (!cmdTopicUrl || pluginIds.length === 0) return;
|
|
133
|
+
const publisher = sqsPublishJsons(makeQueueRef(cmdTopicUrl), "SQS_FIFO");
|
|
134
|
+
const at = new Date().toISOString();
|
|
135
|
+
const commandJsons = pluginIds.map((pluginId) => ({
|
|
136
|
+
id: "registry",
|
|
137
|
+
meta: { service: "ApiFragmentRegistry", time: at, msgId: "pending", correlationId: pluginId },
|
|
138
|
+
commandJson: { TAG: "RecordApiFragmentPush", pluginId, ok, message, at },
|
|
139
|
+
}));
|
|
140
|
+
try {
|
|
141
|
+
await publisher(commandJsons);
|
|
142
|
+
} catch (e) {
|
|
143
|
+
log.error(`RecordApiFragmentPush dispatch failed: ${(e && e.message) || e}`, { comp: COMP });
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ── Entry point ────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
// snapshot: array of { pluginId, encoded, protocol, apiTarget } — the consistent
|
|
150
|
+
// registry contents after the triggering change, carried on the ApiSchemaComputed event.
|
|
151
|
+
export async function pushApiSchema(snapshot) {
|
|
152
|
+
const cfg = readConfig();
|
|
153
|
+
const entries = Array.isArray(snapshot) ? snapshot.filter((e) => e && typeof e.encoded === "string" && e.encoded) : [];
|
|
154
|
+
const pluginIds = [...new Set(entries.map((e) => e.pluginId).filter((p) => typeof p === "string"))];
|
|
155
|
+
if (!cfg.cmdTopicUrl) {
|
|
156
|
+
log.warn("ApiSchemaPush: no command-topic URL configured — skipping", { comp: COMP });
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Fragments straight from the consistent snapshot — NO eventually-consistent read.
|
|
161
|
+
const fragments = entries.map((e) => ({
|
|
162
|
+
encoded: e.encoded,
|
|
163
|
+
protocol: typeof e.protocol === "string" ? e.protocol : "graphql",
|
|
164
|
+
target: e.apiTarget === "Platform" ? "Platform" : "Domain",
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
const rawAdminBase = adminBaseFragment(cfg.clonerEnabled);
|
|
168
|
+
const plans = planAwsPushes(rawAdminBase, systemCallerFieldNames, fragments, cfg.splitApi);
|
|
169
|
+
|
|
170
|
+
let ok = true;
|
|
171
|
+
let message = "";
|
|
172
|
+
for (const plan of plans) {
|
|
173
|
+
const apiId = plan.api === "PlatformApi" ? cfg.platformApiId : cfg.domainApiId;
|
|
174
|
+
if (!apiId) continue;
|
|
175
|
+
const threshold = parseShrinkThreshold(process.env["RUNTIME_SCHEMA_SHRINK_THRESHOLD"]);
|
|
176
|
+
const currentSdl = await getCurrentSchemaSdl(apiId);
|
|
177
|
+
if (isCatastrophicSchemaShrink(currentSdl, plan.sdl, threshold)) {
|
|
178
|
+
const cur = countRootTypeFields(currentSdl, "Mutation") + countRootTypeFields(currentSdl, "Query");
|
|
179
|
+
const nw = countRootTypeFields(plan.sdl, "Mutation") + countRootTypeFields(plan.sdl, "Query");
|
|
180
|
+
log.error(`ABORTED schema push for ${plan.api} (${apiId}): ${nw} root field(s) vs ${cur} live (threshold ${threshold}).`, { comp: COMP });
|
|
181
|
+
emitShrinkRejectionMetric(apiId, cur, nw);
|
|
182
|
+
ok = false;
|
|
183
|
+
message = `shrink guard aborted push for ${plan.api}`;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
await updateAppSyncSchema(apiId, plan.sdl);
|
|
188
|
+
log.info(`schema push OK: ${plan.api} (${apiId})`, { comp: COMP });
|
|
189
|
+
} catch (e) {
|
|
190
|
+
ok = false;
|
|
191
|
+
message = (e && e.message) || String(e);
|
|
192
|
+
log.error(`schema push FAILED: ${plan.api} (${apiId}): ${message}`, { comp: COMP });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
await recordPushOutcomes(cfg.cmdTopicUrl, pluginIds, ok, message);
|
|
197
|
+
}
|
|
@@ -15,6 +15,15 @@ let sideEffectInfos: dict<sideEffectInfo> = Dict.make()
|
|
|
15
15
|
let registerSideEffectHandler = (~sideEffectHandlerName, ~sideEffectModulePaths) =>
|
|
16
16
|
sideEffectInfos->Dict.set(sideEffectHandlerName, {sideEffectModulePaths: sideEffectModulePaths})
|
|
17
17
|
|
|
18
|
+
// Extra Lambda env vars contributed by bespoke side effects (e.g. admin ApiSchemaPush).
|
|
19
|
+
// The shared "AllSideEffectHandlers" Lambda is built once in finish(); all registered
|
|
20
|
+
// entries are merged onto its env there. Deploy-derived config only — never overrides
|
|
21
|
+
// HANDLER_CONFIG.
|
|
22
|
+
let extraEnvVarsAll: dict<Pulumi.Input.t<string>> = Dict.make()
|
|
23
|
+
|
|
24
|
+
let registerExtraEnv = (~extraEnvVars: dict<Pulumi.Input.t<string>>) =>
|
|
25
|
+
extraEnvVars->Dict.forEachWithKey((v, k) => extraEnvVarsAll->Dict.set(k, v))
|
|
26
|
+
|
|
18
27
|
type storedSpec = {
|
|
19
28
|
componentName: string,
|
|
20
29
|
parentResource: Pulumi.Resource.t,
|
|
@@ -143,6 +152,12 @@ let finish = () =>
|
|
|
143
152
|
|
|
144
153
|
let envVars: dict<Pulumi.Input.t<string>> = Dict.make()
|
|
145
154
|
envVars->Dict.set("HANDLER_CONFIG", handlerConfigOutput->Pulumi.Output.asInput)
|
|
155
|
+
// Merge bespoke side-effect config (never overrides HANDLER_CONFIG).
|
|
156
|
+
extraEnvVarsAll->Dict.forEachWithKey((v, k) =>
|
|
157
|
+
if k != "HANDLER_CONFIG" {
|
|
158
|
+
envVars->Dict.set(k, v)
|
|
159
|
+
}
|
|
160
|
+
)
|
|
146
161
|
|
|
147
162
|
// Build AssetArchive: static re-export + user packages
|
|
148
163
|
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
@@ -161,6 +176,38 @@ let finish = () =>
|
|
|
161
176
|
~opts,
|
|
162
177
|
)
|
|
163
178
|
|
|
179
|
+
// The admin ApiSchemaPush side effect (the only extra-env contributor) pushes the
|
|
180
|
+
// stitched schema to AppSync via StartSchemaCreation and reads the live schema for
|
|
181
|
+
// the shrink guard. Grant those AppSync perms to the shared side-effect-handler
|
|
182
|
+
// Lambda role ONLY when a bespoke side effect registered config — Task-only
|
|
183
|
+
// deployments keep the narrow default perimeter. Mirrors the admin EventCollector's
|
|
184
|
+
// AllowAdminStartSchemaCreation grant (PluginRuntime_Builder).
|
|
185
|
+
if extraEnvVarsAll->Dict.keysToArray->Array.length > 0 {
|
|
186
|
+
let _ = PulumiAws.IAM.RolePolicy.make(
|
|
187
|
+
~name="AllSideEffectHandlers-appsyncSchemaPush",
|
|
188
|
+
~args={
|
|
189
|
+
policy: PulumiAws.PolicyDocument.make(
|
|
190
|
+
~id="AllSideEffectHandlersAppsyncSchemaPushPolicy",
|
|
191
|
+
~statements=[
|
|
192
|
+
{
|
|
193
|
+
sid: "AllowSideEffectStartSchemaCreation",
|
|
194
|
+
effect: Allow,
|
|
195
|
+
actions: Actions([
|
|
196
|
+
"appsync:StartSchemaCreation",
|
|
197
|
+
"appsync:GetSchemaCreationStatus",
|
|
198
|
+
"appsync:GetIntrospectionSchema",
|
|
199
|
+
]),
|
|
200
|
+
resources: AllResources,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
)
|
|
204
|
+
->PulumiAws.PolicyDocument.toJsonString
|
|
205
|
+
->Pulumi.Input.make,
|
|
206
|
+
role: runtime.parts.lambdaRole.id->Pulumi.Output.asInput,
|
|
207
|
+
},
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
164
211
|
let channelSpecs = storedSpecs->Array.map(({channelSpec}) => channelSpec)
|
|
165
212
|
let _connectResources = EventCollectorChannel.connect(
|
|
166
213
|
~name="AllSideEffectHandlers",
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as Aws from "@pulumi/aws";
|
|
4
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
3
5
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
4
6
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
7
|
import * as Pulumi from "@pulumi/pulumi";
|
|
6
8
|
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
7
9
|
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
8
10
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
11
|
+
import * as PolicyDocument$PulumiAws from "@reventlessdev/rescript-pulumi-aws/src/IAM/PolicyDocument.res.mjs";
|
|
9
12
|
import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
10
13
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./RuntimeEnvironment_Lambda.res.mjs";
|
|
11
14
|
import * as EventCollectorChannel_DynamoDbStream$ReventlessAws from "../EventCollector/EventCollectorChannel_DynamoDbStream.res.mjs";
|
|
@@ -20,6 +23,14 @@ function registerSideEffectHandler(sideEffectHandlerName, sideEffectModulePaths)
|
|
|
20
23
|
};
|
|
21
24
|
}
|
|
22
25
|
|
|
26
|
+
let extraEnvVarsAll = {};
|
|
27
|
+
|
|
28
|
+
function registerExtraEnv(extraEnvVars) {
|
|
29
|
+
Stdlib_Dict.forEachWithKey(extraEnvVars, (v, k) => {
|
|
30
|
+
extraEnvVarsAll[k] = v;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
23
34
|
let storedSpecs = [];
|
|
24
35
|
|
|
25
36
|
let grandParent = {
|
|
@@ -99,8 +110,29 @@ function finish() {
|
|
|
99
110
|
let handlerConfigOutput = Pulumi.all(handlerOutputs).apply(handlers => `{"handlers":[` + handlers.join(",") + `]}`);
|
|
100
111
|
let envVars = {};
|
|
101
112
|
envVars["HANDLER_CONFIG"] = handlerConfigOutput;
|
|
113
|
+
Stdlib_Dict.forEachWithKey(extraEnvVarsAll, (v, k) => {
|
|
114
|
+
if (k !== "HANDLER_CONFIG") {
|
|
115
|
+
envVars[k] = v;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
102
119
|
let match$1 = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/SideEffectEntryPoint.mjs", packageDirs, undefined);
|
|
103
120
|
let runtime = RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset("AllSideEffectHandlers", "Reactor", match$1.code, match$1.sourceCodeHash, envVars, match[0], match[1], undefined, undefined, undefined, undefined, undefined, opts);
|
|
121
|
+
if (Object.keys(extraEnvVarsAll).length !== 0) {
|
|
122
|
+
new (Aws.iam.RolePolicy)("AllSideEffectHandlers-appsyncSchemaPush", {
|
|
123
|
+
policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, "AllSideEffectHandlersAppsyncSchemaPushPolicy", [{
|
|
124
|
+
Sid: "AllowSideEffectStartSchemaCreation",
|
|
125
|
+
Effect: "Allow",
|
|
126
|
+
Action: [
|
|
127
|
+
"appsync:StartSchemaCreation",
|
|
128
|
+
"appsync:GetSchemaCreationStatus",
|
|
129
|
+
"appsync:GetIntrospectionSchema"
|
|
130
|
+
],
|
|
131
|
+
Resource: "*"
|
|
132
|
+
}])),
|
|
133
|
+
role: runtime.parts.lambdaRole.id
|
|
134
|
+
});
|
|
135
|
+
}
|
|
104
136
|
let channelSpecs = storedSpecs.map(param => param.channelSpec);
|
|
105
137
|
EventCollectorChannel_DynamoDbStream$ReventlessAws.connect("AllSideEffectHandlers", channelSpecs, runtime, opts);
|
|
106
138
|
} else {
|
|
@@ -120,6 +152,8 @@ export {
|
|
|
120
152
|
log,
|
|
121
153
|
sideEffectInfos,
|
|
122
154
|
registerSideEffectHandler,
|
|
155
|
+
extraEnvVarsAll,
|
|
156
|
+
registerExtraEnv,
|
|
123
157
|
storedSpecs,
|
|
124
158
|
grandParent,
|
|
125
159
|
forEventCollector,
|
|
@@ -22,6 +22,7 @@ module Make = (): ReventlessCore.SideEffectHandler.T => {
|
|
|
22
22
|
~resourceNaming,
|
|
23
23
|
~memorySize=?,
|
|
24
24
|
~timeout=?,
|
|
25
|
+
~extraEnvVars=?,
|
|
25
26
|
~opts=?,
|
|
26
27
|
) => {
|
|
27
28
|
let component = Inner.make(
|
|
@@ -49,6 +50,13 @@ module Make = (): ReventlessCore.SideEffectHandler.T => {
|
|
|
49
50
|
~sideEffectModulePaths,
|
|
50
51
|
)
|
|
51
52
|
|
|
53
|
+
// Bespoke side effects (e.g. admin ApiSchemaPush) inject deploy-derived config as
|
|
54
|
+
// extra Lambda env vars, merged onto the shared side-effect-handler Lambda in finish().
|
|
55
|
+
switch extraEnvVars {
|
|
56
|
+
| Some(env) => EventCollectorRuntimeBuilder.registerExtraEnv(~extraEnvVars=env)
|
|
57
|
+
| None => ()
|
|
58
|
+
}
|
|
59
|
+
|
|
52
60
|
component
|
|
53
61
|
}
|
|
54
62
|
}
|
|
@@ -34,10 +34,13 @@ function Make($star) {
|
|
|
34
34
|
forEventCollector: SideEffectHandlerRuntime_Builder_Single$ReventlessAws.forEventCollector,
|
|
35
35
|
finish: SideEffectHandlerRuntime_Builder_Single$ReventlessAws.finish
|
|
36
36
|
});
|
|
37
|
-
let make = (name, sideEffects, allEventTopics, allCommandTopics, targets, queryEngine, scheduler, resourceNaming, memorySize, timeout, opts) => {
|
|
38
|
-
let component = Inner.make(name, sideEffects, allEventTopics, allCommandTopics, targets, queryEngine, scheduler, resourceNaming, memorySize, timeout, opts);
|
|
37
|
+
let make = (name, sideEffects, allEventTopics, allCommandTopics, targets, queryEngine, scheduler, resourceNaming, memorySize, timeout, extraEnvVars, opts) => {
|
|
38
|
+
let component = Inner.make(name, sideEffects, allEventTopics, allCommandTopics, targets, queryEngine, scheduler, resourceNaming, memorySize, timeout, undefined, opts);
|
|
39
39
|
let sideEffectModulePaths = sideEffects.map(SE => Util_Bundle$ReventlessAws.getModuleSpecifier(SE.moduleUrl));
|
|
40
40
|
SideEffectHandlerRuntime_Builder_Single$ReventlessAws.registerSideEffectHandler(name, sideEffectModulePaths);
|
|
41
|
+
if (extraEnvVars !== undefined) {
|
|
42
|
+
SideEffectHandlerRuntime_Builder_Single$ReventlessAws.registerExtraEnv(extraEnvVars);
|
|
43
|
+
}
|
|
41
44
|
return component;
|
|
42
45
|
};
|
|
43
46
|
return {
|