@reventlessdev/reventless-aws 3.0.0-alpha.229 → 3.0.0-alpha.230
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 +8 -0
- package/package.json +5 -5
- package/src/adapter/Geocoder/Geocoder_AwsLocation.res +60 -141
- package/src/adapter/Geocoder/Geocoder_AwsLocation.res.mjs +52 -118
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Ops.res +137 -0
- package/src/adapter/Geocoder/Geocoder_AwsLocation_Ops.res.mjs +124 -0
- package/src/adapter/Runtime/HeartbeatEntryPoint.res +44 -0
- package/src/adapter/Runtime/HeartbeatEntryPoint.res.mjs +47 -0
- package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res +234 -0
- package/src/adapter/Runtime/PluginExtensionPointEntryPoint.res.mjs +226 -0
- package/src/adapter/Upload/Upload_Presign_S3.res +65 -164
- package/src/adapter/Upload/Upload_Presign_S3.res.mjs +56 -119
- package/src/adapter/Upload/Upload_Presign_S3_Ops.res +157 -0
- package/src/adapter/Upload/Upload_Presign_S3_Ops.res.mjs +120 -0
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res +1 -1
- package/src/plugin/runtime/PluginExtensionPointRuntime_Builder.res.mjs +1 -1
- package/src/plugin/runtime/PluginRuntime_Builder.res +1 -1
- package/src/plugin/runtime/PluginRuntime_Builder.res.mjs +1 -1
- package/src/adapter/Runtime/HeartbeatEntryPoint.mjs +0 -38
- package/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs +0 -127
|
@@ -0,0 +1,120 @@
|
|
|
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 Nodecrypto from "node:crypto";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as ClientS3 from "@aws-sdk/client-s3";
|
|
7
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
8
|
+
import * as S3RequestPresigner from "@aws-sdk/s3-request-presigner";
|
|
9
|
+
|
|
10
|
+
function getEnv(k) {
|
|
11
|
+
let v = process.env[k];
|
|
12
|
+
if (v !== undefined && v !== "") {
|
|
13
|
+
return v;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function corsHeaders() {
|
|
18
|
+
return Object.fromEntries([
|
|
19
|
+
[
|
|
20
|
+
"content-type",
|
|
21
|
+
"application/json"
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
"access-control-allow-origin",
|
|
25
|
+
"*"
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"access-control-allow-methods",
|
|
29
|
+
"POST,OPTIONS"
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
"access-control-allow-headers",
|
|
33
|
+
"*"
|
|
34
|
+
]
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function decodeJwtSub(header) {
|
|
39
|
+
try {
|
|
40
|
+
let token = header.startsWith("Bearer ") ? header.slice(7, header.length) : header;
|
|
41
|
+
let payload = token.split(".")[1];
|
|
42
|
+
if (payload === undefined) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
let base64 = payload.replaceAll("-", "+").replaceAll("_", "/");
|
|
46
|
+
let obj = JSON.parse(Buffer.from(base64, "base64").toString("utf8"));
|
|
47
|
+
if (typeof obj === "object" && obj !== null && !Array.isArray(obj)) {
|
|
48
|
+
return Stdlib_Option.flatMap(obj["sub"], Stdlib_JSON.Decode.string);
|
|
49
|
+
} else {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
} catch (exn) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function identityPrefix(event) {
|
|
58
|
+
let authHeader = Stdlib_Option.flatMap(event.headers, h => Stdlib_Option.orElse(h["authorization"], h["Authorization"]));
|
|
59
|
+
let sub = Stdlib_Option.flatMap(authHeader, decodeJwtSub);
|
|
60
|
+
if (sub !== undefined) {
|
|
61
|
+
return sub + `/`;
|
|
62
|
+
} else {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function handler(event) {
|
|
68
|
+
try {
|
|
69
|
+
let bucket = Stdlib_Option.getOr(getEnv("UPLOAD_BUCKET"), "");
|
|
70
|
+
let parsed = Stdlib_Option.getOr(Stdlib_JSON.Decode.object(JSON.parse(Stdlib_Option.getOr(event.body, "{}"))), {});
|
|
71
|
+
let fileName = Stdlib_Option.getOr(Stdlib_Option.flatMap(parsed["fileName"], Stdlib_JSON.Decode.string), "upload");
|
|
72
|
+
let contentType = Stdlib_Option.flatMap(parsed["contentType"], Stdlib_JSON.Decode.string);
|
|
73
|
+
let servedPrefix = Stdlib_Option.getOr(getEnv("SERVED_PREFIX"), "uploads");
|
|
74
|
+
let key = servedPrefix + `/` + identityPrefix(event) + Nodecrypto.randomUUID() + `/` + fileName;
|
|
75
|
+
let client = new ClientS3.S3Client();
|
|
76
|
+
let command = new ClientS3.PutObjectCommand({
|
|
77
|
+
Bucket: bucket,
|
|
78
|
+
Key: key,
|
|
79
|
+
ContentType: contentType
|
|
80
|
+
});
|
|
81
|
+
let uploadUrl = await S3RequestPresigner.getSignedUrl(client, command, {
|
|
82
|
+
expiresIn: 300
|
|
83
|
+
});
|
|
84
|
+
let storageRef = `/` + key;
|
|
85
|
+
return {
|
|
86
|
+
statusCode: 200,
|
|
87
|
+
headers: corsHeaders(),
|
|
88
|
+
body: JSON.stringify(Object.fromEntries([
|
|
89
|
+
[
|
|
90
|
+
"uploadUrl",
|
|
91
|
+
uploadUrl
|
|
92
|
+
],
|
|
93
|
+
[
|
|
94
|
+
"storageRef",
|
|
95
|
+
storageRef
|
|
96
|
+
]
|
|
97
|
+
]))
|
|
98
|
+
};
|
|
99
|
+
} catch (raw_exn) {
|
|
100
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
101
|
+
console.error("UploadPresign: presign failed", exn);
|
|
102
|
+
return {
|
|
103
|
+
statusCode: 400,
|
|
104
|
+
headers: corsHeaders(),
|
|
105
|
+
body: JSON.stringify(Object.fromEntries([[
|
|
106
|
+
"error",
|
|
107
|
+
"presign_failed"
|
|
108
|
+
]]))
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export {
|
|
114
|
+
getEnv,
|
|
115
|
+
corsHeaders,
|
|
116
|
+
decodeJwtSub,
|
|
117
|
+
identityPrefix,
|
|
118
|
+
handler,
|
|
119
|
+
}
|
|
120
|
+
/* node:crypto Not a pure module */
|
|
@@ -96,7 +96,7 @@ let forCommandTopic = (
|
|
|
96
96
|
|
|
97
97
|
// No user packages — all framework imports are in the Layer
|
|
98
98
|
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
99
|
-
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs",
|
|
99
|
+
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/PluginExtensionPointEntryPoint.res.mjs",
|
|
100
100
|
~packageDirs=Dict.make(),
|
|
101
101
|
)
|
|
102
102
|
|
|
@@ -65,7 +65,7 @@ function forCommandTopic(param, connect, memorySizeOpt, timeoutOpt, param$1, par
|
|
|
65
65
|
return `{"queueUrl":"` + queueUrl + `","pluginReadModelTableName":"` + rmTable + `","schedulerRoleArn":"` + schedRoleArn + `","schedulerQueueArn":"` + schedQueueArn + `","schedulerQueueName":"` + schedQueueName + `","publishToAggregates":{` + publishToAggregatesJson + `}}`;
|
|
66
66
|
});
|
|
67
67
|
envVars["HANDLER_CONFIG"] = handlerConfigJson;
|
|
68
|
-
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PluginExtensionPointEntryPoint.mjs", {}, undefined);
|
|
68
|
+
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/PluginExtensionPointEntryPoint.res.mjs", {}, undefined);
|
|
69
69
|
return connect(RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "CommandHandler", "ExtensionPoint", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts));
|
|
70
70
|
}
|
|
71
71
|
|
|
@@ -731,7 +731,7 @@ module Make = (
|
|
|
731
731
|
|
|
732
732
|
// Static re-export from Layer entry point — no esbuild, no user packages
|
|
733
733
|
let {code, sourceCodeHash} = Util_Bundle.buildCodeArchive(
|
|
734
|
-
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.mjs",
|
|
734
|
+
~entryPointModule="@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.res.mjs",
|
|
735
735
|
~packageDirs=Dict.make(),
|
|
736
736
|
)
|
|
737
737
|
|
|
@@ -422,7 +422,7 @@ function Make(EventCollectorChannel) {
|
|
|
422
422
|
envVars["EP_QUEUE_URL"] = epQueueUrl;
|
|
423
423
|
envVars["PLUGIN_ID"] = Pulumi.output(hbConfig.pluginId);
|
|
424
424
|
envVars["HEARTBEAT_TIMEOUT"] = Pulumi.output(hbConfig.heartbeatTimeout.toString());
|
|
425
|
-
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.mjs", {}, undefined);
|
|
425
|
+
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Runtime/HeartbeatEntryPoint.res.mjs", {}, undefined);
|
|
426
426
|
connect(RuntimeEnvironment_Lambda$ReventlessAws.makeFromCodeAsset(name, "Scheduler", "Scheduler", match.code, match.sourceCodeHash, envVars, memorySize, timeout, undefined, undefined, undefined, undefined, undefined, opts));
|
|
427
427
|
};
|
|
428
428
|
let forDcbCommandTopic = (param, connect, memorySizeOpt, timeoutOpt, dcbCommandTopic) => {
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// Heartbeat Lambda handler.
|
|
2
|
-
// Publishes a Heartbeat(timeout) command to the PluginExtensionPoint CommandTopic.
|
|
3
|
-
// Triggered by CloudWatch Events on a schedule.
|
|
4
|
-
|
|
5
|
-
import { reverseConvertToJsonOrThrow } from "sury/src/S.res.mjs";
|
|
6
|
-
import { makeQueueRef } from "./HandlerFactoryHelpers.mjs";
|
|
7
|
-
import { uuid } from "@reventlessdev/reventless-core/src/Message.res.mjs";
|
|
8
|
-
import { name as pluginExtensionPointName, commandSchema } from "@reventlessdev/reventless-infra/src/types/PluginExtensionPointSpec.res.mjs";
|
|
9
|
-
import { publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
|
|
10
|
-
|
|
11
|
-
// === Initialize eagerly at module load (Lambda cold start) ===
|
|
12
|
-
|
|
13
|
-
const epQueueUrl = process.env["EP_QUEUE_URL"] || "";
|
|
14
|
-
const pluginId = process.env["PLUGIN_ID"] || "";
|
|
15
|
-
const timeout = parseInt(process.env["HEARTBEAT_TIMEOUT"], 10) || 10;
|
|
16
|
-
|
|
17
|
-
const publishJsons = sqsPublishJsons(makeQueueRef(epQueueUrl), "SQS_FIFO");
|
|
18
|
-
|
|
19
|
-
// === Exported handler ===
|
|
20
|
-
|
|
21
|
-
export async function handler(_event, _context) {
|
|
22
|
-
const msgId = uuid();
|
|
23
|
-
const commandJson = reverseConvertToJsonOrThrow({ TAG: "Heartbeat", _0: timeout }, commandSchema);
|
|
24
|
-
const message = {
|
|
25
|
-
id: pluginId,
|
|
26
|
-
meta: {
|
|
27
|
-
service: pluginExtensionPointName,
|
|
28
|
-
time: new Date().toISOString(),
|
|
29
|
-
ip: "",
|
|
30
|
-
user: "Heartbeat",
|
|
31
|
-
msgId,
|
|
32
|
-
correlationId: msgId,
|
|
33
|
-
},
|
|
34
|
-
commandJson,
|
|
35
|
-
};
|
|
36
|
-
await publishJsons([message]);
|
|
37
|
-
return "";
|
|
38
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
// PluginExtensionPoint Lambda entry point.
|
|
2
|
-
// All imports are framework packages — no user modules needed.
|
|
3
|
-
// Handles Heartbeat, Cloner, and other plugin-level extension point commands.
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
patchSpecId,
|
|
7
|
-
makeQueueRef,
|
|
8
|
-
scanByTableName,
|
|
9
|
-
log,
|
|
10
|
-
runEffect,
|
|
11
|
-
setRequestId,
|
|
12
|
-
extractMetaField,
|
|
13
|
-
extractSentTimestamp,
|
|
14
|
-
extractRetryCount,
|
|
15
|
-
} from "./HandlerFactoryHelpers.mjs";
|
|
16
|
-
import { sendMessage } from "@reventlessdev/reventless-aws/src/plugin/runtime/Util_PluginMessage_Runtime.res.mjs";
|
|
17
|
-
import * as PluginExtensionPointSpec from "@reventlessdev/reventless-infra/src/types/PluginExtensionPointSpec.res.mjs";
|
|
18
|
-
import { Make as pluginEPPluginMake } from "@reventlessdev/reventless-core/src/plugin/connect/PluginExtensionPoint_Plugin.res.mjs";
|
|
19
|
-
import { Make as extensionPointCallbackMake } from "@reventlessdev/reventless-core/src/components/ExtensionPoint/ExtensionPoint_Callback.res.mjs";
|
|
20
|
-
import { Make as commandTopicCallbackMake } from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Callback.res.mjs";
|
|
21
|
-
import { handleQueueEvent, publishJsons as sqsPublishJsons } from "@reventlessdev/reventless-aws/src/adapter/CommandTopic/CommandTopicChannel_SQS_Runtime.res.mjs";
|
|
22
|
-
import { createSchedule as cwCreateSchedule, deleteSchedule as cwDeleteSchedule } from "@reventlessdev/reventless-aws/src/adapter/ScheduledPublisher/ScheduledPublisher_CloudWatchEvents_Runtime.res.mjs";
|
|
23
|
-
|
|
24
|
-
function buildHandler() {
|
|
25
|
-
const configStr = process.env["HANDLER_CONFIG"] || "{}";
|
|
26
|
-
const config = JSON.parse(configStr);
|
|
27
|
-
|
|
28
|
-
const patchedSpec = patchSpecId(PluginExtensionPointSpec);
|
|
29
|
-
|
|
30
|
-
// Reconstruct runtimeOps — only sendMessageToChannel is used by this
|
|
31
|
-
// Lambda's incoming-command path (ForwardCommand). Cross-plugin
|
|
32
|
-
// subscribe / unsubscribe directives were retired in Phase 3 Step 3.
|
|
33
|
-
const runtimeOps = {
|
|
34
|
-
messagePublish: { sendMessageToChannel: sendMessage },
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Instantiate Plugin EP mapping. updateApiSchema and manageSubscriptions are
|
|
38
|
-
// admin-only hooks; this Lambda only handles incoming commands (Heartbeat,
|
|
39
|
-
// ForwardCommand) so they stay undefined here.
|
|
40
|
-
//
|
|
41
|
-
// `environment` prefixes the disconnect schedule's EventBridge rule name, so it
|
|
42
|
-
// must be stable across deploys and unique per stack. The stack name is both;
|
|
43
|
-
// AWS_LAMBDA_FUNCTION_NAME is neither — it carries a content hash, so replacing
|
|
44
|
-
// this Lambda would orphan every outstanding rule the previous generation
|
|
45
|
-
// created. EventCollectorEntryPoint instantiates the same EP module and must
|
|
46
|
-
// agree, or one Lambda creates rules the other cannot delete.
|
|
47
|
-
const environment = process.env["Environment"] || "unknown";
|
|
48
|
-
const pluginModule = pluginEPPluginMake({
|
|
49
|
-
runtimeOps,
|
|
50
|
-
environment,
|
|
51
|
-
updateApiSchema: undefined,
|
|
52
|
-
manageSubscriptions: undefined,
|
|
53
|
-
});
|
|
54
|
-
const mappingsModule = { mappings: [pluginModule.Mapping] };
|
|
55
|
-
|
|
56
|
-
// Reconstruct publishToAggregates. The deploy-side builder writes
|
|
57
|
-
// HANDLER_CONFIG.publishToAggregates as { aggregateName: envVarName }
|
|
58
|
-
// (see PluginExtensionPointRuntime_Builder.res), so iterate accordingly.
|
|
59
|
-
const publishToAggregates = {};
|
|
60
|
-
for (const [aggName, envVarName] of Object.entries(config.publishToAggregates || {})) {
|
|
61
|
-
const queueUrl = process.env[envVarName] || "";
|
|
62
|
-
publishToAggregates[aggName] = sqsPublishJsons(makeQueueRef(queueUrl), "SQS_FIFO");
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Reconstruct queryEngine
|
|
66
|
-
const queryEngine = {
|
|
67
|
-
scan: (readModelName, filterConfigs, limit) => scanByTableName(config.pluginReadModelTableName, filterConfigs, limit),
|
|
68
|
-
query: async () => { throw new Error("QueryEngine.query not available in bundled Plugin EP handler"); },
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// Reconstruct scheduler
|
|
72
|
-
const scheduler = {
|
|
73
|
-
createSchedule: cwCreateSchedule(config.schedulerRoleArn),
|
|
74
|
-
deleteSchedule: cwDeleteSchedule,
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// CommandTopic resources for scheduler targets
|
|
78
|
-
const commandTopicResources = config.schedulerQueueArn !== ""
|
|
79
|
-
? [{
|
|
80
|
-
name: config.schedulerQueueName,
|
|
81
|
-
id: config.schedulerQueueName,
|
|
82
|
-
urn: config.schedulerQueueArn,
|
|
83
|
-
service: "unknown",
|
|
84
|
-
resourceInfo: "NoInfo",
|
|
85
|
-
role: "",
|
|
86
|
-
region: "",
|
|
87
|
-
resourceType: "",
|
|
88
|
-
configuration: {},
|
|
89
|
-
tags: {},
|
|
90
|
-
}]
|
|
91
|
-
: [];
|
|
92
|
-
|
|
93
|
-
const invalidNameChars = /[^.\-_a-zA-Z0-9]/g;
|
|
94
|
-
const resourceNaming = {
|
|
95
|
-
validateName: (n) => n.replace(invalidNameChars, "_"),
|
|
96
|
-
urnName: (arn) => { const parts = arn.split(":"); return parts[5] || "unknown"; },
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const callbackConfig = { publishToAggregates, commandTopicResources, scheduler, queryEngine, resourceNaming };
|
|
100
|
-
const callback = extensionPointCallbackMake(callbackConfig)(patchedSpec)(mappingsModule);
|
|
101
|
-
const commandTopicCallback = commandTopicCallbackMake(patchedSpec)({
|
|
102
|
-
Spec: patchedSpec,
|
|
103
|
-
commandsHandler: callback.handleIncomingCommands,
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
sqsHandler: handleQueueEvent(makeQueueRef(config.queueUrl), commandTopicCallback.handleJsonCommands),
|
|
108
|
-
comp: `ExtensionPoint(${patchedSpec.name})`,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const { sqsHandler, comp } = buildHandler();
|
|
113
|
-
|
|
114
|
-
export async function handler(event, context) {
|
|
115
|
-
setRequestId(context?.awsRequestId);
|
|
116
|
-
const records = event.Records || [];
|
|
117
|
-
|
|
118
|
-
log.debug("processing " + records.length.toString() + " record(s)", { comp: "PluginExtensionPointRuntime" });
|
|
119
|
-
await runEffect(sqsHandler(event, context), {
|
|
120
|
-
correlationId: extractMetaField(records, "correlationId"),
|
|
121
|
-
causationId: extractMetaField(records, "causationId"),
|
|
122
|
-
comp,
|
|
123
|
-
timestamp: extractSentTimestamp(records),
|
|
124
|
-
retryCount: extractRetryCount(records),
|
|
125
|
-
});
|
|
126
|
-
return "";
|
|
127
|
-
}
|