fix(httpapi): return request not found errors (#28693)
This commit is contained in:
@@ -224,9 +224,7 @@ const scenarios: Scenario[] = [
|
||||
headers: ctx.headers(),
|
||||
body: { reply: "once" },
|
||||
}))
|
||||
.json(200, (body) => {
|
||||
check(body === true, "permission reply should return true even when request is no longer pending")
|
||||
}),
|
||||
.json(404, object, "status"),
|
||||
http.protected.get("/question", "question.list").json(200, array),
|
||||
http.protected
|
||||
.post("/question/{requestID}/reply", "question.reply.invalid")
|
||||
@@ -243,18 +241,14 @@ const scenarios: Scenario[] = [
|
||||
headers: ctx.headers(),
|
||||
body: { answers: [["Yes"]] },
|
||||
}))
|
||||
.json(200, (body) => {
|
||||
check(body === true, "question reply should return true even when request is no longer pending")
|
||||
}),
|
||||
.json(404, object, "status"),
|
||||
http.protected
|
||||
.post("/question/{requestID}/reject", "question.reject")
|
||||
.at((ctx) => ({
|
||||
path: route("/question/{requestID}/reject", { requestID: "que_httpapi_reject" }),
|
||||
headers: ctx.headers(),
|
||||
}))
|
||||
.json(200, (body) => {
|
||||
check(body === true, "question reject should return true even when request is no longer pending")
|
||||
}),
|
||||
.json(404, object, "status"),
|
||||
http.protected
|
||||
.get("/file", "file.list")
|
||||
.seeded((ctx) => ctx.file("hello.txt", "hello\n"))
|
||||
@@ -1249,9 +1243,7 @@ const scenarios: Scenario[] = [
|
||||
headers: ctx.headers(),
|
||||
body: { response: "once" },
|
||||
}))
|
||||
.json(200, (body) => {
|
||||
check(body === true, "deprecated permission response should return true")
|
||||
}),
|
||||
.json(404, object, "status"),
|
||||
http.protected
|
||||
.post("/session/{sessionID}/share", "session.share")
|
||||
.mutating()
|
||||
|
||||
@@ -8,6 +8,8 @@ import { WorkspaceID } from "../../src/control-plane/schema"
|
||||
import { ControlPaths } from "../../src/server/routes/instance/httpapi/groups/control"
|
||||
import { InstancePaths } from "../../src/server/routes/instance/httpapi/groups/instance"
|
||||
import { SessionPaths } from "../../src/server/routes/instance/httpapi/groups/session"
|
||||
import { PermissionID } from "../../src/permission/schema"
|
||||
import { QuestionID } from "../../src/question/schema"
|
||||
import { HttpApiApp } from "../../src/server/routes/instance/httpapi/server"
|
||||
import { HEADER as FenceHeader } from "../../src/server/shared/fence"
|
||||
import { resetDatabase } from "../fixture/db"
|
||||
@@ -151,6 +153,58 @@ describe("instance HttpApi", () => {
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("returns typed not found bodies for missing permission and question requests", () =>
|
||||
Effect.gen(function* () {
|
||||
const dir = yield* tmpdirScoped({ git: true })
|
||||
const request = (path: string, init?: RequestInit) =>
|
||||
Effect.promise(() =>
|
||||
HttpApiApp.webHandler().handler(
|
||||
new Request(`http://localhost${path}`, {
|
||||
...init,
|
||||
headers: { "x-opencode-directory": dir, "content-type": "application/json", ...init?.headers },
|
||||
}),
|
||||
handlerContext,
|
||||
),
|
||||
)
|
||||
const permissionID = PermissionID.ascending()
|
||||
const questionReplyID = QuestionID.ascending()
|
||||
const questionRejectID = QuestionID.ascending()
|
||||
const [permission, questionReply, questionReject] = yield* Effect.all(
|
||||
[
|
||||
request(`/permission/${permissionID}/reply`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ reply: "once" }),
|
||||
}),
|
||||
request(`/question/${questionReplyID}/reply`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ answers: [["Yes"]] }),
|
||||
}),
|
||||
request(`/question/${questionRejectID}/reject`, { method: "POST" }),
|
||||
],
|
||||
{ concurrency: "unbounded" },
|
||||
)
|
||||
|
||||
expect(permission.status).toBe(404)
|
||||
expect(yield* Effect.promise(() => permission.json())).toEqual({
|
||||
_tag: "PermissionNotFoundError",
|
||||
requestID: permissionID,
|
||||
message: `Permission request not found: ${permissionID}`,
|
||||
})
|
||||
expect(questionReply.status).toBe(404)
|
||||
expect(yield* Effect.promise(() => questionReply.json())).toEqual({
|
||||
_tag: "QuestionNotFoundError",
|
||||
requestID: questionReplyID,
|
||||
message: `Question request not found: ${questionReplyID}`,
|
||||
})
|
||||
expect(questionReject.status).toBe(404)
|
||||
expect(yield* Effect.promise(() => questionReject.json())).toEqual({
|
||||
_tag: "QuestionNotFoundError",
|
||||
requestID: questionRejectID,
|
||||
message: `Question request not found: ${questionRejectID}`,
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("serves path and VCS read endpoints", () =>
|
||||
Effect.gen(function* () {
|
||||
const dir = yield* tmpdirScoped({ git: true })
|
||||
|
||||
@@ -157,4 +157,20 @@ describe("PublicApi OpenAPI v2 errors", () => {
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
test("documents permission and question not-found errors", () => {
|
||||
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
|
||||
|
||||
expect(componentName(responseRef(spec.paths["/permission/{requestID}/reply"]?.post?.responses?.["404"]) ?? "")).toBe(
|
||||
"PermissionNotFoundError",
|
||||
)
|
||||
for (const route of [
|
||||
["post", "/question/{requestID}/reply"],
|
||||
["post", "/question/{requestID}/reject"],
|
||||
] as const) {
|
||||
expect(componentName(responseRef(spec.paths[route[1]]?.[route[0]]?.responses?.["404"]) ?? "")).toBe(
|
||||
"QuestionNotFoundError",
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -821,19 +821,24 @@ describe("session HttpApi", () => {
|
||||
}),
|
||||
).toMatchObject({ id: session.id })
|
||||
|
||||
expect(
|
||||
yield* requestJson<boolean>(
|
||||
pathFor(SessionPaths.permissions, {
|
||||
sessionID: session.id,
|
||||
permissionID: String(PermissionID.ascending()),
|
||||
}),
|
||||
{
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({ response: "once" }),
|
||||
},
|
||||
),
|
||||
).toBe(true)
|
||||
const permissionID = String(PermissionID.ascending())
|
||||
const permission = yield* request(
|
||||
pathFor(SessionPaths.permissions, {
|
||||
sessionID: session.id,
|
||||
permissionID,
|
||||
}),
|
||||
{
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify({ response: "once" }),
|
||||
},
|
||||
)
|
||||
expect(permission.status).toBe(404)
|
||||
expect(yield* responseJson(permission)).toEqual({
|
||||
_tag: "PermissionNotFoundError",
|
||||
requestID: permissionID,
|
||||
message: `Permission request not found: ${permissionID}`,
|
||||
})
|
||||
}),
|
||||
{ git: true, config: { formatter: false, lsp: false } },
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user