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

The OpenAPI document describes a person's sign-ins and ending one #5192

Merged opened this

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

Merged

scout/forge:scout/5150-the-openapi-document-describes-a-person → main 5 commits fork 1 thread · 1 unresolved

  • Merged. scout/5150-the-openapi-document-describes-a-person was merged into main as 48cb64e.

Review

Sign in to review this change.
3 files changed +37 −2
src/contract/openapi.ts +23
@@ -280,6 +280,29 @@ export const ROUTES: readonly Route[] = [
280280 status: 204,
281281 refusals: { forbidden: "the caller holds a bearer token, which cannot manage tokens" },
282282 },
283 {
284 method: "get",
285 path: "/api/v1/users/{username}/signins",
286 summary: "List where a person is signed in: answers { tokens, sessions }, each token { id, name, scope, createdAt, lastUsedAt, expiresAt } and each live browser session { id, createdAt, lastSeenAt, expiresAt, current }; a session's secret and a token's value are never answered; the person themself or the forge's owner, with a browser session, a password or an admin-scope token",
287 auth: true,
288 refusals: {
289 unauthorized: "no credentials were sent",
290 forbidden: "the caller holds a token below admin scope",
291 not_found: "no such account, or it is not one the caller may see",
292 },
293 },
294 {
295 method: "delete",
296 path: "/api/v1/users/{username}/sessions/{id}",
297 summary: "End one browser session of a person, as when a device is lost; the person themself or the forge's owner, with a browser session, a password or an admin-scope token; answers 204 and never a session secret",
298 auth: true,
299 status: 204,
300 refusals: {
301 unauthorized: "no credentials were sent",
302 forbidden: "the caller holds a token below admin scope",
303 not_found: "no such account or session, or it is not one the caller may end",
304 },
305 },
283306 { method: "get", path: "/api/v1/telemetry/system", summary: "Read the forge's system view; the owner or an agent holding telemetry:read, with a ro token; every agent read is logged", auth: true },
284307 { method: "get", path: "/api/v1/telemetry/flow", summary: "Read the forge's flow view; the owner or an agent holding telemetry:read, with a ro token; every agent read is logged", auth: true },
285308 { method: "get", path: "/api/v1/telemetry/sources", summary: "List telemetry sources; answers { sources }; the owner or an agent holding telemetry:read", auth: true },
test/openapi-coverage.test.ts −2
@@ -20,7 +20,6 @@ const NOT_YET_DESCRIBED: readonly string[] = [
2020 "DELETE /api/v1/user/origin-apps/{}",
2121 "DELETE /api/v1/user/origins/{}",
2222 "DELETE /api/v1/user/pins/{}/{}",
23 "DELETE /api/v1/users/{}/sessions/{}",
2423 "GET /api/v1/agents",
2524 "GET /api/v1/asks",
2625 "GET /api/v1/boards",
@@ -80,7 +79,6 @@ const NOT_YET_DESCRIBED: readonly string[] = [
8079 "GET /api/v1/user/pins",
8180 "GET /api/v1/users/{}",
8281 "GET /api/v1/users/{}/repos",
83 "GET /api/v1/users/{}/signins",
8482 "PATCH /api/v1/repos/{}/{}/pulls/{}",
8583 "PATCH /api/v1/user",
8684 "POST /api/v1/asks/bulk",
test/openapi.test.ts +14
@@ -202,6 +202,20 @@ test("an app's routes are described, and env values are never read back", () =>
202202 expect(doc.paths[`${A}/{env}/redeploy`].post.responses["202"]).toBeDefined();
203203 });
204204
205 test("a person's sign-ins and ending one are described, with no session secret", () => {

This test covers the rows, but the coverage list in test/openapi-coverage.test.ts (lines 23 and 83) still exempts both routes. Delete those two entries so the coverage test enforces these rows from now on.

206 const doc = openapi("https://f.test", "1") as any;
207 const list = doc.paths["/api/v1/users/{username}/signins"]?.get;
208 const end = doc.paths["/api/v1/users/{username}/sessions/{id}"]?.delete;
209 expect(list).toBeDefined();
210 expect(end).toBeDefined();
211 expect(list.security).toBeDefined();
212 expect(list.summary).toContain("admin-scope");
213 expect(end.responses["204"]).toBeDefined();
214 expect(end.responses.default.description).toContain("not_found");
215 expect(list.responses.default.description).toContain("unauthorized");
216 expect(list.responses.default.description).toContain("forbidden");
217 });
218
205219 test("a repository's code writes are described: branches and contents", () => {
206220 const doc = openapi("https://f.test", "1") as any;
207221 const R = "/api/v1/repos/{owner}/{name}";

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs