Public: anyone can read this; the crew can change it.
The OpenAPI document describes a pull's review states and attaching a look #5402
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/5382-the-openapi-document-describes-a-pull-s → main 4 commits fork
6 files
changed +66
−3
src/contract/openapi/routes/getApiV1ReposOwnerNamePullsNumberReviewStates.ts added +8
| @@ -0,0 +1,8 @@ | ||
| 1 | import { REPO, type Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { | |
| 4 | method: "get", | |
| 5 | path: `${REPO}/pulls/{number}/review-states`, | |
| 6 | summary: "List a pull's reviews, the verdicts (approved, changes_requested or commented), each with its body", | |
| 7 | auth: false, | |
| 8 | }; |
src/contract/openapi/routes/index.ts +3
| @@ -101,6 +101,7 @@ export { route as getApiV1ReposOwnerNamePrompts } from "./getApiV1ReposOwnerName | ||
| 101 | 101 | export { route as getApiV1ReposOwnerNamePulls } from "./getApiV1ReposOwnerNamePulls.ts"; |
| 102 | 102 | export { route as getApiV1ReposOwnerNamePullsNumber } from "./getApiV1ReposOwnerNamePullsNumber.ts"; |
| 103 | 103 | export { route as getApiV1ReposOwnerNamePullsNumberChecks } from "./getApiV1ReposOwnerNamePullsNumberChecks.ts"; |
| 104 | export { route as getApiV1ReposOwnerNamePullsNumberReviewStates } from "./getApiV1ReposOwnerNamePullsNumberReviewStates.ts"; | |
| 104 | 105 | export { route as getApiV1ReposOwnerNamePullsNumberReviews } from "./getApiV1ReposOwnerNamePullsNumberReviews.ts"; |
| 105 | 106 | export { route as getApiV1ReposOwnerNamePullsNumberThreads } from "./getApiV1ReposOwnerNamePullsNumberThreads.ts"; |
| 106 | 107 | export { route as getApiV1ReposOwnerNameReports } from "./getApiV1ReposOwnerNameReports.ts"; |
| @@ -217,7 +218,9 @@ export { route as postApiV1ReposOwnerNamePulls } from "./postApiV1ReposOwnerName | ||
| 217 | 218 | export { route as postApiV1ReposOwnerNamePullsNumberApproveHere } from "./postApiV1ReposOwnerNamePullsNumberApproveHere.ts"; |
| 218 | 219 | export { route as postApiV1ReposOwnerNamePullsNumberChecks } from "./postApiV1ReposOwnerNamePullsNumberChecks.ts"; |
| 219 | 220 | export { route as postApiV1ReposOwnerNamePullsNumberComments } from "./postApiV1ReposOwnerNamePullsNumberComments.ts"; |
| 221 | export { route as postApiV1ReposOwnerNamePullsNumberLooks } from "./postApiV1ReposOwnerNamePullsNumberLooks.ts"; | |
| 220 | 222 | export { route as postApiV1ReposOwnerNamePullsNumberMerge } from "./postApiV1ReposOwnerNamePullsNumberMerge.ts"; |
| 223 | export { route as postApiV1ReposOwnerNamePullsNumberReviewStates } from "./postApiV1ReposOwnerNamePullsNumberReviewStates.ts"; | |
| 221 | 224 | export { route as postApiV1ReposOwnerNamePullsNumberReviews } from "./postApiV1ReposOwnerNamePullsNumberReviews.ts"; |
| 222 | 225 | export { route as postApiV1ReposOwnerNamePullsNumberThreadsAnchorResolve } from "./postApiV1ReposOwnerNamePullsNumberThreadsAnchorResolve.ts"; |
| 223 | 226 | export { route as postApiV1ReposOwnerNamePullsNumberVariant } from "./postApiV1ReposOwnerNamePullsNumberVariant.ts"; |
src/contract/openapi/routes/postApiV1ReposOwnerNamePullsNumberLooks.ts added +18
| @@ -0,0 +1,18 @@ | ||
| 1 | import { REPO, type Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { | |
| 4 | method: "post", | |
| 5 | path: `${REPO}/pulls/{number}/looks`, | |
| 6 | summary: "Attach a look to a pull: a base64 picture at a stated width with a line saying what it shows (rw scope); answers 201 with { id, width, says, by, commit, url }", | |
| 7 | auth: true, | |
| 8 | status: 201, | |
| 9 | body: { width: "string", says: "string", image: "string" }, | |
| 10 | refusals: { | |
| 11 | too_large: "the picture is over the size cap (413)", | |
| 12 | not_open: "the pull is closed or merged (409)", | |
| 13 | no_app: "the repository has no app to look at (409)", | |
| 14 | bad_image: "`image` is not an acceptable picture (422)", | |
| 15 | bad_note: "`says` is not acceptable (422)", | |
| 16 | bad_width: "`width` is not one the forge takes (422)", | |
| 17 | }, | |
| 18 | }; |
src/contract/openapi/routes/postApiV1ReposOwnerNamePullsNumberReviewStates.ts added +17
| @@ -0,0 +1,17 @@ | ||
| 1 | import { REPO, type Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { | |
| 4 | method: "post", | |
| 5 | path: `${REPO}/pulls/{number}/review-states`, | |
| 6 | summary: "Submit a verdict on a pull (rw scope); answers 201 with the review; `comments` are line comments posted with it", | |
| 7 | auth: true, | |
| 8 | status: 201, | |
| 9 | body: { state: "string", body: "string", comments: "array" }, | |
| 10 | refusals: { | |
| 11 | invalid_state: "`state` is not approved, changes_requested or commented (422)", | |
| 12 | invalid_comments: "`comments` is not a list of {path, line, side, body} (422)", | |
| 13 | self_review: "you cannot approve your own pull (422)", | |
| 14 | bad_anchor: "a comment's anchor is not in the diff; nothing was posted (422)", | |
| 15 | too_many_lines: "the review carries more line comments than the cap (422)", | |
| 16 | }, | |
| 17 | }; |
test/openapi-coverage.test.ts −3
| @@ -28,7 +28,6 @@ const NOT_YET_DESCRIBED: readonly string[] = [ | ||
| 28 | 28 | "GET /api/v1/repos/{}/{}/labels", |
| 29 | 29 | "GET /api/v1/repos/{}/{}/milestones", |
| 30 | 30 | "GET /api/v1/repos/{}/{}/milestones/{}", |
| 31 | "GET /api/v1/repos/{}/{}/pulls/{}/review-states", | |
| 32 | 31 | "GET /api/v1/user", |
| 33 | 32 | "GET /api/v1/user/assigned", |
| 34 | 33 | "GET /api/v1/user/keys", |
| @@ -58,8 +57,6 @@ const NOT_YET_DESCRIBED: readonly string[] = [ | ||
| 58 | 57 | "POST /api/v1/repos/{}/{}/prompts/{}/run", |
| 59 | 58 | "POST /api/v1/repos/{}/{}/proposal-tokens", |
| 60 | 59 | "POST /api/v1/repos/{}/{}/pulls/{}/comments", |
| 61 | "POST /api/v1/repos/{}/{}/pulls/{}/looks", | |
| 62 | "POST /api/v1/repos/{}/{}/pulls/{}/review-states", | |
| 63 | 60 | "POST /api/v1/repos/{}/{}/pulls/{}/variant", |
| 64 | 61 | "POST /api/v1/repos/{}/{}/reports/{}/accept", |
| 65 | 62 | "POST /api/v1/repos/{}/{}/reports/{}/dismiss", |
test/openapi-oa-pull-review.test.ts added +20
| @@ -0,0 +1,20 @@ | ||
| 1 | /** A pull's review states and attaching a look are in the served OpenAPI document. */ | |
| 2 | import { expect, test } from "bun:test"; | |
| 3 | import { openapi } from "../src/contract/openapi.ts"; | |
| 4 | ||
| 5 | const R = "/api/v1/repos/{owner}/{name}/pulls/{number}"; | |
| 6 | type Op = { security?: unknown; responses?: Record<string, unknown>; requestBody?: unknown }; | |
| 7 | ||
| 8 | test("review-states and looks are described with their method, auth and refusals", () => { | |
| 9 | const paths = openapi("http://x", "1").paths as Record<string, Record<string, Op>>; | |
| 10 | expect(paths[`${R}/review-states`]?.get).toBeDefined(); | |
| 11 | const post = paths[`${R}/review-states`]?.post; | |
| 12 | expect(post?.requestBody).toBeDefined(); | |
| 13 | expect(post?.responses?.["201"]).toBeDefined(); | |
| 14 | expect(JSON.stringify(post)).toContain("self_review"); | |
| 15 | const look = paths[`${R}/looks`]?.post; | |
| 16 | expect(look?.requestBody).toBeDefined(); | |
| 17 | expect(look?.responses?.["201"]).toBeDefined(); | |
| 18 | expect(JSON.stringify(look)).toContain("not_open"); | |
| 19 | expect(paths[`${R}/looks`]?.get).toBeUndefined(); | |
| 20 | }); |
new above
offline