mirror of
https://github.com/anomalyco/opencode.git
synced 2026-05-22 11:25:15 +00:00
fix(httpapi): return session busy error bodies (#28684)
This commit is contained in:
parent
6c24062d2f
commit
82b796ce31
4 changed files with 48 additions and 8 deletions
|
|
@ -19,7 +19,7 @@ import {
|
|||
WorkspaceRoutingQuery,
|
||||
WorkspaceRoutingQueryFields,
|
||||
} from "../middleware/workspace-routing"
|
||||
import { ApiNotFoundError } from "../errors"
|
||||
import { ApiNotFoundError, SessionBusyError } from "../errors"
|
||||
import { described } from "./metadata"
|
||||
import { QueryBoolean } from "./query"
|
||||
|
||||
|
|
@ -354,7 +354,7 @@ export const SessionApi = HttpApi.make("session")
|
|||
query: WorkspaceRoutingQuery,
|
||||
payload: ShellPayload,
|
||||
success: described(MessageV2.WithParts, "Created message"),
|
||||
error: [HttpApiError.BadRequest, ApiNotFoundError],
|
||||
error: [HttpApiError.BadRequest, ApiNotFoundError, SessionBusyError],
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.shell",
|
||||
|
|
@ -367,7 +367,7 @@ export const SessionApi = HttpApi.make("session")
|
|||
query: WorkspaceRoutingQuery,
|
||||
payload: RevertPayload,
|
||||
success: described(Session.Info, "Updated session"),
|
||||
error: [HttpApiError.BadRequest, ApiNotFoundError],
|
||||
error: [HttpApiError.BadRequest, ApiNotFoundError, SessionBusyError],
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.revert",
|
||||
|
|
@ -380,7 +380,7 @@ export const SessionApi = HttpApi.make("session")
|
|||
params: { sessionID: SessionID },
|
||||
query: WorkspaceRoutingQuery,
|
||||
success: described(Session.Info, "Updated session"),
|
||||
error: [HttpApiError.BadRequest, ApiNotFoundError],
|
||||
error: [HttpApiError.BadRequest, ApiNotFoundError, SessionBusyError],
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.unrevert",
|
||||
|
|
@ -406,7 +406,7 @@ export const SessionApi = HttpApi.make("session")
|
|||
params: { sessionID: SessionID, messageID: MessageID },
|
||||
query: WorkspaceRoutingQuery,
|
||||
success: described(Schema.Boolean, "Successfully deleted message"),
|
||||
error: [HttpApiError.BadRequest, ApiNotFoundError],
|
||||
error: [HttpApiError.BadRequest, ApiNotFoundError, SessionBusyError],
|
||||
}).annotateMerge(
|
||||
OpenApi.annotations({
|
||||
identifier: "session.deleteMessage",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import type { NotFoundError as StorageNotFoundError } from "@/storage/storage"
|
||||
import type { Session } from "@/session/session"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiError } from "effect/unstable/httpapi"
|
||||
import * as ApiError from "../errors"
|
||||
|
||||
export function mapStorageNotFound<A, R>(self: Effect.Effect<A, StorageNotFoundError, R>) {
|
||||
|
|
@ -9,5 +8,14 @@ export function mapStorageNotFound<A, R>(self: Effect.Effect<A, StorageNotFoundE
|
|||
}
|
||||
|
||||
export function mapBusy<A, R>(self: Effect.Effect<A, Session.BusyError, R>) {
|
||||
return self.pipe(Effect.catchTag("SessionBusyError", () => Effect.fail(new HttpApiError.BadRequest({}))))
|
||||
return self.pipe(
|
||||
Effect.catchTag("SessionBusyError", (error) =>
|
||||
Effect.fail(
|
||||
new ApiError.SessionBusyError({
|
||||
sessionID: error.sessionID,
|
||||
message: `Session is busy: ${error.sessionID}`,
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -142,4 +142,19 @@ describe("PublicApi OpenAPI v2 errors", () => {
|
|||
)
|
||||
}
|
||||
})
|
||||
|
||||
test("documents session busy errors", () => {
|
||||
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
|
||||
|
||||
for (const route of [
|
||||
["post", "/session/{sessionID}/shell"],
|
||||
["post", "/session/{sessionID}/revert"],
|
||||
["post", "/session/{sessionID}/unrevert"],
|
||||
["delete", "/session/{sessionID}/message/{messageID}"],
|
||||
] as const) {
|
||||
expect(componentName(responseRef(spec.paths[route[1]]?.[route[0]]?.responses?.["409"]) ?? "")).toBe(
|
||||
"SessionBusyError",
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { afterEach, describe, expect } from "bun:test"
|
||||
import { mkdir } from "node:fs/promises"
|
||||
import path from "node:path"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { Cause, Effect, Exit, Layer } from "effect"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { registerAdapter } from "../../src/control-plane/adapters"
|
||||
import type { WorkspaceAdapter } from "../../src/control-plane/types"
|
||||
|
|
@ -13,6 +13,7 @@ import { InstanceBootstrap as InstanceBootstrapService } from "../../src/project
|
|||
import { InstanceStore } from "../../src/project/instance-store"
|
||||
import { Project } from "../../src/project/project"
|
||||
import { Server } from "../../src/server/server"
|
||||
import * as HttpSessionError from "../../src/server/routes/instance/httpapi/handlers/session-errors"
|
||||
import { SessionPaths } from "../../src/server/routes/instance/httpapi/groups/session"
|
||||
import { Session } from "@/session/session"
|
||||
import { MessageID, PartID, SessionID, type SessionID as SessionIDType } from "../../src/session/schema"
|
||||
|
|
@ -215,6 +216,22 @@ afterEach(async () => {
|
|||
})
|
||||
|
||||
describe("session HttpApi", () => {
|
||||
it.effect("maps busy sessions to public session busy errors", () =>
|
||||
Effect.gen(function* () {
|
||||
const sessionID = SessionID.descending()
|
||||
const exit = yield* HttpSessionError.mapBusy(Effect.fail(new Session.BusyError({ sessionID }))).pipe(Effect.exit)
|
||||
|
||||
expect(Exit.isFailure(exit)).toBe(true)
|
||||
if (Exit.isFailure(exit)) {
|
||||
expect(Cause.squash(exit.cause)).toMatchObject({
|
||||
_tag: "SessionBusyError",
|
||||
sessionID,
|
||||
message: `Session is busy: ${sessionID}`,
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
it.instance(
|
||||
"returns declared not found errors for read routes",
|
||||
() =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue