Source HTTP API ID path patterns (#26623)

This commit is contained in:
Kit Langton
2026-05-09 22:58:47 -04:00
committed by GitHub
parent 2f11c9f7ed
commit 67b9c9c027
25 changed files with 127 additions and 90 deletions
@@ -22,6 +22,7 @@ import {
MessagesQuery,
SessionPaths,
} from "../../src/server/routes/instance/httpapi/groups/session"
import { PtyPaths } from "../../src/server/routes/instance/httpapi/groups/pty"
import { MessagesQuery as V2MessagesQuery } from "../../src/server/routes/instance/httpapi/groups/v2/message"
import { SessionsQuery as V2SessionsQuery } from "../../src/server/routes/instance/httpapi/groups/v2/session"
import { QueryBoolean } from "../../src/server/routes/instance/httpapi/groups/query"
@@ -33,7 +34,12 @@ const originalWorkspaces = Flag.OPENCODE_EXPERIMENTAL_WORKSPACES
type Method = "get" | "post" | "put" | "delete" | "patch"
type QuerySchema = { readonly fields: Record<string, unknown> }
type OpenApiSchema = { readonly maximum?: number; readonly minimum?: number; readonly type?: string }
type OpenApiSchema = {
readonly maximum?: number
readonly minimum?: number
readonly pattern?: string
readonly type?: string
}
type OpenApiParameter = { readonly name: string; readonly in: string; readonly schema?: OpenApiSchema }
type OpenApiOperation = { readonly parameters?: readonly OpenApiParameter[] }
@@ -68,6 +74,16 @@ const numericSdkQueryParams = [
{ method: "get", path: "/api/session/:sessionID/message", name: "limit", schema: { type: "number" } },
] satisfies Array<{ method: Method; path: string; name: string; schema: OpenApiSchema }>
const pathParamPatterns = [
{ method: "get", path: SessionPaths.get, name: "sessionID", pattern: "^ses" },
{ method: "get", path: SessionPaths.message, name: "messageID", pattern: "^msg" },
{ method: "patch", path: SessionPaths.updatePart, name: "partID", pattern: "^prt" },
{ method: "post", path: SessionPaths.permissions, name: "permissionID", pattern: "^per" },
{ method: "post", path: "/permission/:requestID/reply", name: "requestID", pattern: "^per" },
{ method: "post", path: "/question/:requestID/reply", name: "requestID", pattern: "^que" },
{ method: "put", path: PtyPaths.update, name: "ptyID", pattern: "^pty" },
] satisfies Array<{ method: Method; path: string; name: string; pattern: string }>
function app() {
return Server.Default().app
}
@@ -98,6 +114,10 @@ function queryParameter(operation: OpenApiOperation | undefined, name: string) {
return (operation?.parameters ?? []).find((param) => param.in === "query" && param.name === name)
}
function pathParameter(operation: OpenApiOperation | undefined, name: string) {
return (operation?.parameters ?? []).find((param) => param.in === "path" && param.name === name)
}
function assertAdvertisedQueryParamsAreRuntimeFields(input: {
readonly method: Method
readonly operation: OpenApiOperation | undefined
@@ -173,6 +193,19 @@ describe("httpapi query schema drift", () => {
}),
)
it.effect(
"OpenAPI path parameter patterns come from runtime schemas",
Effect.sync(() => {
const spec = OpenApi.fromApi(PublicApi)
for (const expected of pathParamPatterns) {
expect(
pathParameter(spec.paths[openApiPath(expected.path)]?.[expected.method], expected.name)?.schema,
`${expected.method.toUpperCase()} ${expected.path} ${expected.name}`,
).toEqual({ type: "string", pattern: expected.pattern })
}
}),
)
it.effect(
"drift assertion catches spec-only workspace query params",
Effect.sync(() => {
@@ -287,7 +287,7 @@ describe("HttpApi UI fallback", () => {
})
test("keeps matched API routes ahead of the UI fallback", async () => {
const response = await Server.Default().app.request("/session/nope")
const response = await Server.Default().app.request("/session/ses_nope")
expect(response.status).toBe(404)
})