@reventlessdev/reventless-aws 3.0.0-alpha.231 → 3.0.0-alpha.233
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 +23 -0
- package/package.json +7 -7
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res +137 -0
- package/src/adapter/Api/AppSyncEventsSigner_Ops.res.mjs +121 -0
- package/src/adapter/Api/Platform_AdminScan_Ops.res +136 -0
- package/src/adapter/Api/Platform_AdminScan_Ops.res.mjs +98 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +12 -90
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +11 -84
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +86 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +79 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda.res +9 -79
- package/src/adapter/Api/Platform_UIFragments_Lambda.res.mjs +7 -143
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res +48 -0
- package/src/adapter/Api/Platform_UIFragments_Lambda_Ops.res.mjs +63 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res +21 -80
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync.res.mjs +23 -68
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res +70 -0
- package/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs +68 -0
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res +1 -1
- package/src/adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs +1 -1
- package/src/adapter/Postgres/PgMigration_Builder.res +1 -1
- package/src/adapter/Postgres/PgMigration_Builder.res.mjs +1 -1
- package/src/adapter/Runtime/AutomationSliceEntryPoint.mjs +42 -126
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res +246 -0
- package/src/adapter/Runtime/AutomationSliceEntryPoint_Ops.res.mjs +145 -0
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res +27 -2
- package/src/adapter/Runtime/AutomationSliceRuntime_Builder_Single.res.mjs +10 -3
- package/src/adapter/Runtime/CounterEntryPoint.mjs +19 -73
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res +145 -0
- package/src/adapter/Runtime/CounterEntryPoint_Ops.res.mjs +129 -0
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res +16 -3
- package/src/adapter/Runtime/DcbCommandTopicEntryPoint_Ops.res.mjs +11 -6
- package/src/adapter/Runtime/EventCollectorEntryPoint.mjs +52 -510
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res +816 -0
- package/src/adapter/Runtime/EventCollectorEntryPoint_Ops.res.mjs +651 -0
- package/src/adapter/Runtime/EventMapperEntryPoint.mjs +23 -67
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res +126 -0
- package/src/adapter/Runtime/EventMapperEntryPoint_Ops.res.mjs +83 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint.mjs +16 -63
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res +150 -0
- package/src/adapter/Runtime/ExtensionPointEntryPoint_Ops.res.mjs +111 -0
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res +96 -0
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs +70 -0
- package/src/adapter/Runtime/PgMigrationEntryPoint.res +54 -0
- package/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs +37 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint.mjs +20 -55
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res +155 -0
- package/src/adapter/Runtime/PgQueryResolverEntryPoint_Ops.res.mjs +94 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res +80 -0
- package/src/adapter/Runtime/ProjectionEntryPoint_Ops.res.mjs +39 -0
- package/src/adapter/Runtime/QueryDbEntryPoint_Ops.res +4 -4
- package/src/adapter/Runtime/ReadModelEntryPoint.mjs +34 -153
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res +114 -0
- package/src/adapter/Runtime/ReadModelEntryPoint_Ops.res.mjs +83 -0
- package/src/adapter/Runtime/SideEffectEntryPoint.mjs +23 -64
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res +71 -0
- package/src/adapter/Runtime/SideEffectEntryPoint_Ops.res.mjs +57 -0
- package/src/adapter/Runtime/StateViewSliceEntryPoint.mjs +34 -165
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res +206 -0
- package/src/adapter/Runtime/StateViewSliceEntryPoint_Ops.res.mjs +105 -0
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res +156 -0
- package/src/adapter/Runtime/StreamRoutedEntryPoint_Ops.res.mjs +123 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint.mjs +18 -138
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res +198 -0
- package/src/adapter/Runtime/TaskBucketEntryPoint_Ops.res.mjs +155 -0
- package/src/adapter/StateTopic/StateTopic_AppSync.res +12 -170
- package/src/adapter/StateTopic/StateTopic_AppSync.res.mjs +7 -167
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res +208 -0
- package/src/adapter/StateTopic/StateTopic_AppSync_Ops.res.mjs +183 -0
- package/src/components/AutomationSlice_Builder.res +2 -0
- package/src/components/AutomationSlice_Builder.res.mjs +1 -1
- package/src/components/OutboundTranslationSlice_Builder.res +1 -0
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +1 -1
- package/tests/AppSyncEventsSigner_OpsTest.res +75 -0
- package/tests/AppSyncEventsSigner_OpsTest.res.mjs +44 -0
- package/tests/AppSyncEventsSigner_ParityFixture.mjs +36 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res +165 -0
- package/tests/AutomationSliceEntryPoint_OpsTest.res.mjs +280 -0
- package/tests/CounterEntryPoint_OpsTest.res +115 -0
- package/tests/CounterEntryPoint_OpsTest.res.mjs +150 -0
- package/tests/EventCollectorEntryPoint_OpsTest.res +216 -0
- package/tests/EventCollectorEntryPoint_OpsTest.res.mjs +339 -0
- package/tests/EventMapperEntryPoint_OpsTest.res +103 -0
- package/tests/EventMapperEntryPoint_OpsTest.res.mjs +120 -0
- package/tests/ExtensionPointEntryPoint_OpsTest.res +57 -0
- package/tests/ExtensionPointEntryPoint_OpsTest.res.mjs +54 -0
- package/tests/PgMigrationEntryPoint_IntegrationTest.res +22 -31
- package/tests/PgMigrationEntryPoint_IntegrationTest.res.mjs +20 -23
- package/tests/PgQueryResolverEntryPoint_OpsTest.res +38 -0
- package/tests/PgQueryResolverEntryPoint_OpsTest.res.mjs +31 -0
- package/tests/Platform_AdminScan_OpsTest.res +80 -0
- package/tests/Platform_AdminScan_OpsTest.res.mjs +127 -0
- package/tests/ReadModelEntryPoint_OpsTest.res +141 -0
- package/tests/ReadModelEntryPoint_OpsTest.res.mjs +267 -0
- package/tests/SideEffectEntryPoint_OpsTest.res +79 -0
- package/tests/SideEffectEntryPoint_OpsTest.res.mjs +105 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res +167 -0
- package/tests/StateViewSliceEntryPoint_OpsTest.res.mjs +296 -0
- package/tests/TaskBucketEntryPoint_OpsTest.res +117 -0
- package/tests/TaskBucketEntryPoint_OpsTest.res.mjs +165 -0
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res +42 -13
- package/tests/integration/StateViewSliceEntryPoint_IntegrationTest.res.mjs +29 -6
- package/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs +0 -57
- package/src/adapter/Runtime/PgMigrationEntryPoint.mjs +0 -40
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as Platform_AdminScan_Ops$ReventlessAws from "./Platform_AdminScan_Ops.res.mjs";
|
|
6
|
+
|
|
7
|
+
function str(item, key) {
|
|
8
|
+
return Stdlib_Option.flatMap(item[key], Stdlib_JSON.Decode.string);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function toEntry(item, name) {
|
|
12
|
+
let match = str(item, "pluginId");
|
|
13
|
+
let match$1 = str(item, "remoteEntryUrl");
|
|
14
|
+
if (match !== undefined && match$1 !== undefined && match$1 !== "") {
|
|
15
|
+
return Object.fromEntries([
|
|
16
|
+
[
|
|
17
|
+
"pluginId",
|
|
18
|
+
name
|
|
19
|
+
],
|
|
20
|
+
[
|
|
21
|
+
"remoteEntryUrl",
|
|
22
|
+
match$1
|
|
23
|
+
],
|
|
24
|
+
[
|
|
25
|
+
"panels",
|
|
26
|
+
Stdlib_Option.getOr(item["panels"], [])
|
|
27
|
+
],
|
|
28
|
+
[
|
|
29
|
+
"pages",
|
|
30
|
+
Stdlib_Option.getOr(item["pages"], [])
|
|
31
|
+
],
|
|
32
|
+
[
|
|
33
|
+
"registeredAt",
|
|
34
|
+
Stdlib_Option.getOr(item["registeredAt"], "")
|
|
35
|
+
],
|
|
36
|
+
[
|
|
37
|
+
"updatedAt",
|
|
38
|
+
Stdlib_Option.getOr(item["updatedAt"], "")
|
|
39
|
+
]
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function handler(_event) {
|
|
45
|
+
let table = process.env["UI_FRAGMENT_RM_TABLE"];
|
|
46
|
+
if (table !== undefined) {
|
|
47
|
+
if (table === "") {
|
|
48
|
+
console.error("Platform_UIFragments: UI_FRAGMENT_RM_TABLE env var not set");
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
let items = await Platform_AdminScan_Ops$ReventlessAws.scanAll(table, undefined, undefined, undefined);
|
|
52
|
+
return Platform_AdminScan_Ops$ReventlessAws.latestByName(items, item => str(item, "pluginId"), toEntry);
|
|
53
|
+
}
|
|
54
|
+
console.error("Platform_UIFragments: UI_FRAGMENT_RM_TABLE env var not set");
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
str,
|
|
60
|
+
toEntry,
|
|
61
|
+
handler,
|
|
62
|
+
}
|
|
63
|
+
/* Platform_AdminScan_Ops-ReventlessAws Not a pure module */
|
|
@@ -15,78 +15,13 @@
|
|
|
15
15
|
|
|
16
16
|
open PulumiAws
|
|
17
17
|
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
let
|
|
24
|
-
|
|
25
|
-
// allow only [A-Za-z0-9-]. Today's event-log displayNames are bare
|
|
26
|
-
// PascalCase identifiers (Catalog, Ordering, Plugin) so the rule is a no-op
|
|
27
|
-
// in practice — kept in lock-step prophylactically so a future displayName
|
|
28
|
-
// carrying `@/./:` etc. doesn't hit the same silent-drop the Plugins admin
|
|
29
|
-
// RM did when StateTopic still only normalised underscores.
|
|
30
|
-
let channelName = topicName->String.replaceRegExp(%re("/[^A-Za-z0-9-]/g"), "-")
|
|
31
|
-
`
|
|
32
|
-
import { createHmac, createHash } from "node:crypto";
|
|
33
|
-
|
|
34
|
-
const CHANNEL = "/default/${channelName}";
|
|
35
|
-
const APPSYNC_ENDPOINT = process.env.APPSYNC_ENDPOINT;
|
|
36
|
-
const AWS_REGION = process.env.AWS_REGION ?? "eu-west-1";
|
|
37
|
-
|
|
38
|
-
function sha256hex(data) {
|
|
39
|
-
return createHash("sha256").update(typeof data === "string" ? data : JSON.stringify(data)).digest("hex");
|
|
40
|
-
}
|
|
41
|
-
function hmacBuf(key, data) {
|
|
42
|
-
return createHmac("sha256", key).update(data).digest();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async function signedHeaders(host, path, body) {
|
|
46
|
-
const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
|
47
|
-
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
48
|
-
const sessionToken = process.env.AWS_SESSION_TOKEN;
|
|
49
|
-
const now = new Date();
|
|
50
|
-
const amzDate = now.toISOString().replace(/[:\\-]|\\..../g, "").slice(0, 15) + "Z";
|
|
51
|
-
const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, "");
|
|
52
|
-
const headers = { host, "x-amz-date": amzDate, ...(sessionToken ? { "x-amz-security-token": sessionToken } : {}) };
|
|
53
|
-
const canonH = Object.entries(headers).sort(([a],[b]) => a.localeCompare(b)).map(([k,v]) => \`\${k}:\${v}\\n\`).join("");
|
|
54
|
-
const signH = Object.keys(headers).sort().join(";");
|
|
55
|
-
const cr = ["POST", path, "", canonH, signH, sha256hex(body)].join("\\n");
|
|
56
|
-
const scope = \`\${dateStamp}/\${AWS_REGION}/appsync/aws4_request\`;
|
|
57
|
-
const sts = ["AWS4-HMAC-SHA256", amzDate, scope, sha256hex(cr)].join("\\n");
|
|
58
|
-
const kDate = hmacBuf("AWS4" + secretAccessKey, dateStamp);
|
|
59
|
-
const kSigning = hmacBuf(hmacBuf(hmacBuf(kDate, AWS_REGION), "appsync"), "aws4_request");
|
|
60
|
-
const sig = createHmac("sha256", kSigning).update(sts).digest("hex");
|
|
61
|
-
return { ...headers, Authorization: \`AWS4-HMAC-SHA256 Credential=\${accessKeyId}/\${scope}, SignedHeaders=\${signH}, Signature=\${sig}\` };
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export async function handler(event) {
|
|
65
|
-
const url = new URL(APPSYNC_ENDPOINT);
|
|
66
|
-
for (const record of event.Records) {
|
|
67
|
-
let body;
|
|
68
|
-
try {
|
|
69
|
-
body = JSON.parse(record.body);
|
|
70
|
-
} catch (e) {
|
|
71
|
-
console.error("EventLogSubscription: failed to parse record body", record.body, e);
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
const payload = { position: body.position, eventType: body.eventType, payload: body.data };
|
|
75
|
-
const reqBody = JSON.stringify({ id: record.messageId, channel: CHANNEL, events: [JSON.stringify(payload)] });
|
|
76
|
-
const auth = await signedHeaders(url.hostname, "/event", reqBody);
|
|
77
|
-
const res = await fetch(APPSYNC_ENDPOINT + "/event", {
|
|
78
|
-
method: "POST",
|
|
79
|
-
headers: { accept: "application/json, text/javascript", "content-encoding": "amz-1.0", "content-type": "application/json; charset=UTF-8", ...auth },
|
|
80
|
-
body: reqBody,
|
|
81
|
-
});
|
|
82
|
-
if (!res.ok) {
|
|
83
|
-
const txt = await res.text();
|
|
84
|
-
console.error("EventLogSubscription publish failed:", res.status, txt);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
`
|
|
89
|
-
}
|
|
18
|
+
// AppSync Events channel segments allow only [A-Za-z0-9-]. Today's event-log
|
|
19
|
+
// displayNames are bare PascalCase identifiers (Catalog, Ordering, Plugin) so
|
|
20
|
+
// the rule is a no-op in practice — kept prophylactically so a future displayName
|
|
21
|
+
// carrying `@/./:` etc. doesn't hit a silent-drop. Mirrors
|
|
22
|
+
// AppSyncEventsSigner_Ops.pathSegment (the runtime handler's equivalent).
|
|
23
|
+
let channelNameOf = (topicName: string): string =>
|
|
24
|
+
topicName->String.replaceRegExp(%re("/[^A-Za-z0-9-]/g"), "-")
|
|
90
25
|
|
|
91
26
|
// ── Deploy-time resource builder ──────────────────────────────────────────────
|
|
92
27
|
|
|
@@ -224,15 +159,18 @@ let make = (
|
|
|
224
159
|
)
|
|
225
160
|
})
|
|
226
161
|
|
|
227
|
-
// Lambda function — processes SQS records (SNS events) → AppSync Events channel
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
162
|
+
// Lambda function — processes SQS records (SNS events) → AppSync Events channel.
|
|
163
|
+
// Bundle reventless-aws (the compiled `_Ops` handler + its node:crypto signer
|
|
164
|
+
// live inside it) and re-export its `handler`; buildCodeArchive ships the ESM
|
|
165
|
+
// resolve-hook so `@rescript/runtime` resolves from the layer.
|
|
166
|
+
let channelName = channelNameOf(topicName)
|
|
167
|
+
let packageDirs = Dict.fromArray([
|
|
168
|
+
("@reventlessdev/reventless-aws", Util_Bundle.resolvePackageRoot("@reventlessdev/reventless-aws")),
|
|
169
|
+
])
|
|
170
|
+
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
171
|
+
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs",
|
|
172
|
+
~packageDirs,
|
|
233
173
|
)
|
|
234
|
-
let code = Pulumi.Archive.assetArchive(archiveContents)
|
|
235
|
-
let sourceCodeHash = Util_Bundle.hashString(handlerCode)
|
|
236
174
|
|
|
237
175
|
let layers =
|
|
238
176
|
Lambda.reventlessLayerArn
|
|
@@ -259,6 +197,9 @@ let make = (
|
|
|
259
197
|
Lambda.Function.variables: Dict.fromArray([
|
|
260
198
|
("Environment", Pulumi.Pulumi.getStackName()->Pulumi.Input.make),
|
|
261
199
|
("APPSYNC_ENDPOINT", appsyncEndpoint->Pulumi.Output.asInput),
|
|
200
|
+
("EVENT_LOG_CHANNEL", channelName->Pulumi.Input.make),
|
|
201
|
+
("NODE_OPTIONS", Util_Bundle.esmLoaderNodeOptions->Pulumi.Input.make),
|
|
202
|
+
("ESM_FALLBACK_DIRS", Util_Bundle.esmFallbackDirs->Pulumi.Input.make),
|
|
262
203
|
]),
|
|
263
204
|
}: Lambda.Function.functionEnvironment
|
|
264
205
|
)->Pulumi.Input.make,
|
|
@@ -16,66 +16,8 @@ import * as AppSync_EventsApi$ReventlessAws from "../Api/AppSync_EventsApi.res.m
|
|
|
16
16
|
import * as Util_DeadLetterQueue$ReventlessAws from "../../util/Util_DeadLetterQueue.res.mjs";
|
|
17
17
|
import * as Util_EventSourceMapping$ReventlessAws from "../../util/Util_EventSourceMapping.res.mjs";
|
|
18
18
|
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
return `
|
|
22
|
-
import { createHmac, createHash } from "node:crypto";
|
|
23
|
-
|
|
24
|
-
const CHANNEL = "/default/` + channelName + `";
|
|
25
|
-
const APPSYNC_ENDPOINT = process.env.APPSYNC_ENDPOINT;
|
|
26
|
-
const AWS_REGION = process.env.AWS_REGION ?? "eu-west-1";
|
|
27
|
-
|
|
28
|
-
function sha256hex(data) {
|
|
29
|
-
return createHash("sha256").update(typeof data === "string" ? data : JSON.stringify(data)).digest("hex");
|
|
30
|
-
}
|
|
31
|
-
function hmacBuf(key, data) {
|
|
32
|
-
return createHmac("sha256", key).update(data).digest();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function signedHeaders(host, path, body) {
|
|
36
|
-
const accessKeyId = process.env.AWS_ACCESS_KEY_ID;
|
|
37
|
-
const secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
38
|
-
const sessionToken = process.env.AWS_SESSION_TOKEN;
|
|
39
|
-
const now = new Date();
|
|
40
|
-
const amzDate = now.toISOString().replace(/[:\\-]|\\..../g, "").slice(0, 15) + "Z";
|
|
41
|
-
const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, "");
|
|
42
|
-
const headers = { host, "x-amz-date": amzDate, ...(sessionToken ? { "x-amz-security-token": sessionToken } : {}) };
|
|
43
|
-
const canonH = Object.entries(headers).sort(([a],[b]) => a.localeCompare(b)).map(([k,v]) => \`\${k}:\${v}\\n\`).join("");
|
|
44
|
-
const signH = Object.keys(headers).sort().join(";");
|
|
45
|
-
const cr = ["POST", path, "", canonH, signH, sha256hex(body)].join("\\n");
|
|
46
|
-
const scope = \`\${dateStamp}/\${AWS_REGION}/appsync/aws4_request\`;
|
|
47
|
-
const sts = ["AWS4-HMAC-SHA256", amzDate, scope, sha256hex(cr)].join("\\n");
|
|
48
|
-
const kDate = hmacBuf("AWS4" + secretAccessKey, dateStamp);
|
|
49
|
-
const kSigning = hmacBuf(hmacBuf(hmacBuf(kDate, AWS_REGION), "appsync"), "aws4_request");
|
|
50
|
-
const sig = createHmac("sha256", kSigning).update(sts).digest("hex");
|
|
51
|
-
return { ...headers, Authorization: \`AWS4-HMAC-SHA256 Credential=\${accessKeyId}/\${scope}, SignedHeaders=\${signH}, Signature=\${sig}\` };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export async function handler(event) {
|
|
55
|
-
const url = new URL(APPSYNC_ENDPOINT);
|
|
56
|
-
for (const record of event.Records) {
|
|
57
|
-
let body;
|
|
58
|
-
try {
|
|
59
|
-
body = JSON.parse(record.body);
|
|
60
|
-
} catch (e) {
|
|
61
|
-
console.error("EventLogSubscription: failed to parse record body", record.body, e);
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
const payload = { position: body.position, eventType: body.eventType, payload: body.data };
|
|
65
|
-
const reqBody = JSON.stringify({ id: record.messageId, channel: CHANNEL, events: [JSON.stringify(payload)] });
|
|
66
|
-
const auth = await signedHeaders(url.hostname, "/event", reqBody);
|
|
67
|
-
const res = await fetch(APPSYNC_ENDPOINT + "/event", {
|
|
68
|
-
method: "POST",
|
|
69
|
-
headers: { accept: "application/json, text/javascript", "content-encoding": "amz-1.0", "content-type": "application/json; charset=UTF-8", ...auth },
|
|
70
|
-
body: reqBody,
|
|
71
|
-
});
|
|
72
|
-
if (!res.ok) {
|
|
73
|
-
const txt = await res.text();
|
|
74
|
-
console.error("EventLogSubscription publish failed:", res.status, txt);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
`;
|
|
19
|
+
function channelNameOf(topicName) {
|
|
20
|
+
return topicName.replace(/[^A-Za-z0-9-]/g, "-");
|
|
79
21
|
}
|
|
80
22
|
|
|
81
23
|
function make(name, topicName, eventTopicOutputs, eventsApi, opts) {
|
|
@@ -142,17 +84,18 @@ function make(name, topicName, eventTopicOutputs, eventsApi, opts) {
|
|
|
142
84
|
role: lambdaRole.id
|
|
143
85
|
}, opts);
|
|
144
86
|
});
|
|
145
|
-
let
|
|
146
|
-
let
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
87
|
+
let channelName = channelNameOf(topicName);
|
|
88
|
+
let packageDirs = Object.fromEntries([[
|
|
89
|
+
"@reventlessdev/reventless-aws",
|
|
90
|
+
Util_Bundle$ReventlessAws.resolvePackageRoot("@reventlessdev/reventless-aws")
|
|
91
|
+
]]);
|
|
92
|
+
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/EventLogSubscription/EventLogSubscription_AppSync_Ops.res.mjs", packageDirs, undefined);
|
|
150
93
|
let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
|
|
151
94
|
let appsyncEndpoint = AppSync_EventsApi$ReventlessAws.httpEndpoint(eventsApi);
|
|
152
95
|
let lambda = new (Aws.lambda.Function)(name + "EventLogSubscriber", {
|
|
153
96
|
handler: "index.handler",
|
|
154
97
|
runtime: "nodejs22.x",
|
|
155
|
-
code: code,
|
|
98
|
+
code: match.code,
|
|
156
99
|
role: lambdaRole.arn,
|
|
157
100
|
memorySize: 128,
|
|
158
101
|
timeout: 30,
|
|
@@ -167,17 +110,29 @@ function make(name, topicName, eventTopicOutputs, eventsApi, opts) {
|
|
|
167
110
|
[
|
|
168
111
|
"APPSYNC_ENDPOINT",
|
|
169
112
|
appsyncEndpoint
|
|
113
|
+
],
|
|
114
|
+
[
|
|
115
|
+
"EVENT_LOG_CHANNEL",
|
|
116
|
+
channelName
|
|
117
|
+
],
|
|
118
|
+
[
|
|
119
|
+
"NODE_OPTIONS",
|
|
120
|
+
Util_Bundle$ReventlessAws.esmLoaderNodeOptions
|
|
121
|
+
],
|
|
122
|
+
[
|
|
123
|
+
"ESM_FALLBACK_DIRS",
|
|
124
|
+
Util_Bundle$ReventlessAws.esmFallbackDirs
|
|
170
125
|
]
|
|
171
126
|
])
|
|
172
127
|
},
|
|
173
|
-
sourceCodeHash: sourceCodeHash
|
|
128
|
+
sourceCodeHash: match.sourceCodeHash
|
|
174
129
|
}, opts);
|
|
175
130
|
let lambdaOutput = Pulumi.output(lambda);
|
|
176
131
|
Util_EventSourceMapping$ReventlessAws.subscribeSqs(lambdaOutput, name + "EventLogSubEventSourceMapping", queue, undefined, AWS_Tags$ReventlessAws.make(name + "EventLogSubEventSourceMapping", EventTopic$ReventlessCore.componentType, "EventSourceMapping", undefined, name, undefined, undefined, undefined), opts);
|
|
177
132
|
}
|
|
178
133
|
|
|
179
134
|
export {
|
|
180
|
-
|
|
135
|
+
channelNameOf,
|
|
181
136
|
make,
|
|
182
137
|
}
|
|
183
138
|
/* @pulumi/aws Not a pure module */
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Runtime handler for the EventLog → AppSync Events subscription relay —
|
|
2
|
+
// compiled, type-checked ReScript (replaces the inline JS `makeHandlerCode`
|
|
3
|
+
// string in EventLogSubscription_AppSync.res). Runtime-pure and SDK-free: it
|
|
4
|
+
// signs with node:crypto via AppSyncEventsSigner_Ops (see the parity test).
|
|
5
|
+
//
|
|
6
|
+
// Triggered by SQS (SNS event-topic fan-in). For each record it parses the SNS
|
|
7
|
+
// body, wraps it as a `{position, eventType, payload}` event, and publishes it
|
|
8
|
+
// to the fixed plugin event-log channel on the AppSync Events API.
|
|
9
|
+
|
|
10
|
+
@val @scope("process") external processEnv: dict<string> = "env"
|
|
11
|
+
|
|
12
|
+
// Baked at deploy time: the normalised channel segment for this event log.
|
|
13
|
+
let channel = "/default/" ++ processEnv->Dict.get("EVENT_LOG_CHANNEL")->Option.getOr("")
|
|
14
|
+
let endpoint = processEnv->Dict.get("APPSYNC_ENDPOINT")->Option.getOr("")
|
|
15
|
+
|
|
16
|
+
// SQS Lambda event (only the fields this handler reads).
|
|
17
|
+
type sqsRecord = {body: string, messageId: string}
|
|
18
|
+
type sqsEvent = {@as("Records") records: array<sqsRecord>}
|
|
19
|
+
|
|
20
|
+
let processRecord = async (
|
|
21
|
+
~record: sqsRecord,
|
|
22
|
+
~region: string,
|
|
23
|
+
~creds: AppSyncEventsSigner_Ops.creds,
|
|
24
|
+
): unit => {
|
|
25
|
+
let parsed = try Some(record.body->JSON.parseOrThrow) catch {
|
|
26
|
+
| _ => None
|
|
27
|
+
}
|
|
28
|
+
switch parsed->Option.flatMap(JSON.Decode.object) {
|
|
29
|
+
| None => Console.error2("EventLogSubscription: failed to parse record body", record.body)
|
|
30
|
+
| Some(body) =>
|
|
31
|
+
// Wrap as {position, eventType, payload}; omit fields absent on the source
|
|
32
|
+
// body (matches the former JS, where `undefined` fields drop from JSON).
|
|
33
|
+
let payload = Dict.make()
|
|
34
|
+
body->Dict.get("position")->Option.forEach(v => payload->Dict.set("position", v))
|
|
35
|
+
body->Dict.get("eventType")->Option.forEach(v => payload->Dict.set("eventType", v))
|
|
36
|
+
body->Dict.get("data")->Option.forEach(v => payload->Dict.set("payload", v))
|
|
37
|
+
let reqBody =
|
|
38
|
+
Dict.fromArray([
|
|
39
|
+
("id", JSON.Encode.string(record.messageId)),
|
|
40
|
+
("channel", JSON.Encode.string(channel)),
|
|
41
|
+
("events", JSON.Encode.array([JSON.Encode.string(payload->JSON.Encode.object->JSON.stringify)])),
|
|
42
|
+
])
|
|
43
|
+
->JSON.Encode.object
|
|
44
|
+
->JSON.stringify
|
|
45
|
+
let res = await AppSyncEventsSigner_Ops.postEvent(
|
|
46
|
+
~endpoint,
|
|
47
|
+
~region,
|
|
48
|
+
~isoNow=Date.make()->Date.toISOString,
|
|
49
|
+
~creds,
|
|
50
|
+
~body=reqBody,
|
|
51
|
+
)
|
|
52
|
+
if !(res->AppSyncEventsSigner_Ops.responseOk) {
|
|
53
|
+
let txt = await res->AppSyncEventsSigner_Ops.responseText
|
|
54
|
+
Console.error3(
|
|
55
|
+
"EventLogSubscription publish failed:",
|
|
56
|
+
res->AppSyncEventsSigner_Ops.responseStatus->Int.toString,
|
|
57
|
+
txt,
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let handler = async (event: sqsEvent): unit => {
|
|
64
|
+
let region = AppSyncEventsSigner_Ops.region()
|
|
65
|
+
let creds = AppSyncEventsSigner_Ops.envCreds()
|
|
66
|
+
// Sequential, mirroring the former for-await loop (order + at-least-once).
|
|
67
|
+
await event.records->Array.reduce(Promise.resolve(), (acc, record) =>
|
|
68
|
+
acc->Promise.then(_ => processRecord(~record, ~region, ~creds))
|
|
69
|
+
)
|
|
70
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as AppSyncEventsSigner_Ops$ReventlessAws from "../Api/AppSyncEventsSigner_Ops.res.mjs";
|
|
7
|
+
|
|
8
|
+
let channel = "/default/" + Stdlib_Option.getOr(process.env["EVENT_LOG_CHANNEL"], "");
|
|
9
|
+
|
|
10
|
+
let endpoint = Stdlib_Option.getOr(process.env["APPSYNC_ENDPOINT"], "");
|
|
11
|
+
|
|
12
|
+
async function processRecord(record, region, creds) {
|
|
13
|
+
let parsed;
|
|
14
|
+
try {
|
|
15
|
+
parsed = JSON.parse(record.body);
|
|
16
|
+
} catch (exn) {
|
|
17
|
+
parsed = undefined;
|
|
18
|
+
}
|
|
19
|
+
let body = Stdlib_Option.flatMap(parsed, Stdlib_JSON.Decode.object);
|
|
20
|
+
if (body !== undefined) {
|
|
21
|
+
let payload = {};
|
|
22
|
+
Stdlib_Option.forEach(body["position"], v => {
|
|
23
|
+
payload["position"] = v;
|
|
24
|
+
});
|
|
25
|
+
Stdlib_Option.forEach(body["eventType"], v => {
|
|
26
|
+
payload["eventType"] = v;
|
|
27
|
+
});
|
|
28
|
+
Stdlib_Option.forEach(body["data"], v => {
|
|
29
|
+
payload["payload"] = v;
|
|
30
|
+
});
|
|
31
|
+
let reqBody = JSON.stringify(Object.fromEntries([
|
|
32
|
+
[
|
|
33
|
+
"id",
|
|
34
|
+
record.messageId
|
|
35
|
+
],
|
|
36
|
+
[
|
|
37
|
+
"channel",
|
|
38
|
+
channel
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
"events",
|
|
42
|
+
[JSON.stringify(payload)]
|
|
43
|
+
]
|
|
44
|
+
]));
|
|
45
|
+
let res = await AppSyncEventsSigner_Ops$ReventlessAws.postEvent(endpoint, region, new Date().toISOString(), creds, reqBody);
|
|
46
|
+
if (res.ok) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let txt = await res.text();
|
|
50
|
+
console.error("EventLogSubscription publish failed:", res.status.toString(), txt);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
console.error("EventLogSubscription: failed to parse record body", record.body);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function handler(event) {
|
|
57
|
+
let region = AppSyncEventsSigner_Ops$ReventlessAws.region();
|
|
58
|
+
let creds = AppSyncEventsSigner_Ops$ReventlessAws.envCreds();
|
|
59
|
+
return await Stdlib_Array.reduce(event.Records, Promise.resolve(), (acc, record) => acc.then(() => processRecord(record, region, creds)));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export {
|
|
63
|
+
channel,
|
|
64
|
+
endpoint,
|
|
65
|
+
processRecord,
|
|
66
|
+
handler,
|
|
67
|
+
}
|
|
68
|
+
/* channel Not a pure module */
|
|
@@ -49,7 +49,7 @@ let make = (
|
|
|
49
49
|
opts->Option.map(ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions)
|
|
50
50
|
|
|
51
51
|
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
52
|
-
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs",
|
|
52
|
+
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs",
|
|
53
53
|
~packageDirs=Dict.make(),
|
|
54
54
|
)
|
|
55
55
|
|
|
@@ -23,7 +23,7 @@ import * as RuntimeEnvironment_Lambda$ReventlessAws from "../Runtime/RuntimeEnvi
|
|
|
23
23
|
function make(name, logs, securityGroupId, subnetIds, intervalMinutesOpt, opts) {
|
|
24
24
|
let intervalMinutes = intervalMinutesOpt !== undefined ? intervalMinutesOpt : 1;
|
|
25
25
|
let customOpts = Stdlib_Option.map(opts, Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions);
|
|
26
|
-
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.mjs", {}, undefined);
|
|
26
|
+
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgChangeFeedRelayEntryPoint.res.mjs", {}, undefined);
|
|
27
27
|
let logJsonOutputs = logs.map(l => Pulumi.all([
|
|
28
28
|
l.connectionConfig,
|
|
29
29
|
l.targetQueueUrl
|
|
@@ -33,7 +33,7 @@ let make = (
|
|
|
33
33
|
open PulumiAws
|
|
34
34
|
|
|
35
35
|
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
36
|
-
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/PgMigrationEntryPoint.mjs",
|
|
36
|
+
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs",
|
|
37
37
|
~packageDirs=Dict.make(),
|
|
38
38
|
)
|
|
39
39
|
|
|
@@ -11,7 +11,7 @@ import * as Util_Bundle$ReventlessAws from "../../util/Util_Bundle.res.mjs";
|
|
|
11
11
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "../Runtime/RuntimeEnvironment_Lambda.res.mjs";
|
|
12
12
|
|
|
13
13
|
function make(name, handlerConfig, secretArn, securityGroupId, subnetIds, opts) {
|
|
14
|
-
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgMigrationEntryPoint.mjs", {}, undefined);
|
|
14
|
+
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PgMigrationEntryPoint.res.mjs", {}, undefined);
|
|
15
15
|
let sourceCodeHash = match.sourceCodeHash;
|
|
16
16
|
let envVars = Object.fromEntries([[
|
|
17
17
|
"HANDLER_CONFIG",
|