Public: anyone can read this; the crew can change it.
The OpenAPI document describes people, organizations and agents #4857
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/4847-the-openapi-document-describes-people-or → main 5 commits fork
8 files
changed +47
−0
src/contract/openapi/routes/getApiV1Agents.ts added +3
| @@ -0,0 +1,3 @@ | ||
| 1 | import type { Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { method: "get", path: "/api/v1/agents", summary: "List the crew's agents; answers { items, next_cursor: null }; needs a signed-in caller, anonymous gets 401", auth: true }; |
src/contract/openapi/routes/getApiV1OrgsOrg.ts added +3
| @@ -0,0 +1,3 @@ | ||
| 1 | import type { Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { method: "get", path: "/api/v1/orgs/{org}", summary: "Read one organization's public profile plus `teams`, the teams the caller may see: all for an org admin, only their own for a member, [] for a stranger or anonymous caller", auth: false }; |
src/contract/openapi/routes/getApiV1OrgsOrgTeams.ts added +3
| @@ -0,0 +1,3 @@ | ||
| 1 | import type { Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { method: "get", path: "/api/v1/orgs/{org}/teams", summary: "List an organization's teams; answers { items, next_cursor }, paged by `limit` and `cursor`: all for an org admin, only the caller's own teams for a member, [] for a stranger or anonymous caller", auth: false, query: { limit: "integer", cursor: "string" } }; |
src/contract/openapi/routes/getApiV1OrgsOrgTeamsTeamMembers.ts added +3
| @@ -0,0 +1,3 @@ | ||
| 1 | import type { Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { method: "get", path: "/api/v1/orgs/{org}/teams/{team}/members", summary: "List a team's members as public profiles; answers { items, next_cursor }, paged by `limit` and `cursor`; 404 when the caller may not see the team", auth: false, query: { limit: "integer", cursor: "string" } }; |
src/contract/openapi/routes/getApiV1UsersUsername.ts added +3
| @@ -0,0 +1,3 @@ | ||
| 1 | import type { Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { method: "get", path: "/api/v1/users/{username}", summary: "Read one person's public profile; 404 when they do not exist or hid themselves from the caller", auth: false }; |
src/contract/openapi/routes/getApiV1UsersUsernameRepos.ts added +3
| @@ -0,0 +1,3 @@ | ||
| 1 | import type { Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { method: "get", path: "/api/v1/users/{username}/repos", summary: "List the repositories a person owns that the caller may see (private ones only to those with access); answers { items, next_cursor }, paged by `limit` and `cursor`", auth: false, query: { limit: "integer", cursor: "string" } }; |
src/contract/openapi/routes/index.ts +6
| @@ -29,6 +29,7 @@ export { route as getApiV1AdminLimits } from "./getApiV1AdminLimits.ts"; | ||
| 29 | 29 | export { route as getApiV1AdminPeople } from "./getApiV1AdminPeople.ts"; |
| 30 | 30 | export { route as getApiV1AdminSettings } from "./getApiV1AdminSettings.ts"; |
| 31 | 31 | export { route as getApiV1AdminTeams } from "./getApiV1AdminTeams.ts"; |
| 32 | export { route as getApiV1Agents } from "./getApiV1Agents.ts"; | |
| 32 | 33 | export { route as getApiV1AgentsUsername } from "./getApiV1AgentsUsername.ts"; |
| 33 | 34 | export { route as getApiV1Apps } from "./getApiV1Apps.ts"; |
| 34 | 35 | export { route as getApiV1Asked } from "./getApiV1Asked.ts"; |
| @@ -49,6 +50,9 @@ export { route as getApiV1Map } from "./getApiV1Map.ts"; | ||
| 49 | 50 | export { route as getApiV1MigrateJob } from "./getApiV1MigrateJob.ts"; |
| 50 | 51 | export { route as getApiV1MigrateJobStream } from "./getApiV1MigrateJobStream.ts"; |
| 51 | 52 | export { route as getApiV1OpenapiJson } from "./getApiV1OpenapiJson.ts"; |
| 53 | export { route as getApiV1OrgsOrg } from "./getApiV1OrgsOrg.ts"; | |
| 54 | export { route as getApiV1OrgsOrgTeams } from "./getApiV1OrgsOrgTeams.ts"; | |
| 55 | export { route as getApiV1OrgsOrgTeamsTeamMembers } from "./getApiV1OrgsOrgTeamsTeamMembers.ts"; | |
| 52 | 56 | export { route as getApiV1PassportHealth } from "./getApiV1PassportHealth.ts"; |
| 53 | 57 | export { route as getApiV1Repos } from "./getApiV1Repos.ts"; |
| 54 | 58 | export { route as getApiV1ReposOwnerName } from "./getApiV1ReposOwnerName.ts"; |
| @@ -111,6 +115,8 @@ export { route as getApiV1UserOriginsIdBrowseOwnerNamePulls } from "./getApiV1Us | ||
| 111 | 115 | export { route as getApiV1UserOriginsIdBrowseOwnerNamePullsNumber } from "./getApiV1UserOriginsIdBrowseOwnerNamePullsNumber.ts"; |
| 112 | 116 | export { route as getApiV1UserOriginsIdMirrorJob } from "./getApiV1UserOriginsIdMirrorJob.ts"; |
| 113 | 117 | export { route as getApiV1UserOriginsIdMirrorJobStream } from "./getApiV1UserOriginsIdMirrorJobStream.ts"; |
| 118 | export { route as getApiV1UsersUsername } from "./getApiV1UsersUsername.ts"; | |
| 119 | export { route as getApiV1UsersUsernameRepos } from "./getApiV1UsersUsernameRepos.ts"; | |
| 114 | 120 | export { route as getApiV1UsersUsernameSignins } from "./getApiV1UsersUsernameSignins.ts"; |
| 115 | 121 | export { route as getMetrics } from "./getMetrics.ts"; |
| 116 | 122 | export { route as patchApiV1AdminSettings } from "./patchApiV1AdminSettings.ts"; |
test/openapi.test.ts +23
| @@ -328,3 +328,26 @@ test("the origin applications are described, and a client secret is written, nev | ||
| 328 | 328 | expect(list.get.summary).toContain("hasSecret"); |
| 329 | 329 | expect(JSON.stringify(add.responses["201"])).not.toContain("clientSecret"); |
| 330 | 330 | }); |
| 331 | ||
| 332 | test("people, organizations and agents are described", () => { | |
| 333 | const doc = openapi("https://f.test", "1") as any; | |
| 334 | for (const path of [ | |
| 335 | "/api/v1/users/{username}", | |
| 336 | "/api/v1/users/{username}/repos", | |
| 337 | "/api/v1/orgs/{org}", | |
| 338 | "/api/v1/orgs/{org}/teams", | |
| 339 | "/api/v1/orgs/{org}/teams/{team}/members", | |
| 340 | "/api/v1/agents", | |
| 341 | ]) | |
| 342 | expect(doc.paths[path]?.get, `get ${path}`).toBeDefined(); | |
| 343 | for (const path of [ | |
| 344 | "/api/v1/users/{username}/repos", | |
| 345 | "/api/v1/orgs/{org}/teams", | |
| 346 | "/api/v1/orgs/{org}/teams/{team}/members", | |
| 347 | ]) { | |
| 348 | const get = doc.paths[path].get; | |
| 349 | expect(get.parameters.filter((p: any) => p.in === "query").map((p: any) => p.name), path).toEqual(["limit", "cursor"]); | |
| 350 | expect(get.summary, path).toContain("next_cursor"); | |
| 351 | expect(get.summary, path).not.toContain("bare array"); | |
| 352 | } | |
| 353 | }); |
new above
offline