52 lines
3.7 KiB
TypeScript
52 lines
3.7 KiB
TypeScript
import { caseRunAccepted } from "./transport.ts";
|
|
|
|
export function createHarnessRLHttpApp(options: { service: ReturnType<typeof import("./service.ts")["createHarnessRLService"]> }) {
|
|
return {
|
|
async fetch(request: Request) {
|
|
const url = new URL(request.url);
|
|
try {
|
|
if (url.pathname === "/healthz" || url.pathname === "/health/live") return json(200, { ok: true, serviceId: "hwlab-harnessrl-api" });
|
|
if (url.pathname === "/readyz" || url.pathname === "/health/ready") return json(200, await options.service.health());
|
|
if (url.pathname === "/v1/caserun" && request.method === "GET") return json(200, { ok: true, contractVersion: "hwlab-caserun-web-v1", casesRoute: "/v1/caserun/cases", runsRoute: "/v1/caserun/runs", executionAuthority: "temporal", registryAuthority: "postgresql" });
|
|
if (url.pathname === "/v1/caserun/cases" && request.method === "GET") {
|
|
const cases = await options.service.listCases();
|
|
return json(200, { ok: true, contractVersion: "hwlab-caserun-cases-v1", count: cases.length, cases });
|
|
}
|
|
if (url.pathname === "/v1/caserun/runs" && request.method === "POST") {
|
|
const body = await bodyObject(request);
|
|
const record = await options.service.submit({ caseId: String(body.caseId ?? ""), runId: body.runId ? String(body.runId) : undefined, runtimeApiUrl: runtimeApiUrl(request) });
|
|
return json(202, caseRunAccepted(record));
|
|
}
|
|
if (url.pathname === "/v1/caserun/runs" && request.method === "GET") {
|
|
return json(200, await options.service.listRuns({
|
|
status: url.searchParams.get("status") ?? undefined,
|
|
limit: url.searchParams.get("limit") ?? undefined,
|
|
cursor: url.searchParams.get("cursor") ?? undefined
|
|
}));
|
|
}
|
|
const match = /^\/v1\/caserun\/runs\/([^/]+)(?:\/(events|aggregate|cancel))?$/u.exec(url.pathname);
|
|
if (match) {
|
|
const runId = decodeURIComponent(match[1]);
|
|
const suffix = match[2] ?? "";
|
|
if (!suffix && request.method === "GET") return json(200, await options.service.getRun(runId));
|
|
if (suffix === "events" && request.method === "GET") return json(200, await options.service.events(runId));
|
|
if (suffix === "aggregate" && request.method === "GET") return json(200, await options.service.aggregate(runId));
|
|
if (suffix === "cancel" && request.method === "POST") return json(202, await options.service.cancel(runId));
|
|
return json(405, errorBody("method_not_allowed", suffix === "cancel" ? "POST required" : "GET required"));
|
|
}
|
|
return json(404, errorBody("caserun_route_not_found", "CaseRun REST route is not implemented"));
|
|
} catch (error: any) {
|
|
const code = error?.code ?? "harnessrl_error";
|
|
const status = code.includes("not_found") ? 404 : code.startsWith("invalid_") ? 400 : 500;
|
|
return json(status, errorBody(code, error?.message ?? String(error), error?.details));
|
|
}
|
|
},
|
|
close: () => options.service.close()
|
|
};
|
|
}
|
|
|
|
async function bodyObject(request: Request) { const body = await request.json().catch(() => null); if (!body || typeof body !== "object" || Array.isArray(body)) throw Object.assign(new Error("JSON object body is required"), { code: "invalid_json" }); return body as Record<string, unknown>; }
|
|
function runtimeApiUrl(request: Request) { const url = new URL(request.url); return `${url.protocol}//${url.host}`; }
|
|
function errorBody(code: string, message: string, details?: unknown) { return { ok: false, error: { code, message, details } }; }
|
|
function json(status: number, body: unknown) { return Response.json(body, { status, headers: { "cache-control": "no-store" } }); }
|