1import * as Lambda from "@/AWS/Lambda";
2import * as ResourceExplorer from "@/AWS/ResourceExplorer";
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 ResourceExplorerTestFunction extends Lambda.Function<Lambda.Function>()(
13 "ResourceExplorerTestFunction",
14) {}
15
16export default ResourceExplorerTestFunction.make(
17 {
18 main,
19 functionUrl: true,
20 // search over a fresh index can be slow on cold start — AWS's 3s
21 // default intermittently times out
22 timeout: Duration.seconds(30),
23 },
24 Effect.gen(function* () {
25 // The index is the region singleton that makes search possible; the
26 // view provider retries through its provisioning window.
27 yield* ResourceExplorer.Index("Explorer", {});
28 const view = yield* ResourceExplorer.View("SearchView", {
29 includedProperties: ["tags"],
30 });
31 const search = yield* ResourceExplorer.Search(view);
32 const listResources = yield* ResourceExplorer.ListResources(view);
33 const listSupportedResourceTypes =
34 yield* ResourceExplorer.ListSupportedResourceTypes();
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 === "/search") {
43 const q = url.searchParams.get("q") ?? "service:s3";
44 const result = yield* search({ QueryString: q, MaxResults: 25 });
45 return yield* HttpServerResponse.json({
46 viewArn: result.ViewArn,
47 totalResources: result.Count?.TotalResources ?? 0,
48 complete: result.Count?.Complete ?? false,
49 resources: (result.Resources ?? []).map((r) => r.Arn),
50 });
51 }
52
53 if (request.method === "GET" && pathname === "/resources") {
54 const filter = url.searchParams.get("filter");
55 const result = yield* listResources({
56 Filters: filter !== null ? { FilterString: filter } : undefined,
57 MaxResults: 25,
58 });
59 return yield* HttpServerResponse.json({
60 viewArn: result.ViewArn,
61 resources: (result.Resources ?? []).map((r) => r.Arn),
62 });
63 }
64
65 if (request.method === "GET" && pathname === "/types") {
66 const result = yield* listSupportedResourceTypes({
67 MaxResults: 100,
68 });
69 return yield* HttpServerResponse.json({
70 resourceTypes: (result.ResourceTypes ?? []).map(
71 (t) => t.ResourceType,
72 ),
73 });
74 }
75
76 return yield* HttpServerResponse.json(
77 { error: "Not found" },
78 { status: 404 },
79 );
80 }).pipe(Effect.orDie),
81 };
82 }).pipe(
83 Effect.provide(
84 Layer.mergeAll(
85 ResourceExplorer.SearchHttp,
86 ResourceExplorer.ListResourcesHttp,
87 ResourceExplorer.ListSupportedResourceTypesHttp,
88 ),
89 ),
90 ),
91);