Public: anyone can read this; the crew can change it.

GET /api/v1/passport/health names its Server-Timing phases: session, read #5325

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/forge:scout/5312-get-api-v1-passport-health-names-its-ser → main 1 commit fork

  • Merged. scout/5312-get-api-v1-passport-health-names-its-ser was merged into main as 98a7888.

Review

Sign in to review this change.
2 files changed +59 −8
src/http/passport_edge.ts +12 −8
@@ -18,6 +18,7 @@ import { verify } from "../kernel/passport.ts";
1818 import type { Handler } from "./context.ts";
1919 import { apiError, json } from "./respond.ts";
2020 import type { Router } from "./router.ts";
21 import { timePhase } from "../core/route_timing.ts";
2122
2223 export const PASSPORT_AUDIENCE = "forge";
2324 export const HEALTH_SCOPE = "health:read";
@@ -25,15 +26,18 @@ export const HEALTH_SCOPE = "health:read";
2526 export function passportRoutes(router: Router<Handler>): void {
2627 router.add("GET", "/api/v1/passport/health", (ctx) => {
2728 const header = ctx.request.headers.get("authorization") ?? "";
28 const seen = header.startsWith("Passport ")
29 ? verify(ctx.app.config.dataDir, header.slice(9).trim(), {
30 audience: PASSPORT_AUDIENCE,
31 scope: HEALTH_SCOPE,
32 now: Date.now(),
33 })
34 : undefined;
29 const seen = timePhase(ctx.request, "session", () =>
30 header.startsWith("Passport ")
31 ? verify(ctx.app.config.dataDir, header.slice(9).trim(), {
32 audience: PASSPORT_AUDIENCE,
33 scope: HEALTH_SCOPE,
34 now: Date.now(),
35 })
36 : undefined,
37 );
3538 if (!seen?.ok)
3639 return apiError("unauthorized", 401, "a valid passport for this is needed");
37 return json(readHealth(ctx.app, undefined), 200, { "cache-control": "no-store" });
40 const health = timePhase(ctx.request, "read", () => readHealth(ctx.app, undefined));
41 return json(health, 200, { "cache-control": "no-store" });
3842 });
3943 }
test/passport-health-timing.test.ts added +47
@@ -0,0 +1,47 @@
1 /** GET /api/v1/passport/health names its phases in Server-Timing. */
2 import { afterAll, beforeAll, expect, test } from "bun:test";
3 import { mkdtemp, rm } from "node:fs/promises";
4 import { tmpdir } from "node:os";
5 import { join } from "node:path";
6 import { createApp, type App } from "../src/app.ts";
7 import { serve, type Servers } from "../src/http/server.ts";
8 import { issue } from "../src/kernel/passport.ts";
9
10 let dataDir = "";
11 let app: App, servers: Servers;
12
13 beforeAll(async () => {
14 dataDir = await mkdtemp(join(tmpdir(), "forge-pttime-"));
15 app = createApp({ dataDir, port: 0 });
16 servers = await serve(app);
17 });
18
19 afterAll(async () => {
20 servers.stop();
21 app.close();
22 await rm(dataDir, { recursive: true, force: true });
23 });
24
25 const phases = (res: Response) =>
26 (res.headers.get("server-timing") ?? "").split(", ").map((p) => p.split(";")[0]!).filter((n) => n && n !== "app");
27
28 const ask = (token?: string) =>
29 fetch(`${servers.url}/api/v1/passport/health`, {
30 headers: token === undefined ? {} : { authorization: `Passport ${token}` },
31 });
32
33 test("a good passport names session and read in order", async () => {
34 const r = issue(dataDir, { iss: "runner", aud: "forge", scopes: ["health:read"], ttlMs: 60_000, now: Date.now() });
35 if (!r.ok) throw new Error(r.error);
36 const res = await ask(r.value);
37 await res.arrayBuffer();
38 expect(res.status).toBe(200);
39 expect(phases(res)).toEqual(["session", "read"]);
40 });
41
42 test("a refused passport names session only", async () => {
43 const res = await ask();
44 await res.arrayBuffer();
45 expect(res.status).toBe(401);
46 expect(phases(res)).toEqual(["session"]);
47 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs