1import * as AWS from "@/AWS";
2import * as Context from "effect/Context";
3import * as Duration from "effect/Duration";
4import * as Effect from "effect/Effect";
5import * as Layer from "effect/Layer";
6import { HttpServerRequest } from "effect/http/HttpServerRequest";
7import * as HttpServerResponse from "effect/http/HttpServerResponse";
8
9// Gated (AWS_TEST_SLOW) fixture: a single-region Multi-Region Access Point
10// plus a Lambda that exercises the two MRAP failover bindings —
11// GetMultiRegionAccessPointRoutes and SubmitMultiRegionAccessPointRoutes.
12// MRAP provisioning is asynchronous and takes 10-20 minutes, hence the gate.
13export class S3ControlMrapBindingsFunction extends AWS.Lambda.Function<AWS.Lambda.Function>()(
14 "S3ControlMrapBindingsFunction",
15) {}
16
17export class BoundMrap extends Context.Service<
18 BoundMrap,
19 { mrap: AWS.S3Control.MultiRegionAccessPoint }
20>()("BoundMrap") {}
21
22export const BoundMrapLive = Layer.effect(
23 BoundMrap,
24 Effect.gen(function* () {
25 const bucket = yield* AWS.S3.Bucket("S3ControlMrapBucket", {});
26 const mrap = yield* AWS.S3Control.MultiRegionAccessPoint(
27 "S3ControlMrapBindingsMrap",
28 { regions: [{ bucket: bucket.bucketName }] },
29 );
30 return { mrap };
31 }),
32);
33
34export default S3ControlMrapBindingsFunction.make(
35 {
36 main: import.meta.url,
37 functionUrl: true,
38 timeout: Duration.seconds(60),
39 },
40 Effect.gen(function* () {
41 const { mrap } = yield* BoundMrap;
42
43 const getRoutes =
44 yield* AWS.S3Control.GetMultiRegionAccessPointRoutes(mrap);
45 const submitRoutes =
46 yield* AWS.S3Control.SubmitMultiRegionAccessPointRoutes(mrap);
47
48 return {
49 fetch: Effect.gen(function* () {
50 const request = yield* HttpServerRequest;
51 const url = new URL(request.originalUrl);
52 const pathname = url.pathname;
53
54 if (request.method === "GET" && pathname === "/routes") {
55 const { Routes } = yield* getRoutes();
56 return yield* HttpServerResponse.json({
57 mrapName: mrap.multiRegionAccessPointName,
58 routes: (Routes ?? []).map((r) => ({
59 region: r.Region,
60 trafficDialPercentage: r.TrafficDialPercentage,
61 })),
62 });
63 }
64
65 if (request.method === "GET" && pathname === "/dial-100") {
66 // Re-submit every route at 100% — an idempotent write that proves
67 // the Submit binding end-to-end without shifting real traffic.
68 const { Routes } = yield* getRoutes();
69 yield* submitRoutes({
70 RouteUpdates: (Routes ?? []).map((r) => ({
71 Region: r.Region,
72 TrafficDialPercentage: 100,
73 })),
74 });
75 const after = yield* getRoutes();
76 return yield* HttpServerResponse.json({
77 routes: (after.Routes ?? []).map((r) => ({
78 region: r.Region,
79 trafficDialPercentage: r.TrafficDialPercentage,
80 })),
81 });
82 }
83
84 return yield* HttpServerResponse.json(
85 { error: "Not found", method: request.method, pathname },
86 { status: 404 },
87 );
88 }).pipe(Effect.orDie),
89 };
90 }).pipe(
91 Effect.provide(
92 Layer.mergeAll(
93 AWS.S3Control.GetMultiRegionAccessPointRoutesHttp,
94 AWS.S3Control.SubmitMultiRegionAccessPointRoutesHttp,
95 BoundMrapLive,
96 ),
97 ),
98 ),
99);