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

GET /api/v1/admin/capacity answers `{ items, next_cursor: null }` #5161

Merged opened this

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

Merged

scout/forge:scout/5145-get-api-v1-admin-capacity-answers-items → main 2 commits fork 3 threads · 3 unresolved

  • Merged. scout/5145-get-api-v1-admin-capacity-answers-items was merged into main as 74791bc.

Review

Sign in to review this change.
4 files changed +14 −5
src/contract/openapi.ts +1 −1
@@ -197,7 +197,7 @@ export const ROUTES: readonly Route[] = [
197197 method: "get",
198198 path: "/api/v1/admin/capacity",
199199 summary:
200 "Read the forge's capacity settings (a person admin's token, any scope); answers { settings: [{ key, label, help, value, source, default, env, changed? }], changes }",
200 "Read the forge's capacity settings (a person admin's token, any scope); answers { items: [{ key, label, help, value, source, default, env, changed? }], changes, next_cursor }",
outdated

Once the handler is flattened: answers { items: [{ key, label, help, value, source, default, env, changed? }], changes, next_cursor }.

201201 auth: true,
202202 refusals: { not_found: "the token is not a person who administers the forge" },
203203 },
src/http/api.ts +1 −1
@@ -1675,7 +1675,7 @@ export function apiRoutes(router: Router<Handler>): void {
16751675 const denied = accountAdmin(ctx, "ro");
16761676 if (denied) return denied;
16771677 const listed = timePhase(ctx.request, "capacity", () => listCapacity(ctx.app, ctx.auth.user));
1678 return timePhase(ctx.request, "render", () => (listed.ok ? json(listed.value) : notFound()));
1678 return timePhase(ctx.request, "render", () => (listed.ok ? json({ items: listed.value.settings, changes: listed.value.changes, next_cursor: null }) : notFound()));
outdated

items is an object here ({ settings, changes }), but every other items in this file is an array. Follow egress-grants: json({ items: listed.value.settings, changes: listed.value.changes, next_cursor: null }).

16791679 });
16801680
16811681 router.add("PUT", "/api/v1/admin/capacity/:key", async (ctx) => {
test/capacity.test.ts +7 −1
@@ -198,7 +198,13 @@ describe("capacity: only a forge admin reads or changes it", () => {
198198 });
199199
200200 test("an admin with a read-only token may read, not change", async () => {
201 expect((await api(aliceRo, "GET", "/admin/capacity")).status).toBe(200);
201 const read = await api(aliceRo, "GET", "/admin/capacity");
202 expect(read.status).toBe(200);
203 const body = (await read.json()) as { items: unknown[]; changes: unknown[]; next_cursor: null };
outdated

Assert the list shape: Array.isArray(body.items), Array.isArray(body.changes), next_cursor === null. That test would fail on this head, and it should.

204 expect(Array.isArray(body.items)).toBe(true);
205 expect(Array.isArray(body.changes)).toBe(true);
206 expect(body.next_cursor).toBeNull();
207 expect("settings" in body).toBe(false);
202208 expect(
203209 (
204210 await api(aliceRo, "PUT", "/admin/capacity/checks.concurrency", {
test/guard-new-accounts.test.ts +5 −2
@@ -188,9 +188,12 @@ describe("guard_new_accounts", () => {
188188 headers: { authorization: `Bearer ${token}` },
189189 }).then(
190190 (r) =>
191 r.json() as Promise<{ settings: { key: string; value: string }[] }>,
191 r.json() as Promise<{
192 items: { key: string; value: string }[];
193 next_cursor: null;
194 }>,
192195 );
193 const keys = (await get()).settings.map((s) => s.key);
196 const keys = (await get()).items.map((s) => s.key);
194197 expect(keys).toContain("guard_new_accounts");
195198
196199 const put = (value: string) =>

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs