mirror of
https://gitee.com/zhangsken/opencode.git
synced 2026-10-16 10:30:01 +08:00
fix(protocol): return missing integration error
This commit is contained in:
@@ -109,8 +109,8 @@ Review endpoints in document order. For each endpoint, select one disposition an
|
||||
|
||||
| Done | Method | Path | Operation ID | Decision | Notes |
|
||||
|---|---|---|---|---|---|
|
||||
| [ ] 025 | `GET` | `/api/integration` | `integration.list` | | |
|
||||
| [ ] 026 | `GET` | `/api/integration/{integrationID}` | `integration.get` | | |
|
||||
| [x] 025 | `GET` | `/api/integration` | `integration.list` | Keep | Full integration inventory is consumed by authentication and integration-selection clients. |
|
||||
| [x] 026 | `GET` | `/api/integration/{integrationID}` | `integration.get` | Change | Missing integration now returns typed `404` instead of optional data. |
|
||||
| [ ] 027 | `POST` | `/api/experimental/integration/wellknown` | `experimental.integration.wellknown.add` | | |
|
||||
| [ ] 028 | `POST` | `/api/integration/{integrationID}/connect/key` | `integration.connect.key` | | |
|
||||
| [ ] 029 | `POST` | `/api/integration/{integrationID}/connect/oauth` | `integration.oauth.connect` | | |
|
||||
|
||||
@@ -1465,10 +1465,7 @@ export type IntegrationGetInput = {
|
||||
readonly integrationID: Integration.ID
|
||||
readonly location?: { readonly directory?: string | undefined } | undefined
|
||||
}
|
||||
export type IntegrationGetOutput = {
|
||||
readonly location: Location.PublicRef
|
||||
readonly data: Integration.Info | undefined
|
||||
}
|
||||
export type IntegrationGetOutput = { readonly location: Location.PublicRef; readonly data: Integration.Info }
|
||||
export type IntegrationGetOperation<E = never> = (input: IntegrationGetInput) => Effect.Effect<IntegrationGetOutput, E>
|
||||
|
||||
export type IntegrationWellknownAddInput = {
|
||||
|
||||
@@ -1100,7 +1100,7 @@ export function make(options: ClientOptions) {
|
||||
path: `/api/integration/${encodeURIComponent(input.integrationID)}`,
|
||||
query: { location: input["location"] },
|
||||
successStatus: 200,
|
||||
declaredStatuses: [400, 401],
|
||||
declaredStatuses: [400, 401, 404],
|
||||
empty: false,
|
||||
},
|
||||
requestOptions,
|
||||
|
||||
@@ -2502,6 +2502,14 @@ export type ProviderNotFoundError = {
|
||||
export const isProviderNotFoundError = (value: unknown): value is ProviderNotFoundError =>
|
||||
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "ProviderNotFoundError"
|
||||
|
||||
export type IntegrationNotFoundError = {
|
||||
readonly _tag: "IntegrationNotFoundError"
|
||||
readonly integrationID: string
|
||||
readonly message: string
|
||||
}
|
||||
export const isIntegrationNotFoundError = (value: unknown): value is IntegrationNotFoundError =>
|
||||
typeof value === "object" && value !== null && "_tag" in value && value["_tag"] === "IntegrationNotFoundError"
|
||||
|
||||
export type McpServerNotFoundError = {
|
||||
readonly _tag: "McpServerNotFoundError"
|
||||
readonly server: string
|
||||
@@ -4509,7 +4517,7 @@ export type IntegrationGetInput = {
|
||||
readonly location?: { readonly location?: { readonly directory?: string | undefined } | undefined }["location"]
|
||||
}
|
||||
|
||||
export type IntegrationGetOutput = { location: LocationPublicRef; data: IntegrationInfo | null }
|
||||
export type IntegrationGetOutput = { location: LocationPublicRef; data: IntegrationInfo }
|
||||
|
||||
export type IntegrationWellknownAddInput = {
|
||||
readonly location?: { readonly location?: { readonly directory?: string | undefined } | undefined }["location"]
|
||||
|
||||
@@ -268,7 +268,11 @@ export const make = Effect.fn("PluginHost.make")(function* (
|
||||
},
|
||||
integration: {
|
||||
list: () => response(integration.list()),
|
||||
get: (input) => response(integration.get(Integration.ID.make(input.integrationID))),
|
||||
get: Effect.fn(function* (input) {
|
||||
const item = yield* integration.get(Integration.ID.make(input.integrationID))
|
||||
if (!item) return yield* Effect.fail(new Error(`Integration not found: ${input.integrationID}`))
|
||||
return yield* response(Effect.succeed(item))
|
||||
}),
|
||||
connect: {
|
||||
key: (input) =>
|
||||
integration.connection.key({
|
||||
|
||||
@@ -5164,14 +5164,7 @@
|
||||
"$ref": "#/components/schemas/Location.PublicRef"
|
||||
},
|
||||
"data": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Integration.Info"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
"$ref": "#/components/schemas/Integration.Info"
|
||||
}
|
||||
},
|
||||
"required": ["location", "data"],
|
||||
@@ -5199,6 +5192,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "IntegrationNotFoundError",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/IntegrationNotFoundErrorEncoded"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Retrieve one integration and its authentication methods.",
|
||||
@@ -15118,6 +15121,23 @@
|
||||
"required": ["id", "type", "label"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"IntegrationNotFoundErrorEncoded": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_tag": {
|
||||
"type": "string",
|
||||
"enum": ["IntegrationNotFoundError"]
|
||||
},
|
||||
"integrationID": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["_tag", "integrationID", "message"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"InvalidCursorErrorEncoded": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -82,6 +82,15 @@ export class ProviderNotFoundError extends Schema.TaggedError<ProviderNotFoundEr
|
||||
{ httpApiStatus: 404 },
|
||||
) {}
|
||||
|
||||
export class IntegrationNotFoundError extends Schema.TaggedError<IntegrationNotFoundError>()(
|
||||
"IntegrationNotFoundError",
|
||||
{
|
||||
integrationID: Schema.String,
|
||||
message: Schema.String,
|
||||
},
|
||||
{ httpApiStatus: 404 },
|
||||
) {}
|
||||
|
||||
export class ProjectNotFoundError extends Schema.TaggedError<ProjectNotFoundError>()(
|
||||
"ProjectNotFoundError",
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Location } from "@opencode/schema/location"
|
||||
import { Form } from "@opencode/schema/form"
|
||||
import { Schema } from "effect"
|
||||
import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi"
|
||||
import { InvalidRequestError } from "../errors.js"
|
||||
import { IntegrationNotFoundError, InvalidRequestError } from "../errors.js"
|
||||
import { LocationQuery, locationQueryOpenApi } from "./location.js"
|
||||
|
||||
export const IntegrationGroup = HttpApiGroup.make("server.integration")
|
||||
@@ -25,7 +25,8 @@ export const IntegrationGroup = HttpApiGroup.make("server.integration")
|
||||
HttpApiEndpoint.get("integration.get", "/api/integration/:integrationID", {
|
||||
params: { integrationID: Integration.ID },
|
||||
query: LocationQuery,
|
||||
success: Location.response(Schema.UndefinedOr(Integration.Info)),
|
||||
success: Location.response(Integration.Info),
|
||||
error: IntegrationNotFoundError,
|
||||
})
|
||||
.annotateMerge(locationQueryOpenApi)
|
||||
.annotateMerge(
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Integration } from "@opencode/core/integration"
|
||||
import { Effect } from "effect"
|
||||
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi"
|
||||
import { Api } from "../api"
|
||||
import { InvalidRequestError } from "@opencode/protocol/errors"
|
||||
import { IntegrationNotFoundError, InvalidRequestError } from "@opencode/protocol/errors"
|
||||
import { response } from "../location"
|
||||
import { WellKnown } from "@opencode/core/wellknown"
|
||||
|
||||
@@ -32,7 +32,13 @@ export const IntegrationHandler = HttpApiBuilder.group(Api, "server.integration"
|
||||
"integration.get",
|
||||
Effect.fn(function* (ctx) {
|
||||
const service = yield* Integration.Service
|
||||
return yield* response(service.get(ctx.params.integrationID))
|
||||
const integration = yield* service.get(ctx.params.integrationID)
|
||||
if (!integration)
|
||||
return yield* new IntegrationNotFoundError({
|
||||
integrationID: ctx.params.integrationID,
|
||||
message: `Integration not found: ${ctx.params.integrationID}`,
|
||||
})
|
||||
return yield* response(Effect.succeed(integration))
|
||||
}),
|
||||
)
|
||||
.handle(
|
||||
|
||||
@@ -5164,14 +5164,7 @@
|
||||
"$ref": "#/components/schemas/Location.PublicRef"
|
||||
},
|
||||
"data": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Integration.Info"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
"$ref": "#/components/schemas/Integration.Info"
|
||||
}
|
||||
},
|
||||
"required": ["location", "data"],
|
||||
@@ -5199,6 +5192,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "IntegrationNotFoundError",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/IntegrationNotFoundErrorEncoded"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Retrieve one integration and its authentication methods.",
|
||||
@@ -15118,6 +15121,23 @@
|
||||
"required": ["id", "type", "label"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"IntegrationNotFoundErrorEncoded": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_tag": {
|
||||
"type": "string",
|
||||
"enum": ["IntegrationNotFoundError"]
|
||||
},
|
||||
"integrationID": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["_tag", "integrationID", "message"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"InvalidCursorErrorEncoded": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -5164,14 +5164,7 @@
|
||||
"$ref": "#/components/schemas/Location.PublicRef"
|
||||
},
|
||||
"data": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/Integration.Info"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
"$ref": "#/components/schemas/Integration.Info"
|
||||
}
|
||||
},
|
||||
"required": ["location", "data"],
|
||||
@@ -5199,6 +5192,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "IntegrationNotFoundError",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/IntegrationNotFoundErrorEncoded"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Retrieve one integration and its authentication methods.",
|
||||
@@ -15118,6 +15121,23 @@
|
||||
"required": ["id", "type", "label"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"IntegrationNotFoundErrorEncoded": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"_tag": {
|
||||
"type": "string",
|
||||
"enum": ["IntegrationNotFoundError"]
|
||||
},
|
||||
"integrationID": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": ["_tag", "integrationID", "message"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"InvalidCursorErrorEncoded": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Reference in New Issue
Block a user