1import * as Effect from "effect/Effect";
2import * as Layer from "effect/Layer";
3import * as Schema from "effect/Schema";
4import * as HttpServerResponse from "effect/http/HttpServerResponse";
5import * as HttpApi from "effect/http-api/HttpApi";
6import * as HttpApiBuilder from "effect/http-api/HttpApiBuilder";
7import * as HttpApiEndpoint from "effect/http-api/HttpApiEndpoint";
8import * as HttpApiGroup from "effect/http-api/HttpApiGroup";
9import { Job, JobId } from "./Job.ts";
10import { JobNotifications, NotifyJobError } from "./JobNotifications.ts";
11import { GetJobError, JobStorage, PutJobError } from "./JobStorage.ts";
12
13export const getJob = HttpApiEndpoint.get("getJob", "/", {
14 success: Job,
15 query: {
16 jobId: JobId.pipe(Schema.optional),
17 },
18});
19
20export const createJob = HttpApiEndpoint.post("createJob", "/", {
21 success: JobId,
22 payload: Schema.Struct({
23 content: Schema.String,
24 }),
25});
26
27export const JobApi = HttpApi.make("JobApi").add(
28 HttpApiGroup.make("Jobs").add(getJob, createJob),
29);
30
31export const JobApiLive = HttpApiBuilder.layer(JobApi).pipe(
32 Layer.provide(
33 HttpApiBuilder.group(JobApi, "Jobs", (handlers) =>
34 Effect.gen(function* () {
35 const jobService = yield* JobStorage;
36 const notifications = yield* JobNotifications;
37
38 return handlers
39 .handle(
40 "getJob",
41 Effect.fn(function* (req) {
42 if (!req.query.jobId) {
43 return HttpServerResponse.text("Job ID is required", {
44 status: 400,
45 });
46 }
47 const job = yield* jobService.getJob(req.query.jobId).pipe(
48 Effect.catchTag("GetJobError", (error) =>
49 Effect.succeed(
50 HttpServerResponse.text(error.message, {
51 status: 500,
52 }),
53 ),
54 ),
55 );
56 if (job instanceof GetJobError) {
57 return HttpServerResponse.text(job.message, {
58 status: 500,
59 });
60 }
61 if (!job) {
62 return HttpServerResponse.text("Job not found", {
63 status: 404,
64 });
65 }
66 return job!;
67 }),
68 )
69 .handle(
70 "createJob",
71 Effect.fn(function* (req) {
72 const jobId = crypto.randomUUID();
73 const job = yield* jobService
74 .putJob({
75 id: jobId,
76 content: req.payload.content,
77 })
78 .pipe(
79 Effect.catchTag("PutJobError", (error) =>
80 Effect.succeed(error),
81 ),
82 );
83 if (job instanceof PutJobError) {
84 return HttpServerResponse.text(job.message, {
85 status: 500,
86 });
87 }
88 const notificationResult = yield* notifications
89 .notifyJobCreated(job)
90 .pipe(
91 Effect.catchTag("NotifyJobError", (error) =>
92 Effect.succeed(error),
93 ),
94 );
95 if (notificationResult instanceof NotifyJobError) {
96 return HttpServerResponse.text(notificationResult.message, {
97 status: 500,
98 });
99 }
100 return job.id;
101 }),
102 );
103 }),
104 ),
105 ),
106);