@reventlessdev/reventless-aws 3.0.0-alpha.209 → 3.0.0-alpha.211
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 +16 -0
- package/package.json +8 -8
- package/src/Platform.res +348 -707
- package/src/Platform.res.mjs +271 -739
- package/src/adapter/QueryDb/PgQueryResolver_Builder.res +4 -47
- package/src/adapter/QueryDb/PgQueryResolver_Builder.res.mjs +1 -30
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res +0 -11
- package/src/adapter/QueryDb/QueryDbResolvers_AppSync.res.mjs +0 -5
- package/src/adapter/Runtime/SideEffectHandlerRuntime_Builder_Single.res +6 -7
- package/src/components/Api/AppSync_Adapter.res +143 -101
- package/src/components/Api/AppSync_Adapter.res.mjs +60 -57
- package/src/components/Api/AppSync_MergedApi.res +218 -0
- package/src/components/Api/AppSync_MergedApi.res.mjs +122 -0
- package/src/components/Api/AppSync_SdlDecorate.res +49 -61
- package/src/components/Api/AppSync_SdlDecorate.res.mjs +38 -29
- package/src/components/Plugin.res.mjs +1 -2
- package/tests/AppSync_AdapterTest.res +184 -0
- package/tests/AppSync_AdapterTest.res.mjs +136 -0
- package/tests/AppSync_SdlDecorateTest.res +33 -86
- package/tests/AppSync_SdlDecorateTest.res.mjs +26 -71
- package/tests/MCP_LambdaTest.res +4 -6
- package/tests/MCP_LambdaTest.res.mjs +2 -2
- package/src/adapter/Api/ApiFragmentDeregistration.res +0 -138
- package/src/adapter/Api/ApiFragmentDeregistration.res.mjs +0 -108
- package/src/adapter/Api/ApiSchemaPush.res +0 -79
- package/src/adapter/Api/ApiSchemaPush.res.mjs +0 -64
- package/src/adapter/Api/Platform_ApiFragments_Lambda.res +0 -222
- package/src/adapter/Api/Platform_ApiFragments_Lambda.res.mjs +0 -202
- package/src/adapter/QueryDb/NodeResolver_AppSync.res +0 -71
- package/src/adapter/QueryDb/NodeResolver_AppSync.res.mjs +0 -44
- package/src/adapter/Runtime/ApiSchemaPush_Runtime.mjs +0 -204
- package/tests/ApiSchemaPushTest.res +0 -32
- package/tests/ApiSchemaPushTest.res.mjs +0 -20
|
@@ -61,10 +61,10 @@ globalThis.describe("AppSync_SdlDecorate.injectAwsSubscribe", () => {
|
|
|
61
61
|
});
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
-
globalThis.describe("AppSync_Adapter.
|
|
65
|
-
globalThis.test("assembles neutral
|
|
66
|
-
let
|
|
67
|
-
types: [],
|
|
64
|
+
globalThis.describe("AppSync_Adapter.stitchStandaloneWithAwsDirectives", () => {
|
|
65
|
+
globalThis.test("assembles a neutral fragment into an AWS-dialect standalone document", () => {
|
|
66
|
+
let fragment = GraphQL_Stitcher$ReventlessCore.encode({
|
|
67
|
+
types: [`type PluginStatusChangeEvent {\n pluginId: ID!\n}`],
|
|
68
68
|
mutations: [` Platform_PluginStatusChanged(pluginId: ID!, status: PluginStatus!): PluginStatusChangeEvent`],
|
|
69
69
|
queries: [],
|
|
70
70
|
subscriptions: [` onPluginStatusChange: PluginStatusChangeEvent`],
|
|
@@ -73,19 +73,9 @@ globalThis.describe("AppSync_Adapter.stitchWithAwsDirectives", () => {
|
|
|
73
73
|
mutations: ["Platform_PluginStatusChanged"]
|
|
74
74
|
}]
|
|
75
75
|
});
|
|
76
|
-
let
|
|
77
|
-
types: [`type PluginStatusChangeEvent {\n pluginId: ID!\n}`],
|
|
78
|
-
mutations: [` Catalog_AddProduct(input: AddInput): CommandResult`],
|
|
79
|
-
queries: [],
|
|
80
|
-
subscriptions: [` onCatalog_AddProduct(id: ID): CommandResult`],
|
|
81
|
-
subscriptionSources: [{
|
|
82
|
-
field: "onCatalog_AddProduct",
|
|
83
|
-
mutations: ["Catalog_AddProduct"]
|
|
84
|
-
}]
|
|
85
|
-
});
|
|
86
|
-
let sdl = AppSync_Adapter$ReventlessAws.stitchWithAwsDirectives(baseFragment, [pluginFragment]);
|
|
76
|
+
let sdl = AppSync_Adapter$ReventlessAws.stitchStandaloneWithAwsDirectives(fragment);
|
|
87
77
|
globalThis.expect(sdl).toContain(`@aws_subscribe(mutations: ["Platform_PluginStatusChanged"])`);
|
|
88
|
-
globalThis.expect(sdl).toContain(
|
|
78
|
+
globalThis.expect(sdl).not.toContain("node(id: ID!): Node");
|
|
89
79
|
});
|
|
90
80
|
});
|
|
91
81
|
|
|
@@ -115,65 +105,30 @@ globalThis.describe("AppSync_SdlDecorate.injectAwsAuthAll", () => {
|
|
|
115
105
|
});
|
|
116
106
|
});
|
|
117
107
|
|
|
118
|
-
globalThis.describe("AppSync_SdlDecorate.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
queries: [],
|
|
123
|
-
subscriptions: [],
|
|
124
|
-
subscriptionSources: []
|
|
125
|
-
});
|
|
126
|
-
let iamFieldNames = ["Platform_RegisterApiFragment"];
|
|
127
|
-
let mkFrag = (mutation, target) => ({
|
|
128
|
-
encoded: GraphQL_Stitcher$ReventlessCore.encode({
|
|
129
|
-
types: [],
|
|
130
|
-
mutations: [mutation],
|
|
131
|
-
queries: [],
|
|
132
|
-
subscriptions: [],
|
|
133
|
-
subscriptionSources: []
|
|
134
|
-
}).encoded,
|
|
135
|
-
protocol: "graphql",
|
|
136
|
-
target: target
|
|
137
|
-
});
|
|
138
|
-
let platformFrag = mkFrag(` Inspector_Ping: CommandResult`, "Platform");
|
|
139
|
-
let domainFrag = mkFrag(` Catalog_AddProduct(input: AddInput): CommandResult`, "Domain");
|
|
140
|
-
globalThis.test("split mode: Platform API carries admin base (dual-auth) + Platform frags; Domain API excludes the admin base", () => {
|
|
141
|
-
let plans = AppSync_SdlDecorate$ReventlessAws.planAwsPushes(rawAdminBase, iamFieldNames, [
|
|
142
|
-
platformFrag,
|
|
143
|
-
domainFrag
|
|
144
|
-
], true);
|
|
145
|
-
globalThis.expect(plans.length).toBe(2);
|
|
146
|
-
let platformPlan = Stdlib_Option.getOrThrow(plans.find(p => p.api === "PlatformApi"), undefined);
|
|
147
|
-
let domainPlan = Stdlib_Option.getOrThrow(plans.find(p => p.api === "DomainApi"), undefined);
|
|
148
|
-
globalThis.expect(platformPlan.sdl).toContain("Platform_RegisterApiFragment");
|
|
149
|
-
globalThis.expect(platformPlan.sdl).toContain(`@aws_cognito_user_pools(cognito_groups: ["Admin"]) @aws_iam`);
|
|
150
|
-
globalThis.expect(platformPlan.sdl).toContain("Inspector_Ping");
|
|
151
|
-
globalThis.expect(platformPlan.sdl).toContain(`type CommandAccepted @aws_cognito_user_pools @aws_iam {`);
|
|
152
|
-
globalThis.expect(domainPlan.sdl).toContain("Catalog_AddProduct");
|
|
153
|
-
globalThis.expect(domainPlan.sdl.includes("Platform_RegisterApiFragment")).toBe(false);
|
|
154
|
-
});
|
|
155
|
-
globalThis.test("unified mode: a single Domain API carries admin base + all frags", () => {
|
|
156
|
-
let plans = AppSync_SdlDecorate$ReventlessAws.planAwsPushes(rawAdminBase, iamFieldNames, [
|
|
157
|
-
platformFrag,
|
|
158
|
-
domainFrag
|
|
159
|
-
], false);
|
|
160
|
-
globalThis.expect(plans.length).toBe(1);
|
|
161
|
-
let plan = plans[0];
|
|
162
|
-
globalThis.expect(plan.api).toBe("DomainApi");
|
|
163
|
-
globalThis.expect(plan.sdl).toContain("Platform_RegisterApiFragment");
|
|
164
|
-
globalThis.expect(plan.sdl).toContain("Inspector_Ping");
|
|
165
|
-
globalThis.expect(plan.sdl).toContain("Catalog_AddProduct");
|
|
108
|
+
globalThis.describe("AppSync_SdlDecorate.stampSharedIamTypes", () => {
|
|
109
|
+
globalThis.test("stamps the CommandResult members", () => {
|
|
110
|
+
let sdl = AppSync_SdlDecorate$ReventlessAws.stampSharedIamTypes("type CommandAccepted {\n id: ID!\n}");
|
|
111
|
+
globalThis.expect(sdl).toContain("type CommandAccepted @aws_cognito_user_pools @aws_iam {");
|
|
166
112
|
});
|
|
167
113
|
});
|
|
168
114
|
|
|
169
|
-
globalThis.describe("AppSync_SdlDecorate.
|
|
170
|
-
globalThis.test("stamps
|
|
171
|
-
let sdl = AppSync_SdlDecorate$ReventlessAws.
|
|
172
|
-
globalThis.expect(sdl).toContain("
|
|
115
|
+
globalThis.describe("AppSync_SdlDecorate.stampCanonicalTypes", () => {
|
|
116
|
+
globalThis.test("stamps shared object types, the Node interface, and the CommandResult union", () => {
|
|
117
|
+
let sdl = AppSync_SdlDecorate$ReventlessAws.stampCanonicalTypes("interface Node {\n id: ID!\n}\n\ntype PageInfo {\n hasNextPage: Boolean!\n}\n\nunion CommandResult = CommandAccepted | CommandRejected | CommandPending\n\ntype CommandAccepted {\n msgId: ID!\n}");
|
|
118
|
+
globalThis.expect(sdl).toContain("interface Node @canonical {");
|
|
119
|
+
globalThis.expect(sdl).toContain("type PageInfo @canonical {");
|
|
120
|
+
globalThis.expect(sdl).toContain("union CommandResult @canonical = CommandAccepted");
|
|
121
|
+
globalThis.expect(sdl).toContain("type CommandAccepted @canonical {");
|
|
173
122
|
});
|
|
174
|
-
globalThis.test("
|
|
175
|
-
let sdl = AppSync_SdlDecorate$ReventlessAws.stampSharedIamTypes("type
|
|
176
|
-
globalThis.expect(sdl).toContain("type
|
|
123
|
+
globalThis.test("composes after stampSharedIamTypes and leaves other types alone", () => {
|
|
124
|
+
let sdl = AppSync_SdlDecorate$ReventlessAws.stampCanonicalTypes(AppSync_SdlDecorate$ReventlessAws.stampSharedIamTypes("type PageInfo {\n hasNextPage: Boolean!\n}\n\ntype Product {\n id: ID!\n}"));
|
|
125
|
+
globalThis.expect(sdl).toContain("type PageInfo @aws_cognito_user_pools @aws_iam @canonical {");
|
|
126
|
+
globalThis.expect(sdl).toContain("type Product {");
|
|
127
|
+
globalThis.expect(sdl.includes("type Product @canonical")).toBe(false);
|
|
128
|
+
});
|
|
129
|
+
globalThis.test("is idempotent", () => {
|
|
130
|
+
let once = AppSync_SdlDecorate$ReventlessAws.stampCanonicalTypes("type PageInfo {\n x: Int\n}");
|
|
131
|
+
globalThis.expect(AppSync_SdlDecorate$ReventlessAws.stampCanonicalTypes(once)).toBe(once);
|
|
177
132
|
});
|
|
178
133
|
});
|
|
179
134
|
|
package/tests/MCP_LambdaTest.res
CHANGED
|
@@ -7,9 +7,8 @@ describe("MCP_Lambda.generateAdminConfig", () => {
|
|
|
7
7
|
~serverVersion="1.0.0",
|
|
8
8
|
~cloner=false,
|
|
9
9
|
)
|
|
10
|
-
// Admin has Plugin_Activate, Plugin_Deactivate, Plugin_Retire
|
|
11
|
-
|
|
12
|
-
expect(config.tools)->toHaveLength(5)
|
|
10
|
+
// Admin has Plugin_Activate, Plugin_Deactivate, Plugin_Retire
|
|
11
|
+
expect(config.tools)->toHaveLength(3)
|
|
13
12
|
})
|
|
14
13
|
|
|
15
14
|
testSync("produces additional clone tool when cloner=true", () => {
|
|
@@ -18,9 +17,8 @@ describe("MCP_Lambda.generateAdminConfig", () => {
|
|
|
18
17
|
~serverVersion="1.0.0",
|
|
19
18
|
~cloner=true,
|
|
20
19
|
)
|
|
21
|
-
// Plugin_Activate, Plugin_Deactivate, Plugin_Retire,
|
|
22
|
-
|
|
23
|
-
expect(config.tools)->toHaveLength(6)
|
|
20
|
+
// Plugin_Activate, Plugin_Deactivate, Plugin_Retire, Clone
|
|
21
|
+
expect(config.tools)->toHaveLength(4)
|
|
24
22
|
})
|
|
25
23
|
|
|
26
24
|
testSync("produces resources from admin query entries", () => {
|
|
@@ -6,11 +6,11 @@ import * as MCP_Lambda$ReventlessAws from "../src/adapter/Mcp/MCP_Lambda.res.mjs
|
|
|
6
6
|
globalThis.describe("MCP_Lambda.generateAdminConfig", () => {
|
|
7
7
|
globalThis.test("produces tools from admin mutation entries (no cloner)", () => {
|
|
8
8
|
let config = MCP_Lambda$ReventlessAws.generateAdminConfig("test", "1.0.0", false, undefined, undefined);
|
|
9
|
-
globalThis.expect(config.tools).toHaveLength(
|
|
9
|
+
globalThis.expect(config.tools).toHaveLength(3);
|
|
10
10
|
});
|
|
11
11
|
globalThis.test("produces additional clone tool when cloner=true", () => {
|
|
12
12
|
let config = MCP_Lambda$ReventlessAws.generateAdminConfig("test", "1.0.0", true, undefined, undefined);
|
|
13
|
-
globalThis.expect(config.tools).toHaveLength(
|
|
13
|
+
globalThis.expect(config.tools).toHaveLength(4);
|
|
14
14
|
});
|
|
15
15
|
globalThis.test("produces resources from admin query entries", () => {
|
|
16
16
|
let config = MCP_Lambda$ReventlessAws.generateAdminConfig("test", "1.0.0", undefined, undefined, undefined);
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/** Pulumi dynamic resource whose sole purpose is to deregister a plugin's
|
|
2
|
-
API-schema fragment on `pulumi destroy` (final retirement of the plugin
|
|
3
|
-
stack). `create` / `update` are no-ops — registration is done by
|
|
4
|
-
`Platform.registerFragmentViaApi` at deploy time; only `delete` matters,
|
|
5
|
-
and it calls `Platform_DeregisterApiFragment` on the platform's Platform API
|
|
6
|
-
(SigV4), whose event triggers the platform-side reactive single writer (2e)
|
|
7
|
-
to re-stitch the schema WITHOUT this plugin's fields.
|
|
8
|
-
|
|
9
|
-
Serialization: Pulumi serialises the provider object (and its captured import
|
|
10
|
-
closure) into stack state, so it must NOT statically capture the AWS SDK —
|
|
11
|
-
that breaks Pulumi's closure serialiser (the CJS/ESM dual-export confusion
|
|
12
|
-
documented in AppSync_Resolver_Retrying). The delete handler therefore
|
|
13
|
-
DYNAMICALLY imports the signed caller at invocation time; only the string
|
|
14
|
-
specifier lands in state.
|
|
15
|
-
|
|
16
|
-
Version supersession must NOT deregister: `diff` never reports changes/replaces
|
|
17
|
-
(pluginId, endpoint, and region are all version-stable), so a version bump is
|
|
18
|
-
an in-place no-op and the `delete` handler fires only on a genuine stack
|
|
19
|
-
destroy — the "final retirement, resolvers gone" trigger. */
|
|
20
|
-
|
|
21
|
-
let log = ReventlessCore.Logger.fromEnv()
|
|
22
|
-
|
|
23
|
-
type providerInputs = {
|
|
24
|
-
pluginId: string,
|
|
25
|
-
endpoint: string,
|
|
26
|
-
region: string,
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Pulumi passes OUTPUTS (possibly undefined) to delete/read handlers, so encode
|
|
30
|
-
// the carrier fields into the resource id — always available. "|" cannot appear
|
|
31
|
-
// in a plugin name, an AWS region, or an AppSync GraphQL endpoint URL.
|
|
32
|
-
let encodeId = (i: providerInputs): string => `${i.pluginId}|${i.region}|${i.endpoint}`
|
|
33
|
-
let decodeId = (id: string): option<providerInputs> =>
|
|
34
|
-
switch id->String.split("|") {
|
|
35
|
-
| [pluginId, region, endpoint] => Some({pluginId, region, endpoint})
|
|
36
|
-
| _ => None
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Dynamically import the signed caller and send Platform_DeregisterApiFragment.
|
|
40
|
-
// The `import(...)` keeps Util_AppSync_Caller (and its transitive @aws-sdk/@smithy
|
|
41
|
-
// deps) OUT of the serialised provider closure — only this string is captured.
|
|
42
|
-
// Positional call matches the compiled signature
|
|
43
|
-
// `sendMutation(endpoint, region, mutation, selection, variables)`.
|
|
44
|
-
let sendDeregister: (JSON.t, string, string) => promise<unit> = %raw(`
|
|
45
|
-
async function (variables, endpoint, region) {
|
|
46
|
-
const mod = await import("@reventlessdev/reventless-aws/src/util/Util_AppSync_Caller.res.mjs");
|
|
47
|
-
await mod.sendMutation(endpoint, region, "Platform_ApiFragmentRegistry_DeregisterApiFragment", "{ __typename }", variables);
|
|
48
|
-
}
|
|
49
|
-
`)
|
|
50
|
-
|
|
51
|
-
let errMessage: 'a => string = %raw(`(e) => (e && e.message) ? String(e.message) : String(e)`)
|
|
52
|
-
|
|
53
|
-
type createResult = {id: string, outs: providerInputs}
|
|
54
|
-
type updateResult = {outs: providerInputs}
|
|
55
|
-
type diffResult = {changes: bool, replaces: array<string>, deleteBeforeReplace: bool}
|
|
56
|
-
|
|
57
|
-
let create = async (inputs: providerInputs): createResult => {id: encodeId(inputs), outs: inputs}
|
|
58
|
-
|
|
59
|
-
let update = async (_id: string, _olds: providerInputs, news: providerInputs): updateResult => {
|
|
60
|
-
outs: news,
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Never change/replace — see the module note: keeps version bumps from deleting
|
|
64
|
-
// (and therefore deregistering) the fragment.
|
|
65
|
-
let diff = (_id: string, _olds: providerInputs, _news: providerInputs): diffResult => {
|
|
66
|
-
changes: false,
|
|
67
|
-
replaces: [],
|
|
68
|
-
deleteBeforeReplace: false,
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
let delete_ = async (id: string, props: providerInputs): unit => {
|
|
72
|
-
let carrier = switch decodeId(id) {
|
|
73
|
-
| Some(c) => c
|
|
74
|
-
| None => props
|
|
75
|
-
}
|
|
76
|
-
let variables =
|
|
77
|
-
Dict.fromArray([
|
|
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")),
|
|
82
|
-
("pluginId", JSON.Encode.string(carrier.pluginId)),
|
|
83
|
-
])->JSON.Encode.object
|
|
84
|
-
// Best-effort: on a full teardown the platform API may already be gone, and a
|
|
85
|
-
// destroy must not fail because deregistration could not reach it.
|
|
86
|
-
try {
|
|
87
|
-
await sendDeregister(variables, carrier.endpoint, carrier.region)
|
|
88
|
-
log.info(~comp="ApiFragmentDeregistration", `Deregistered API fragment for ${carrier.pluginId}`)
|
|
89
|
-
} catch {
|
|
90
|
-
| exn =>
|
|
91
|
-
log.warn(
|
|
92
|
-
~comp="ApiFragmentDeregistration",
|
|
93
|
-
`deregister for ${carrier.pluginId} failed (best-effort; platform may be gone): ${errMessage(
|
|
94
|
-
exn,
|
|
95
|
-
)}`,
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Provider as a plain JS object — no Pulumi Output captures; all state flows
|
|
101
|
-
// through inputs / id.
|
|
102
|
-
let provider = {
|
|
103
|
-
"create": create,
|
|
104
|
-
"update": update,
|
|
105
|
-
"delete": delete_,
|
|
106
|
-
"diff": diff,
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// ── Pulumi dynamic resource binding ──────────────────────────────────────────
|
|
110
|
-
|
|
111
|
-
type t
|
|
112
|
-
|
|
113
|
-
type constructorProps = {
|
|
114
|
-
pluginId: Pulumi.Input.t<string>,
|
|
115
|
-
endpoint: Pulumi.Input.t<string>,
|
|
116
|
-
region: Pulumi.Input.t<string>,
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// pulumi.dynamic.Resource constructor: (provider, name, props, opts). Explicit
|
|
120
|
-
// /index.js path because @pulumi/pulumi/dynamic is a directory import not
|
|
121
|
-
// resolvable in ESM mode.
|
|
122
|
-
@module("@pulumi/pulumi/dynamic/index.js") @new
|
|
123
|
-
external _newResource: ('provider, string, 'props, Pulumi.CustomResourceOptions.t) => t = "Resource"
|
|
124
|
-
|
|
125
|
-
let make = (
|
|
126
|
-
~name: string,
|
|
127
|
-
~pluginId: string,
|
|
128
|
-
~endpoint: Pulumi.Input.t<string>,
|
|
129
|
-
~region: string,
|
|
130
|
-
~opts: Pulumi.CustomResourceOptions.t={},
|
|
131
|
-
): t => {
|
|
132
|
-
let props: constructorProps = {
|
|
133
|
-
pluginId: pluginId->Pulumi.Input.make,
|
|
134
|
-
endpoint,
|
|
135
|
-
region: region->Pulumi.Input.make,
|
|
136
|
-
}
|
|
137
|
-
_newResource(provider, name, props, opts)
|
|
138
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
4
|
-
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
5
|
-
import * as IndexJs from "@pulumi/pulumi/dynamic/index.js";
|
|
6
|
-
|
|
7
|
-
let log = Logger$ReventlessCore.fromEnv();
|
|
8
|
-
|
|
9
|
-
function encodeId(i) {
|
|
10
|
-
return i.pluginId + `|` + i.region + `|` + i.endpoint;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function decodeId(id) {
|
|
14
|
-
let match = id.split("|");
|
|
15
|
-
if (match.length !== 3) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
let pluginId = match[0];
|
|
19
|
-
let region = match[1];
|
|
20
|
-
let endpoint = match[2];
|
|
21
|
-
return {
|
|
22
|
-
pluginId: pluginId,
|
|
23
|
-
endpoint: endpoint,
|
|
24
|
-
region: region
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
let sendDeregister = (async function (variables, endpoint, region) {
|
|
29
|
-
const mod = await import("@reventlessdev/reventless-aws/src/util/Util_AppSync_Caller.res.mjs");
|
|
30
|
-
await mod.sendMutation(endpoint, region, "Platform_ApiFragmentRegistry_DeregisterApiFragment", "{ __typename }", variables);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
let errMessage = ((e) => (e && e.message) ? String(e.message) : String(e));
|
|
34
|
-
|
|
35
|
-
async function create(inputs) {
|
|
36
|
-
return {
|
|
37
|
-
id: encodeId(inputs),
|
|
38
|
-
outs: inputs
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function update(_id, _olds, news) {
|
|
43
|
-
return {
|
|
44
|
-
outs: news
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function diff(_id, _olds, _news) {
|
|
49
|
-
return {
|
|
50
|
-
changes: false,
|
|
51
|
-
replaces: [],
|
|
52
|
-
deleteBeforeReplace: false
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function delete_(id, props) {
|
|
57
|
-
let c = decodeId(id);
|
|
58
|
-
let carrier = c !== undefined ? c : props;
|
|
59
|
-
let variables = Object.fromEntries([
|
|
60
|
-
[
|
|
61
|
-
"id",
|
|
62
|
-
"registry"
|
|
63
|
-
],
|
|
64
|
-
[
|
|
65
|
-
"pluginId",
|
|
66
|
-
carrier.pluginId
|
|
67
|
-
]
|
|
68
|
-
]);
|
|
69
|
-
try {
|
|
70
|
-
await sendDeregister(variables, carrier.endpoint, carrier.region);
|
|
71
|
-
return log.info("ApiFragmentDeregistration", undefined, `Deregistered API fragment for ` + carrier.pluginId);
|
|
72
|
-
} catch (raw_exn) {
|
|
73
|
-
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
74
|
-
return log.warn("ApiFragmentDeregistration", undefined, `deregister for ` + carrier.pluginId + ` failed (best-effort; platform may be gone): ` + errMessage(exn));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
let provider = {
|
|
79
|
-
create: create,
|
|
80
|
-
update: update,
|
|
81
|
-
delete: delete_,
|
|
82
|
-
diff: diff
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
function make(name, pluginId, endpoint, region, optsOpt) {
|
|
86
|
-
let opts = optsOpt !== undefined ? optsOpt : ({});
|
|
87
|
-
let props = {
|
|
88
|
-
pluginId: pluginId,
|
|
89
|
-
endpoint: endpoint,
|
|
90
|
-
region: region
|
|
91
|
-
};
|
|
92
|
-
return new IndexJs.Resource(provider, name, props, opts);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export {
|
|
96
|
-
log,
|
|
97
|
-
encodeId,
|
|
98
|
-
decodeId,
|
|
99
|
-
sendDeregister,
|
|
100
|
-
errMessage,
|
|
101
|
-
create,
|
|
102
|
-
update,
|
|
103
|
-
diff,
|
|
104
|
-
delete_,
|
|
105
|
-
provider,
|
|
106
|
-
make,
|
|
107
|
-
}
|
|
108
|
-
/* log Not a pure module */
|
|
@@ -1,79 +0,0 @@
|
|
|
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 Spec = ReventlessCore.ApiFragmentRegistrySpec
|
|
18
|
-
|
|
19
|
-
// Source is built EXPLICITLY rather than aliasing/including the spec. SideEffectHandler_Callback
|
|
20
|
-
// reads Source.{name,eventSchema,Id.schema} reflectively off this module's compiled export at
|
|
21
|
-
// Lambda cold start, but the spec uses these only at the TYPE level, so ReScript dead-shakes
|
|
22
|
-
// them: a bare `module Source = Spec` alias erases the whole binding to `let Source;`, and even
|
|
23
|
-
// the spec's own @@reventless.spec-injected `module Id` compiles to `let Id;` (undefined) — the
|
|
24
|
-
// cross-module runtime reflection is invisible to ReScript's optimiser (deploy-time is fine
|
|
25
|
-
// because Platform.res hand-wires these values). Both surfaced on deploy as "Cannot read
|
|
26
|
-
// properties of undefined (reading 'eventSchema' / 'schema')". Forwarding name/eventSchema (real
|
|
27
|
-
// spec exports) and re-binding Id (String — the singleton registry id, matching Platform.res's
|
|
28
|
-
// hand-wiring) materialises all three, while the type aliases keep full transparency for the
|
|
29
|
-
// pattern match below.
|
|
30
|
-
module Source = {
|
|
31
|
-
type event = Spec.event
|
|
32
|
-
type fragmentSnapshotEntry = Spec.fragmentSnapshotEntry
|
|
33
|
-
// `include` (not `module Id = Reventless.Id.String`) — a bare module alias compiles to
|
|
34
|
-
// `Id: undefined` in the Source record (Id is only type-projected via Source.Id.t), and
|
|
35
|
-
// SideEffectHandler_Callback's `Source.Id.schema` reflective read crashes at cold start.
|
|
36
|
-
// `include` materialises the Id module's runtime values into the record.
|
|
37
|
-
module Id = {
|
|
38
|
-
include Reventless.Id.String
|
|
39
|
-
}
|
|
40
|
-
let name = Spec.name
|
|
41
|
-
let eventSchema = Spec.eventSchema
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
let moduleUrl: string = %raw(`import.meta.url`)
|
|
45
|
-
|
|
46
|
-
type jsEntry = {pluginId: string, encoded: string, protocol: string, apiTarget: string}
|
|
47
|
-
|
|
48
|
-
let pushApiSchema: array<jsEntry> => promise<unit> = %raw(`
|
|
49
|
-
async function (snapshot) {
|
|
50
|
-
const mod = await import("@reventlessdev/reventless-aws/src/adapter/Runtime/ApiSchemaPush_Runtime.mjs");
|
|
51
|
-
await mod.pushApiSchema(snapshot);
|
|
52
|
-
}
|
|
53
|
-
`)
|
|
54
|
-
|
|
55
|
-
let execute = async (
|
|
56
|
-
_id: Source.Id.t,
|
|
57
|
-
_meta: Reventless.Message.meta,
|
|
58
|
-
event: Source.event,
|
|
59
|
-
_queryEngine: Reventless.QueryEngine.operations,
|
|
60
|
-
) =>
|
|
61
|
-
switch event {
|
|
62
|
-
| ApiSchemaComputed({snapshot}) =>
|
|
63
|
-
let jsSnapshot = snapshot->Array.map((e: Source.fragmentSnapshotEntry) => {
|
|
64
|
-
pluginId: e.pluginId,
|
|
65
|
-
encoded: e.encoded,
|
|
66
|
-
protocol: e.protocol,
|
|
67
|
-
apiTarget: switch e.apiTarget {
|
|
68
|
-
| Reventless.Plugin.Domain => "Domain"
|
|
69
|
-
| Reventless.Plugin.Platform => "Platform"
|
|
70
|
-
},
|
|
71
|
-
})
|
|
72
|
-
await pushApiSchema(jsSnapshot)
|
|
73
|
-
// Fact events are handled via the ApiSchemaComputed echo (which carries the
|
|
74
|
-
// consistent snapshot); the write-back and lifecycle facts are no-ops here.
|
|
75
|
-
| ApiFragmentRegistered(_)
|
|
76
|
-
| ApiFragmentUpdated(_)
|
|
77
|
-
| ApiFragmentDeregistered(_)
|
|
78
|
-
| ApiFragmentPushRecorded(_) => ()
|
|
79
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
-
|
|
3
|
-
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
4
|
-
import * as ApiFragmentRegistrySpec$ReventlessCore from "@reventlessdev/reventless-core/src/admin/ApiFragmentRegistry/ApiFragmentRegistrySpec.res.mjs";
|
|
5
|
-
|
|
6
|
-
let Id_schema = Id$Reventless.$$String.schema;
|
|
7
|
-
|
|
8
|
-
let Id_make = Id$Reventless.$$String.make;
|
|
9
|
-
|
|
10
|
-
let Id_makeFromString = Id$Reventless.$$String.makeFromString;
|
|
11
|
-
|
|
12
|
-
let Id_toString = Id$Reventless.$$String.toString;
|
|
13
|
-
|
|
14
|
-
let Id_cmp = Id$Reventless.$$String.cmp;
|
|
15
|
-
|
|
16
|
-
let Id = {
|
|
17
|
-
schema: Id_schema,
|
|
18
|
-
make: Id_make,
|
|
19
|
-
makeFromString: Id_makeFromString,
|
|
20
|
-
toString: Id_toString,
|
|
21
|
-
cmp: Id_cmp
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
let Source = {
|
|
25
|
-
Id: Id,
|
|
26
|
-
name: ApiFragmentRegistrySpec$ReventlessCore.name,
|
|
27
|
-
eventSchema: ApiFragmentRegistrySpec$ReventlessCore.eventSchema
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
let moduleUrl = import.meta.url;
|
|
31
|
-
|
|
32
|
-
let pushApiSchema = (async function (snapshot) {
|
|
33
|
-
const mod = await import("@reventlessdev/reventless-aws/src/adapter/Runtime/ApiSchemaPush_Runtime.mjs");
|
|
34
|
-
await mod.pushApiSchema(snapshot);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
async function execute(_id, _meta, event, _queryEngine) {
|
|
38
|
-
if (event.TAG !== "ApiSchemaComputed") {
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
let jsSnapshot = event.snapshot.map(e => {
|
|
42
|
-
let match = e.apiTarget;
|
|
43
|
-
let tmp;
|
|
44
|
-
tmp = match === "Domain" ? "Domain" : "Platform";
|
|
45
|
-
return {
|
|
46
|
-
pluginId: e.pluginId,
|
|
47
|
-
encoded: e.encoded,
|
|
48
|
-
protocol: e.protocol,
|
|
49
|
-
apiTarget: tmp
|
|
50
|
-
};
|
|
51
|
-
});
|
|
52
|
-
return await pushApiSchema(jsSnapshot);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
let Spec;
|
|
56
|
-
|
|
57
|
-
export {
|
|
58
|
-
Spec,
|
|
59
|
-
Source,
|
|
60
|
-
moduleUrl,
|
|
61
|
-
pushApiSchema,
|
|
62
|
-
execute,
|
|
63
|
-
}
|
|
64
|
-
/* moduleUrl Not a pure module */
|