|
1 | 1 | import { |
2 | | - OpenAPIHono, |
3 | | - type RouteConfigToTypedResponse, |
| 2 | + OpenAPIHono, |
| 3 | + type RouteConfigToTypedResponse, |
4 | 4 | } from "@hono/zod-openapi"; |
5 | 5 | import { runUnitTestRequestSchema } from "../lib/validation"; |
6 | 6 | import { v4 } from "uuid"; |
7 | 7 | import { |
8 | | - cleanupSessionDirectory, |
9 | | - createSessionDirectory, |
10 | | - prepareUnitTestFiles, |
| 8 | + cleanupSessionDirectory, |
| 9 | + createSessionDirectory, |
| 10 | + prepareUnitTestFiles, |
11 | 11 | } from "../lib/files"; |
12 | 12 | import { |
13 | | - buildDockerCommand, |
14 | | - getContainerName, |
15 | | - trackContainer, |
16 | | - untrackContainer, |
| 13 | + buildDockerCommand, |
| 14 | + getContainerName, |
| 15 | + trackContainer, |
| 16 | + untrackContainer, |
17 | 17 | } from "../lib/docker"; |
18 | 18 | import { unitTestRoute } from "../lib/openapi"; |
19 | 19 |
|
20 | 20 | const app = new OpenAPIHono(); |
21 | 21 |
|
| 22 | +// Path is overriden, as I prefer to have the |
| 23 | +// full path in lib/openapi.ts to keep the |
| 24 | +// semantics there and in index.ts |
22 | 25 | app.openapi( |
23 | | - { ...unitTestRoute, path: "/" }, |
24 | | - async (c): Promise<RouteConfigToTypedResponse<typeof unitTestRoute>> => { |
25 | | - let body = null; |
| 26 | + { ...unitTestRoute, path: "/" }, |
| 27 | + async (c): Promise<RouteConfigToTypedResponse<typeof unitTestRoute>> => { |
| 28 | + let body = null; |
26 | 29 |
|
27 | | - try { |
28 | | - body = await c.req.json(); |
29 | | - } catch { |
30 | | - return c.json({ success: false, error: "Invalid JSON" }, 400); |
31 | | - } |
| 30 | + try { |
| 31 | + body = await c.req.json(); |
| 32 | + } catch { |
| 33 | + return c.json({ success: false, error: "Invalid JSON" }, 400); |
| 34 | + } |
32 | 35 |
|
33 | | - const validatedData = runUnitTestRequestSchema.safeParse(body); |
| 36 | + const validatedData = runUnitTestRequestSchema.safeParse(body); |
34 | 37 |
|
35 | | - if (!validatedData.success) { |
36 | | - return c.json( |
37 | | - { success: false, error: validatedData.error.message }, |
38 | | - 400, |
39 | | - ); |
40 | | - } |
| 38 | + if (!validatedData.success) { |
| 39 | + return c.json( |
| 40 | + { success: false, error: validatedData.error.message }, |
| 41 | + 400, |
| 42 | + ); |
| 43 | + } |
41 | 44 |
|
42 | | - const { user_code, unit_tests, api_key, config } = validatedData.data; |
| 45 | + const { user_code, unit_tests, api_key, config } = validatedData.data; |
43 | 46 |
|
44 | | - if (api_key !== Bun.env.API_KEY) { |
45 | | - return c.json({ success: false, error: "Invalid API key" }, 401); |
46 | | - } |
| 47 | + if (api_key !== Bun.env.API_KEY) { |
| 48 | + return c.json({ success: false, error: "Invalid API key" }, 401); |
| 49 | + } |
47 | 50 |
|
48 | | - // Create unique session |
49 | | - const sessionId = v4(); |
50 | | - const sessionPath = await createSessionDirectory(sessionId); |
51 | | - const containerName = getContainerName(sessionId); |
| 51 | + // Create unique session |
| 52 | + const sessionId = v4(); |
| 53 | + const sessionPath = await createSessionDirectory(sessionId); |
| 54 | + const containerName = getContainerName(sessionId); |
52 | 55 |
|
53 | | - try { |
54 | | - // Setup files and container |
55 | | - await prepareUnitTestFiles(sessionPath, user_code, unit_tests); |
56 | | - trackContainer(containerName); |
| 56 | + try { |
| 57 | + // Setup files and container |
| 58 | + await prepareUnitTestFiles(sessionPath, user_code, unit_tests); |
| 59 | + trackContainer(containerName); |
57 | 60 |
|
58 | | - // Execute in Docker |
59 | | - const command = buildDockerCommand( |
60 | | - sessionId, |
61 | | - sessionPath, |
62 | | - "unit-test", |
63 | | - config, |
64 | | - ); |
65 | | - const proc = Bun.spawn(command, { |
66 | | - stdout: "pipe", |
67 | | - stderr: "pipe", |
68 | | - }); |
| 61 | + // Execute in Docker |
| 62 | + const command = buildDockerCommand(sessionId, sessionPath, config); |
| 63 | + const proc = Bun.spawn(command, { |
| 64 | + stdout: "pipe", |
| 65 | + stderr: "pipe", |
| 66 | + }); |
69 | 67 |
|
70 | | - const stdout = await new Response(proc.stdout).text(); |
71 | | - const stderr = await new Response(proc.stderr).text(); |
| 68 | + const stdout = await new Response(proc.stdout).text(); |
| 69 | + const stderr = await new Response(proc.stderr).text(); |
72 | 70 |
|
73 | | - // Wait for process to complete and get exit code |
74 | | - const exitCode = await proc.exited; |
| 71 | + // Wait for process to complete and get exit code |
| 72 | + const exitCode = await proc.exited; |
75 | 73 |
|
76 | | - const result = stdout ? JSON.parse(stdout) : undefined; |
| 74 | + const result = stdout ? JSON.parse(stdout) : undefined; |
77 | 75 |
|
78 | | - return c.json( |
79 | | - { |
80 | | - success: exitCode == 0, |
81 | | - runalyzer_output: result, |
82 | | - runalyzer_errors: |
83 | | - stderr || (exitCode === 124 ? "Time limit exceeded" : ""), |
84 | | - exit_code: exitCode, |
85 | | - }, |
86 | | - 200, |
87 | | - ); |
88 | | - } catch (error) { |
89 | | - console.error(error); |
90 | | - return c.json({ success: false, error: "Internal server error" }, 500); |
91 | | - } finally { |
92 | | - untrackContainer(containerName); |
93 | | - await cleanupSessionDirectory(sessionPath, sessionId); |
94 | | - } |
95 | | - }, |
| 76 | + // Yes, this could be one return |
| 77 | + // But typescript wants it to be split |
| 78 | + // Because of the discriminatory union |
| 79 | + if (exitCode === 0) { |
| 80 | + return c.json( |
| 81 | + { |
| 82 | + success: true, |
| 83 | + runalyzer_output: result, |
| 84 | + runalyzer_errors: stderr, |
| 85 | + exit_code: exitCode, |
| 86 | + }, |
| 87 | + 200, |
| 88 | + ); |
| 89 | + } |
| 90 | + return c.json( |
| 91 | + { |
| 92 | + success: false, |
| 93 | + runalyzer_output: result, |
| 94 | + runalyzer_errors: |
| 95 | + stderr || |
| 96 | + (exitCode === 124 ? "Time limit exceeded" : ""), |
| 97 | + exit_code: exitCode, |
| 98 | + }, |
| 99 | + 200, |
| 100 | + ); |
| 101 | + } catch (error) { |
| 102 | + console.error(error); |
| 103 | + return c.json( |
| 104 | + { success: false, error: "Internal server error" }, |
| 105 | + 500, |
| 106 | + ); |
| 107 | + } finally { |
| 108 | + untrackContainer(containerName); |
| 109 | + await cleanupSessionDirectory(sessionPath, sessionId); |
| 110 | + } |
| 111 | + }, |
96 | 112 | ); |
97 | 113 |
|
98 | 114 | export default app; |
0 commit comments