1import * as Lambda from "@/AWS/Lambda";
2import * as Route53Profiles from "@/AWS/Route53Profiles";
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";
8import path from "pathe";
9
10const main = path.resolve(import.meta.dirname, "handler.ts");
11
12export class ProfilesTestFunction extends Lambda.Function<Lambda.Function>()(
13 "ProfilesTestFunction",
14) {}
15
16export default ProfilesTestFunction.make(
17 {
18 main,
19 functionUrl: true,
20 timeout: Duration.seconds(30),
21 },
22 Effect.gen(function* () {
23 // The profile the bindings are bound to. Nothing is ever associated with
24 // it (no VPC association, no attached DNS resources), so both lists
25 // legitimately return empty item lists — which still proves the IAM
26 // grants and the ProfileId injection end-to-end.
27 const profile = yield* Route53Profiles.Profile("BindingProfile");
28
29 const listProfileAssociations =
30 yield* Route53Profiles.ListProfileAssociations(profile);
31 const listProfileResourceAssociations =
32 yield* Route53Profiles.ListProfileResourceAssociations(profile);
33
34 const bound = { listProfileAssociations, listProfileResourceAssociations };
35
36 return {
37 fetch: Effect.gen(function* () {
38 const request = yield* HttpServerRequest;
39 const url = new URL(request.originalUrl);
40 const pathname = url.pathname;
41
42 if (request.method === "GET" && pathname === "/bindings") {
43 return yield* HttpServerResponse.json({
44 bound: Object.keys(bound),
45 });
46 }
47
48 // Account-wide list filtered to the bound profile: the ProfileId is
49 // injected from the binding.
50 if (request.method === "GET" && pathname === "/associations") {
51 const { ProfileAssociations = [] } = yield* listProfileAssociations();
52 return yield* HttpServerResponse.json({
53 count: ProfileAssociations.length,
54 resourceIds: ProfileAssociations.map((item) => item.ResourceId),
55 });
56 }
57
58 // Profile-scoped list: the ProfileId is injected from the binding.
59 if (request.method === "GET" && pathname === "/resource-associations") {
60 const { ProfileResourceAssociations = [] } =
61 yield* listProfileResourceAssociations();
62 return yield* HttpServerResponse.json({
63 count: ProfileResourceAssociations.length,
64 resourceArns: ProfileResourceAssociations.map(
65 (item) => item.ResourceArn,
66 ),
67 });
68 }
69
70 return yield* HttpServerResponse.json(
71 { error: "Not found", method: request.method, pathname },
72 { status: 404 },
73 );
74 }).pipe(Effect.orDie),
75 };
76 }).pipe(
77 Effect.provide(
78 Layer.mergeAll(
79 Route53Profiles.ListProfileAssociationsHttp,
80 Route53Profiles.ListProfileResourceAssociationsHttp,
81 ),
82 ),
83 ),
84);