1import * as DocDBElastic from "@/AWS/DocDBElastic";
2import * as Lambda from "@/AWS/Lambda";
3import * as Effect from "effect/Effect";
4import * as Layer from "effect/Layer";
5import { HttpServerRequest } from "effect/http/HttpServerRequest";
6import * as HttpServerResponse from "effect/http/HttpServerResponse";
7import path from "pathe";
8
9const main = path.resolve(import.meta.dirname, "handler.ts");
10
11export class DocDBElasticTestFunction extends Lambda.Function<Lambda.Function>()(
12 "DocDBElasticTestFunction",
13) {}
14
15/**
16 * Account-level binding fixture: no elastic cluster is ever created. List
17 * routes prove each grant and response decode against real (possibly empty)
18 * account data; probe routes drive ARN-addressed operations against
19 * well-formed-but-nonexistent ARNs (passed by the test as a query parameter)
20 * and must surface the service's typed not-found tag — an IAM gap would
21 * surface AccessDeniedException and fail the route with an opaque 500.
22 */
23export default DocDBElasticTestFunction.make(
24 {
25 main,
26 functionUrl: true,
27 },
28 Effect.gen(function* () {
29 const listSnapshots = yield* DocDBElastic.ListClusterSnapshots();
30 const getSnapshot = yield* DocDBElastic.GetClusterSnapshot();
31 const deleteSnapshot = yield* DocDBElastic.DeleteClusterSnapshot();
32 const copySnapshot = yield* DocDBElastic.CopyClusterSnapshot();
33 const restoreCluster = yield* DocDBElastic.RestoreClusterFromSnapshot();
34 const listPending = yield* DocDBElastic.ListPendingMaintenanceActions();
35 const getPending = yield* DocDBElastic.GetPendingMaintenanceAction();
36 const applyPending = yield* DocDBElastic.ApplyPendingMaintenanceAction();
37
38 const bound = {
39 listSnapshots,
40 getSnapshot,
41 deleteSnapshot,
42 copySnapshot,
43 restoreCluster,
44 listPending,
45 getPending,
46 applyPending,
47 };
48
49 return {
50 fetch: Effect.gen(function* () {
51 const request = yield* HttpServerRequest;
52 const url = new URL(request.originalUrl);
53 const pathname = url.pathname;
54 const arn = url.searchParams.get("arn") ?? "";
55
56 if (request.method === "GET" && pathname === "/bindings") {
57 return yield* HttpServerResponse.json({
58 bound: Object.keys(bound),
59 });
60 }
61
62 if (request.method === "GET" && pathname === "/snapshots") {
63 const result = yield* listSnapshots();
64 return yield* HttpServerResponse.json({
65 count: (result.snapshots ?? []).length,
66 });
67 }
68
69 if (request.method === "GET" && pathname === "/maintenance") {
70 const result = yield* listPending();
71 return yield* HttpServerResponse.json({
72 count: (result.resourcePendingMaintenanceActions ?? []).length,
73 });
74 }
75
76 if (request.method === "GET" && pathname === "/snapshot-probe") {
77 const tag = yield* getSnapshot({ snapshotArn: arn }).pipe(
78 Effect.map(() => "Found"),
79 Effect.catchTag("ResourceNotFoundException", (e) =>
80 Effect.succeed(e._tag),
81 ),
82 );
83 return yield* HttpServerResponse.json({ tag });
84 }
85
86 if (request.method === "GET" && pathname === "/delete-probe") {
87 const tag = yield* deleteSnapshot({ snapshotArn: arn }).pipe(
88 Effect.map(() => "Deleted"),
89 Effect.catchTag("ResourceNotFoundException", (e) =>
90 Effect.succeed(e._tag),
91 ),
92 );
93 return yield* HttpServerResponse.json({ tag });
94 }
95
96 if (request.method === "GET" && pathname === "/copy-probe") {
97 const tag = yield* copySnapshot({
98 snapshotArn: arn,
99 targetSnapshotName: "alchemy-docdb-elastic-copy-probe",
100 }).pipe(
101 Effect.map(() => "Copied"),
102 Effect.catch((e) => Effect.succeed(e._tag)),
103 );
104 return yield* HttpServerResponse.json({ tag });
105 }
106
107 if (request.method === "GET" && pathname === "/restore-probe") {
108 const tag = yield* restoreCluster({
109 snapshotArn: arn,
110 clusterName: "alchemy-docdb-elastic-restore-probe",
111 }).pipe(
112 Effect.map(() => "Restored"),
113 Effect.catch((e) => Effect.succeed(e._tag)),
114 );
115 return yield* HttpServerResponse.json({ tag });
116 }
117
118 if (request.method === "GET" && pathname === "/pending-probe") {
119 const tag = yield* getPending({ resourceArn: arn }).pipe(
120 Effect.map(() => "Found"),
121 Effect.catchTag(
122 ["ResourceNotFoundException", "ValidationException"],
123 (e) => Effect.succeed(e._tag),
124 ),
125 );
126 return yield* HttpServerResponse.json({ tag });
127 }
128
129 if (request.method === "GET" && pathname === "/apply-probe") {
130 const tag = yield* applyPending({
131 resourceArn: arn,
132 applyAction: "ENGINE_UPDATE",
133 optInType: "NEXT_MAINTENANCE",
134 }).pipe(
135 Effect.map(() => "Applied"),
136 Effect.catchTag(
137 ["ResourceNotFoundException", "ValidationException"],
138 (e) => Effect.succeed(e._tag),
139 ),
140 );
141 return yield* HttpServerResponse.json({ tag });
142 }
143
144 return yield* HttpServerResponse.json(
145 { error: "Not found", method: request.method, pathname },
146 { status: 404 },
147 );
148 }).pipe(Effect.orDie),
149 };
150 }).pipe(
151 Effect.provide(
152 Layer.mergeAll(
153 DocDBElastic.ListClusterSnapshotsHttp,
154 DocDBElastic.GetClusterSnapshotHttp,
155 DocDBElastic.DeleteClusterSnapshotHttp,
156 DocDBElastic.CopyClusterSnapshotHttp,
157 DocDBElastic.RestoreClusterFromSnapshotHttp,
158 DocDBElastic.ListPendingMaintenanceActionsHttp,
159 DocDBElastic.GetPendingMaintenanceActionHttp,
160 DocDBElastic.ApplyPendingMaintenanceActionHttp,
161 ),
162 ),
163 ),
164);