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

The OpenAPI document describes mirroring and forking from an origin, its job and stream, and origin apps #5456

Merged opened this

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

Merged

scout/forge:scout/5152-the-openapi-document-describes-mirroring → main 4 commits fork

  • Merged. scout/5152-the-openapi-document-describes-mirroring was merged into main as 440ca64.

Review

Sign in to review this change.
2 files changed +80 −0
src/contract/openapi.ts +59
@@ -737,6 +737,65 @@ export const ROUTES: readonly Route[] = [
737737 auth: true,
738738 body: { originId: "string" },
739739 },
740 {
741 method: "post",
742 path: "/api/v1/user/origins/{id}/mirror",
743 summary: "Mirror a repository from one of your origins as a job; answers 202 with { job } to poll or stream",
744 auth: true,
745 body: { fullName: "string", name: "string" },
746 status: 202,
747 refusals: { not_found: "404: no such origin of yours, or no such repository there" },
748 },
749 {
750 method: "post",
751 path: "/api/v1/user/origins/{id}/fork",
752 summary: "Fork a repository at its origin, then mirror the fork as a job; answers 202 with { remote, existed, job, mirroredAs }",
753 auth: true,
754 body: { fullName: "string" },
755 status: 202,
756 refusals: {
757 not_found: "404: no such origin of yours",
758 origin_unreachable: "422: the origin could not be reached",
759 fork_timeout: "504: the origin took the fork and it has not appeared yet; try again shortly",
760 mirror_failed: "502: a local copy of the fork could not be made",
761 },
762 },
763 {
764 method: "get",
765 path: "/api/v1/user/origins/{id}/mirror/{job}",
766 summary: "Read a mirror job's stage: { job }; stages run creating, fetching, labels, issues, pulls, comments, crew, then done or failed",
767 auth: true,
768 refusals: { not_found: "404: no such origin or job of yours" },
769 },
770 {
771 method: "get",
772 path: "/api/v1/user/origins/{id}/mirror/{job}/stream",
773 summary: "Watch a mirror job live; answers text/event-stream, one event per stage (mirror.creating, mirror.fetching, mirror.labels, mirror.issues, mirror.pulls, mirror.comments, mirror.crew, mirror.done, mirror.failed) carrying the job, and ends after done or failed",
774 auth: true,
775 refusals: { not_found: "404: no such origin or job of yours" },
776 },
777 { method: "get", path: "/api/v1/user/origin-apps", summary: "List your registered OAuth applications, one per origin host", auth: true },
778 {
779 method: "post",
780 path: "/api/v1/user/origin-apps",
781 summary: "Register an OAuth application for an origin host (a forgejo or gitea needs its own; github may fall back to the forge's); answers 201 with the application",
782 auth: true,
783 body: { host: "string", kind: "string", clientId: "string", clientSecret: "string" },
784 status: 201,
785 refusals: {
786 unknown_host: "422: that does not look like a forge host",
787 private_host: "422: that address is inside a network, not on the internet",
788 unreachable: "422: that host could not be reached",
789 },
790 },
791 {
792 method: "delete",
793 path: "/api/v1/user/origin-apps/{id}",
794 summary: "Remove a registered OAuth application of yours; answers 204",
795 auth: true,
796 status: 204,
797 refusals: { not_found: "404: no such application of yours" },
798 },
740799 { method: "get", path: `${REPO}/forks`, summary: "List the forks of a repository you can read", auth: false },
741800 { method: "get", path: `${REPO}/tags`, summary: "List a repository's tags: [{ name, oid, commit }]", auth: false },
742801 {
test/openapi.test.ts +21
@@ -220,6 +220,27 @@ test("an app's routes are described, and env values are never read back", () =>
220220 expect(doc.paths[`${A}/{env}/redeploy`].post.responses["202"]).toBeDefined();
221221 });
222222
223 test("mirroring and forking from an origin, its job and stream, and origin apps are described", () => {
224 const doc = openapi("https://f.test", "1") as any;
225 const want: [string, string][] = [
226 ["/api/v1/user/origins/{id}/mirror", "post"],
227 ["/api/v1/user/origins/{id}/fork", "post"],
228 ["/api/v1/user/origins/{id}/mirror/{job}", "get"],
229 ["/api/v1/user/origins/{id}/mirror/{job}/stream", "get"],
230 ["/api/v1/migrate/{job}/stream", "get"],
231 ["/api/v1/user/origin-apps", "get"],
232 ["/api/v1/user/origin-apps", "post"],
233 ["/api/v1/user/origin-apps/{id}", "delete"],
234 ];
235 for (const [path, method] of want) {
236 expect(doc.paths[path]?.[method]).toBeDefined();
237 expect(doc.paths[path][method].security).toBeDefined();
238 }
239 const row = ROUTES.find((r: any) => r.path === "/api/v1/user/origins/{id}/mirror/{job}/stream") as any;
240 expect(row.summary).toContain("text/event-stream");
241 expect(row.summary).toContain("mirror.done");
242 });
243
223244 test("the admin's writes are described: a capacity setting, a limit and a team", () => {
224245 const doc = openapi("https://f.test", "1") as any;
225246 for (const path of ["capacity/{key}", "limits/{key}", "teams/{slug}"]) {

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs