fix(sdk): preserve SSE event payload types
This commit is contained in:
parent
7a17925495
commit
8a8b572e9f
4 changed files with 43 additions and 22 deletions
|
|
@ -34,6 +34,8 @@ type OpenApiSchema = {
|
||||||
additionalProperties?: OpenApiSchema | boolean
|
additionalProperties?: OpenApiSchema | boolean
|
||||||
allOf?: OpenApiSchema[]
|
allOf?: OpenApiSchema[]
|
||||||
anyOf?: OpenApiSchema[]
|
anyOf?: OpenApiSchema[]
|
||||||
|
contentMediaType?: string
|
||||||
|
contentSchema?: OpenApiSchema
|
||||||
description?: string
|
description?: string
|
||||||
enum?: Array<string | boolean>
|
enum?: Array<string | boolean>
|
||||||
items?: OpenApiSchema
|
items?: OpenApiSchema
|
||||||
|
|
@ -88,6 +90,7 @@ function matchLegacyOpenApi(input: Record<string, unknown>) {
|
||||||
// payload and inside an annotated union arm. Resolve these by inlining the
|
// payload and inside an annotated union arm. Resolve these by inlining the
|
||||||
// actual schema from any parent union that references them.
|
// actual schema from any parent union that references them.
|
||||||
fixSelfReferencingComponents(spec)
|
fixSelfReferencingComponents(spec)
|
||||||
|
collapseJsonSseComponents(spec)
|
||||||
|
|
||||||
// Effect's Schema.optional emits `anyOf: [T, {type:"null"}]` in OpenAPI,
|
// Effect's Schema.optional emits `anyOf: [T, {type:"null"}]` in OpenAPI,
|
||||||
// but the legacy SDK expected plain `T` for optional fields. Strip null
|
// but the legacy SDK expected plain `T` for optional fields. Strip null
|
||||||
|
|
@ -229,6 +232,26 @@ function collapseDuplicateComponents(spec: OpenApiSpec) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collapseJsonSseComponents(spec: OpenApiSpec) {
|
||||||
|
const schemas = spec.components?.schemas
|
||||||
|
if (!schemas) return
|
||||||
|
for (const [name, schema] of Object.entries(schemas)) {
|
||||||
|
const ref = schema.contentMediaType === "application/json" ? schema.contentSchema?.$ref : undefined
|
||||||
|
const target = ref?.replace("#/components/schemas/", "")
|
||||||
|
if (schema.type !== "string" || !target || !schemas[target]) continue
|
||||||
|
// Effect represents JSON SSE payloads as encoded string schemas, but the
|
||||||
|
// generated SDK's SSE client already decodes them. Keep the payload union
|
||||||
|
// under its stable, unsuffixed component name instead of exposing `string`.
|
||||||
|
const canonical = name.replace(/\d+$/, "")
|
||||||
|
if (target.replace(/\d+$/, "") !== canonical) continue
|
||||||
|
schemas[canonical] = schemas[target]
|
||||||
|
rewriteRefs(spec, name, canonical)
|
||||||
|
rewriteRefs(spec, target, canonical)
|
||||||
|
if (name !== canonical) delete schemas[name]
|
||||||
|
if (target !== canonical) delete schemas[target]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeComponentNames(spec: OpenApiSpec) {
|
function normalizeComponentNames(spec: OpenApiSpec) {
|
||||||
const schemas = spec.components?.schemas
|
const schemas = spec.components?.schemas
|
||||||
if (!schemas) return
|
if (!schemas) return
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,22 @@ describe("PublicApi OpenAPI v2 errors", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("exposes decoded SSE payload schemas", () => {
|
||||||
|
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
|
||||||
|
|
||||||
|
expect(spec.paths["/api/event"]?.get?.responses?.["200"]?.content?.["text/event-stream"]?.schema?.$ref).toBe(
|
||||||
|
"#/components/schemas/V2Event",
|
||||||
|
)
|
||||||
|
expect(spec.components.schemas.V2Event?.anyOf?.length).toBeGreaterThan(0)
|
||||||
|
expect(spec.components.schemas.V2Event1).toBeUndefined()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
spec.paths["/api/session/{sessionID}/event"]?.get?.responses?.["200"]?.content?.["text/event-stream"]?.schema
|
||||||
|
?.properties?.data?.$ref,
|
||||||
|
).toBe("#/components/schemas/SessionDurableEvent")
|
||||||
|
expect(spec.components.schemas.SessionDurableEvent1).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
test("preserves /api auth responses", () => {
|
test("preserves /api auth responses", () => {
|
||||||
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
|
const spec = OpenApi.fromApi(PublicApi) as OpenApiSpec
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2766,8 +2766,6 @@ export type SessionHistory = {
|
||||||
hasMore: boolean
|
hasMore: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SessionDurableEvent1 = string
|
|
||||||
|
|
||||||
export type SessionMessagesResponse = {
|
export type SessionMessagesResponse = {
|
||||||
data: Array<SessionMessage>
|
data: Array<SessionMessage>
|
||||||
cursor: {
|
cursor: {
|
||||||
|
|
@ -2847,7 +2845,7 @@ export type QuestionRejected2 = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type V2Event1 =
|
export type V2Event =
|
||||||
| ModelsDevRefreshed
|
| ModelsDevRefreshed
|
||||||
| IntegrationUpdated
|
| IntegrationUpdated
|
||||||
| IntegrationConnectionUpdated
|
| IntegrationConnectionUpdated
|
||||||
|
|
@ -2937,8 +2935,6 @@ export type V2Event1 =
|
||||||
| ServerConnected
|
| ServerConnected
|
||||||
| GlobalDisposed
|
| GlobalDisposed
|
||||||
|
|
||||||
export type V2Event = string
|
|
||||||
|
|
||||||
export type ForbiddenError = {
|
export type ForbiddenError = {
|
||||||
_tag: "ForbiddenError"
|
_tag: "ForbiddenError"
|
||||||
message: string
|
message: string
|
||||||
|
|
@ -11897,7 +11893,7 @@ export type V2SessionEventsResponses = {
|
||||||
200: {
|
200: {
|
||||||
id: string
|
id: string
|
||||||
event: string
|
event: string
|
||||||
data: SessionDurableEvent1
|
data: SessionDurableEvent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11260,7 +11260,7 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"$ref": "#/components/schemas/SessionDurableEvent1"
|
"$ref": "#/components/schemas/SessionDurableEvent"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["id", "event", "data"],
|
"required": ["id", "event", "data"],
|
||||||
|
|
@ -23732,13 +23732,6 @@
|
||||||
"required": ["data", "hasMore"],
|
"required": ["data", "hasMore"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"SessionDurableEvent1": {
|
|
||||||
"type": "string",
|
|
||||||
"contentSchema": {
|
|
||||||
"$ref": "#/components/schemas/SessionDurableEvent"
|
|
||||||
},
|
|
||||||
"contentMediaType": "application/json"
|
|
||||||
},
|
|
||||||
"SessionMessagesResponse": {
|
"SessionMessagesResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -23975,7 +23968,7 @@
|
||||||
"required": ["id", "type", "data"],
|
"required": ["id", "type", "data"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
"V2Event1": {
|
"V2Event": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{
|
{
|
||||||
"$ref": "#/components/schemas/Models-devRefreshed"
|
"$ref": "#/components/schemas/Models-devRefreshed"
|
||||||
|
|
@ -24243,13 +24236,6 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"V2Event": {
|
|
||||||
"type": "string",
|
|
||||||
"contentSchema": {
|
|
||||||
"$ref": "#/components/schemas/V2Event1"
|
|
||||||
},
|
|
||||||
"contentMediaType": "application/json"
|
|
||||||
},
|
|
||||||
"ForbiddenError": {
|
"ForbiddenError": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue