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

The OpenAPI document describes the forge's admin settings: settings, limits, capacity, teams and people #4876

Merged opened this

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

Merged

scout/forge:scout/4867-the-openapi-document-describes-the-forge → main 1 commit fork 1 thread · 1 unresolved

  • Merged. scout/4867-the-openapi-document-describes-the-forge was merged into main as 3ffccdf.

Review

Sign in to review this change.
2 files changed +71 −0
src/contract/openapi.ts +54
@@ -66,6 +66,60 @@ export const ROUTES: readonly Route[] = [
6666 auth: false,
6767 body: { title: "string", body: "string", page: "string" },
6868 },
69 {
70 method: "get",
71 path: "/api/v1/admin/settings",
72 summary:
73 "Read the forge's signup mode and account quotas (a person admin's token, any scope); answers { quotas, signup, signupChanges, changes }",
74 auth: true,
75 refusals: { not_found: "the token is not a person who administers the forge" },
76 },
77 {
78 method: "patch",
79 path: "/api/v1/admin/settings",
80 summary:
81 "Change the signup mode (open, invite or closed) or the account quotas, one of the two per call (a person admin's token, admin scope); answers { signup } or { quotas }; a quota is a positive whole number, or null for no limit",
82 auth: true,
83 body: { signup: "string", quotas: "object" },
84 refusals: {
85 invalid_body: "neither signup nor quotas was sent, or both were",
86 invalid_signup: "signup is not open, invite or closed",
87 no_such_quota: "a quota key is unknown",
88 invalid_quota: "a quota is not a positive whole number or null",
89 not_found: "the token is not a person who administers the forge",
90 },
91 },
92 {
93 method: "get",
94 path: "/api/v1/admin/limits",
95 summary:
96 "Read the forge's limits (a person admin's token, any scope); answers { limits: [{ key, value, isSet, defaultValue, ... }], changes }",
97 auth: true,
98 refusals: { not_found: "the token is not a person who administers the forge" },
99 },
100 {
101 method: "get",
102 path: "/api/v1/admin/capacity",
103 summary:
104 "Read the forge's capacity settings (a person admin's token, any scope); answers { settings: [{ key, label, help, value, source, default, env, changed? }], changes }",
105 auth: true,
106 refusals: { not_found: "the token is not a person who administers the forge" },
107 },
108 {
109 method: "get",
110 path: "/api/v1/admin/teams",
111 summary:
112 "Read the crew's teams (a person admin's token, any scope); answers { teams: [{ slug, charter, dial, members, ... }] }",
113 auth: true,
114 refusals: { not_found: "the token is not a person who administers the forge" },
115 },
116 {
117 method: "get",
118 path: "/api/v1/admin/people",
119 summary:
120 "List every person and their role (admin scope); answers { people: [{ username, role, usdPerMonth }] }, role being admin, member or guest",
121 auth: true,
122 },

Nit: accountAdmin answers 404 to a non-admin here as on the other five rows, but this row lists no refusals. Add refusals: { not_found: "the token is not a person who administers the forge" }.

69123 { method: "get", path: `${REPO}/issues`, summary: "List a repository's issues; fields=brief leaves out bodies and comments, any other fields value is a 422 invalid_fields", auth: false, query: { ...LIST_QUERY, fields: "string" } },
70124 { method: "get", path: `${REPO}/pulls`, summary: "List a repository's pulls", auth: false, query: LIST_QUERY },
71125 {
test/openapi-oa-adm.test.ts added +17
@@ -0,0 +1,17 @@
1 /** The OpenAPI document describes the forge's admin settings routes. */
2 import { expect, test } from "bun:test";
3 import { ROUTES } from "../src/contract/openapi.ts";
4
5 const WANTED: readonly [string, string][] = [
6 ["get", "/api/v1/admin/settings"],
7 ["patch", "/api/v1/admin/settings"],
8 ["get", "/api/v1/admin/limits"],
9 ["get", "/api/v1/admin/capacity"],
10 ["get", "/api/v1/admin/teams"],
11 ["get", "/api/v1/admin/people"],
12 ];
13
14 test("each admin settings path and method is in the document", () => {
15 for (const [method, path] of WANTED)
16 expect(ROUTES.some((r) => r.method === method && r.path === path && r.auth)).toBe(true);
17 });

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs