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

The OpenAPI document describes the caller's password change and email verification #5056

Merged opened this

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

Merged

scout/forge:scout/5038-the-openapi-document-describes-the-calle → main 1 commit fork

  • Merged. scout/5038-the-openapi-document-describes-the-calle was merged into main as 0e5d402.

Review

Sign in to review this change.
2 files changed +65 −0
src/contract/openapi.ts +29
@@ -152,6 +152,35 @@ export const ROUTES: readonly Route[] = [
152152 body: { source: "object", owner: "string", name: "string", visibility: "string", branch: "string", after: "string", crew: "object" },
153153 },
154154 { method: "get", path: "/api/v1/migrate/{job}", summary: "Read a migration job's stage", auth: true },
155 {
156 method: "post",
157 path: "/api/v1/user/password",
158 summary: "Change the caller's password: current is verified, next replaces it; answers 200 with {}. A person's password or an admin-scope token only",
159 auth: true,
160 body: { current: "string", next: "string" },
161 refusals: {
162 unauthorized: "401: no signed-in account",
163 forbidden: "403: a cross-origin browser request, or a token without admin scope",
164 bad_credentials: "403: current is not the password",
165 throttled: "429: too many wrong passwords from this address or for this name",
166 weak_password: "422: next is shorter than the minimum",
167 password_too_long: "422: next is longer than the cap",
168 },
169 },
170 {
171 method: "post",
172 path: "/api/v1/user/email/verify",
173 summary: "Send the caller another email verification link: {outcome} is sent (202) or already (200, nothing sent)",
174 auth: true,
175 status: 202,
176 refusals: {
177 unauthorized: "401: no signed-in account",
178 too_soon: "429: a link was sent a moment ago",
179 capped: "429: the forge's mail cap is reached",
180 no_mail: "503: this forge sends no mail",
181 failed: "502: the link could not be sent",
182 },
183 },
155184 {
156185 method: "post",
157186 path: "/mcp",
test/openapi-account.test.ts added +36
@@ -0,0 +1,36 @@
1 /** The served OpenAPI document describes the caller's password change and verification email. */
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
9 let dataDir: string;
10 let app: App;
11 let servers: Servers;
12
13 beforeAll(async () => {
14 dataDir = await mkdtemp(join(tmpdir(), "forge-openapi-account-"));
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 test("the served document has the password and email verify routes", async () => {
26 const res = await fetch(`${servers.url}/contract/openapi.json`);
27 expect(res.status).toBe(200);
28 const doc = (await res.json()) as any;
29 const password = doc.paths["/api/v1/user/password"]?.post;
30 const verify = doc.paths["/api/v1/user/email/verify"]?.post;
31 expect(password).toBeDefined();
32 expect(verify).toBeDefined();
33 expect(JSON.stringify(password.requestBody)).toContain("current");
34 expect(JSON.stringify(password)).toContain("bad_credentials");
35 expect(JSON.stringify(verify)).toContain("too_soon");
36 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs