1import * as Forecast from "@/AWS/Forecast";
2import * as Lambda from "@/AWS/Lambda";
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 ForecastTestFunction extends Lambda.Function<Lambda.Function>()(
13 "ForecastTestFunction",
14) {}
15
16/**
17 * Account-level binding fixture: Amazon Forecast is closed to new customers,
18 * so no Forecast object is ever created. Every route drives an ARN-addressed
19 * operation against a well-formed-but-nonexistent ARN (passed by the test as
20 * a query parameter) and returns the typed error tag the service answers
21 * with — `ResourceNotFoundException` in a grandfathered account,
22 * `AccessDeniedException` (the new-customer closure) otherwise. Either way
23 * the route proves the deploy-time IAM bind, the runtime call plumbing, and
24 * the typed error decode inside the Lambda.
25 */
26export default ForecastTestFunction.make(
27 {
28 main,
29 functionUrl: true,
30 timeout: Duration.seconds(30),
31 },
32 Effect.gen(function* () {
33 const createDatasetImportJob = yield* Forecast.CreateDatasetImportJob();
34 const describeDatasetImportJob = yield* Forecast.DescribeDatasetImportJob();
35 const createAutoPredictor = yield* Forecast.CreateAutoPredictor();
36 const describeAutoPredictor = yield* Forecast.DescribeAutoPredictor();
37 const getAccuracyMetrics = yield* Forecast.GetAccuracyMetrics();
38 const createForecast = yield* Forecast.CreateForecast();
39 const describeForecast = yield* Forecast.DescribeForecast();
40 const stopResource = yield* Forecast.StopResource();
41 const resumeResource = yield* Forecast.ResumeResource();
42 const queryForecast = yield* Forecast.QueryForecast();
43 const queryWhatIfForecast = yield* Forecast.QueryWhatIfForecast();
44 const createForecastExportJob = yield* Forecast.CreateForecastExportJob();
45 const describeForecastExportJob =
46 yield* Forecast.DescribeForecastExportJob();
47 const createWhatIfAnalysis = yield* Forecast.CreateWhatIfAnalysis();
48 const describeWhatIfAnalysis = yield* Forecast.DescribeWhatIfAnalysis();
49 const createWhatIfForecast = yield* Forecast.CreateWhatIfForecast();
50 const describeWhatIfForecast = yield* Forecast.DescribeWhatIfForecast();
51 const createWhatIfForecastExport =
52 yield* Forecast.CreateWhatIfForecastExport();
53 const describeWhatIfForecastExport =
54 yield* Forecast.DescribeWhatIfForecastExport();
55 const deleteResourceTree = yield* Forecast.DeleteResourceTree();
56
57 const bound = {
58 createDatasetImportJob,
59 describeDatasetImportJob,
60 createAutoPredictor,
61 describeAutoPredictor,
62 getAccuracyMetrics,
63 createForecast,
64 describeForecast,
65 stopResource,
66 resumeResource,
67 queryForecast,
68 queryWhatIfForecast,
69 createForecastExportJob,
70 describeForecastExportJob,
71 createWhatIfAnalysis,
72 describeWhatIfAnalysis,
73 createWhatIfForecast,
74 describeWhatIfForecast,
75 createWhatIfForecastExport,
76 describeWhatIfForecastExport,
77 deleteResourceTree,
78 };
79
80 return {
81 fetch: Effect.gen(function* () {
82 const request = yield* HttpServerRequest;
83 const url = new URL(request.originalUrl);
84 const pathname = url.pathname;
85 const arn = url.searchParams.get("arn") ?? "";
86
87 if (request.method === "GET" && pathname === "/bindings") {
88 return yield* HttpServerResponse.json({
89 bound: Object.keys(bound),
90 });
91 }
92
93 if (request.method === "GET" && pathname === "/import-probe") {
94 const tag = yield* describeDatasetImportJob({
95 DatasetImportJobArn: arn,
96 }).pipe(
97 Effect.map(() => "Found"),
98 Effect.catch((e) => Effect.succeed(e._tag)),
99 );
100 return yield* HttpServerResponse.json({ tag });
101 }
102
103 if (request.method === "GET" && pathname === "/predictor-probe") {
104 const tag = yield* describeAutoPredictor({
105 PredictorArn: arn,
106 }).pipe(
107 Effect.map(() => "Found"),
108 Effect.catch((e) => Effect.succeed(e._tag)),
109 );
110 return yield* HttpServerResponse.json({ tag });
111 }
112
113 if (request.method === "GET" && pathname === "/accuracy-probe") {
114 const tag = yield* getAccuracyMetrics({ PredictorArn: arn }).pipe(
115 Effect.map(() => "Found"),
116 Effect.catch((e) => Effect.succeed(e._tag)),
117 );
118 return yield* HttpServerResponse.json({ tag });
119 }
120
121 if (request.method === "GET" && pathname === "/forecast-probe") {
122 const tag = yield* describeForecast({ ForecastArn: arn }).pipe(
123 Effect.map(() => "Found"),
124 Effect.catch((e) => Effect.succeed(e._tag)),
125 );
126 return yield* HttpServerResponse.json({ tag });
127 }
128
129 if (request.method === "GET" && pathname === "/create-forecast-probe") {
130 const tag = yield* createForecast({
131 ForecastName: "alchemy_forecast_bindings_probe",
132 PredictorArn: arn,
133 }).pipe(
134 Effect.map(() => "Created"),
135 Effect.catch((e) => Effect.succeed(e._tag)),
136 );
137 return yield* HttpServerResponse.json({ tag });
138 }
139
140 if (request.method === "GET" && pathname === "/stop-probe") {
141 const tag = yield* stopResource({ ResourceArn: arn }).pipe(
142 Effect.map(() => "Stopped"),
143 Effect.catch((e) => Effect.succeed(e._tag)),
144 );
145 return yield* HttpServerResponse.json({ tag });
146 }
147
148 if (request.method === "GET" && pathname === "/resume-probe") {
149 const tag = yield* resumeResource({ ResourceArn: arn }).pipe(
150 Effect.map(() => "Resumed"),
151 Effect.catch((e) => Effect.succeed(e._tag)),
152 );
153 return yield* HttpServerResponse.json({ tag });
154 }
155
156 if (request.method === "GET" && pathname === "/query-probe") {
157 const tag = yield* queryForecast({
158 ForecastArn: arn,
159 Filters: { item_id: "alchemy_probe" },
160 }).pipe(
161 Effect.map(() => "Queried"),
162 Effect.catch((e) => Effect.succeed(e._tag)),
163 );
164 return yield* HttpServerResponse.json({ tag });
165 }
166
167 if (request.method === "GET" && pathname === "/whatif-query-probe") {
168 const tag = yield* queryWhatIfForecast({
169 WhatIfForecastArn: arn,
170 Filters: { item_id: "alchemy_probe" },
171 }).pipe(
172 Effect.map(() => "Queried"),
173 Effect.catch((e) => Effect.succeed(e._tag)),
174 );
175 return yield* HttpServerResponse.json({ tag });
176 }
177
178 if (request.method === "GET" && pathname === "/export-create-probe") {
179 const tag = yield* createForecastExportJob({
180 ForecastExportJobName: "alchemy_forecast_export_probe",
181 ForecastArn: arn,
182 Destination: {
183 S3Config: {
184 Path: "s3://alchemy-nonexistent-probe-bucket/exports/",
185 RoleArn: "arn:aws:iam::000000000000:role/alchemy_probe",
186 },
187 },
188 }).pipe(
189 Effect.map(() => "Created"),
190 Effect.catch((e) => Effect.succeed(e._tag)),
191 );
192 return yield* HttpServerResponse.json({ tag });
193 }
194
195 if (request.method === "GET" && pathname === "/export-probe") {
196 const tag = yield* describeForecastExportJob({
197 ForecastExportJobArn: arn,
198 }).pipe(
199 Effect.map(() => "Found"),
200 Effect.catch((e) => Effect.succeed(e._tag)),
201 );
202 return yield* HttpServerResponse.json({ tag });
203 }
204
205 if (
206 request.method === "GET" &&
207 pathname === "/whatif-analysis-create-probe"
208 ) {
209 const tag = yield* createWhatIfAnalysis({
210 WhatIfAnalysisName: "alchemy_whatif_analysis_probe",
211 ForecastArn: arn,
212 }).pipe(
213 Effect.map(() => "Created"),
214 Effect.catch((e) => Effect.succeed(e._tag)),
215 );
216 return yield* HttpServerResponse.json({ tag });
217 }
218
219 if (request.method === "GET" && pathname === "/whatif-analysis-probe") {
220 const tag = yield* describeWhatIfAnalysis({
221 WhatIfAnalysisArn: arn,
222 }).pipe(
223 Effect.map(() => "Found"),
224 Effect.catch((e) => Effect.succeed(e._tag)),
225 );
226 return yield* HttpServerResponse.json({ tag });
227 }
228
229 if (request.method === "GET" && pathname === "/whatif-create-probe") {
230 const tag = yield* createWhatIfForecast({
231 WhatIfForecastName: "alchemy_whatif_forecast_probe",
232 WhatIfAnalysisArn: arn,
233 }).pipe(
234 Effect.map(() => "Created"),
235 Effect.catch((e) => Effect.succeed(e._tag)),
236 );
237 return yield* HttpServerResponse.json({ tag });
238 }
239
240 if (request.method === "GET" && pathname === "/whatif-forecast-probe") {
241 const tag = yield* describeWhatIfForecast({
242 WhatIfForecastArn: arn,
243 }).pipe(
244 Effect.map(() => "Found"),
245 Effect.catch((e) => Effect.succeed(e._tag)),
246 );
247 return yield* HttpServerResponse.json({ tag });
248 }
249
250 if (
251 request.method === "GET" &&
252 pathname === "/whatif-export-create-probe"
253 ) {
254 const tag = yield* createWhatIfForecastExport({
255 WhatIfForecastExportName: "alchemy_whatif_export_probe",
256 WhatIfForecastArns: [arn],
257 Destination: {
258 S3Config: {
259 Path: "s3://alchemy-nonexistent-probe-bucket/whatif/",
260 RoleArn: "arn:aws:iam::000000000000:role/alchemy_probe",
261 },
262 },
263 }).pipe(
264 Effect.map(() => "Created"),
265 Effect.catch((e) => Effect.succeed(e._tag)),
266 );
267 return yield* HttpServerResponse.json({ tag });
268 }
269
270 if (request.method === "GET" && pathname === "/whatif-export-probe") {
271 const tag = yield* describeWhatIfForecastExport({
272 WhatIfForecastExportArn: arn,
273 }).pipe(
274 Effect.map(() => "Found"),
275 Effect.catch((e) => Effect.succeed(e._tag)),
276 );
277 return yield* HttpServerResponse.json({ tag });
278 }
279
280 if (request.method === "GET" && pathname === "/delete-tree-probe") {
281 const tag = yield* deleteResourceTree({ ResourceArn: arn }).pipe(
282 Effect.map(() => "Deleted"),
283 Effect.catch((e) => Effect.succeed(e._tag)),
284 );
285 return yield* HttpServerResponse.json({ tag });
286 }
287
288 return yield* HttpServerResponse.json(
289 { error: "Not found", method: request.method, pathname },
290 { status: 404 },
291 );
292 }).pipe(Effect.orDie),
293 };
294 }).pipe(
295 Effect.provide(
296 Layer.mergeAll(
297 Forecast.CreateDatasetImportJobHttp,
298 Forecast.DescribeDatasetImportJobHttp,
299 Forecast.CreateAutoPredictorHttp,
300 Forecast.DescribeAutoPredictorHttp,
301 Forecast.GetAccuracyMetricsHttp,
302 Forecast.CreateForecastHttp,
303 Forecast.DescribeForecastHttp,
304 Forecast.StopResourceHttp,
305 Forecast.ResumeResourceHttp,
306 Forecast.QueryForecastHttp,
307 Forecast.QueryWhatIfForecastHttp,
308 Forecast.CreateForecastExportJobHttp,
309 Forecast.DescribeForecastExportJobHttp,
310 Forecast.CreateWhatIfAnalysisHttp,
311 Forecast.DescribeWhatIfAnalysisHttp,
312 Forecast.CreateWhatIfForecastHttp,
313 Forecast.DescribeWhatIfForecastHttp,
314 Forecast.CreateWhatIfForecastExportHttp,
315 Forecast.DescribeWhatIfForecastExportHttp,
316 Forecast.DeleteResourceTreeHttp,
317 ),
318 ),
319 ),
320);