Skip to content

Commit 3f6c426

Browse files
committed
feat(openapi): accept empty body, improve decode error
1 parent 5204560 commit 3f6c426

2 files changed

Lines changed: 70 additions & 38 deletions

File tree

packages/openapi/src/adapters/fetch/openapi-payload-codec.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,23 @@ describe('openAPIPayloadCodec', () => {
107107

108108
const result = await codec.decode(response)
109109
expect(result).toBeUndefined()
110+
111+
const response2 = new Response('', {
112+
headers: { 'Content-Type': 'application/json' },
113+
})
114+
115+
const result2 = await codec.decode(response2)
116+
expect(result2).toBeUndefined()
117+
})
118+
119+
it('throws 400 BAD_REQUEST when the body is invalid', async () => {
120+
const response = new Response('invalid json', {
121+
headers: { 'Content-Type': 'application/json' },
122+
})
123+
124+
await expect(codec.decode(response))
125+
.rejects
126+
.toThrow('Cannot parse request/response. Please check the request/response body and Content-Type header.')
110127
})
111128
})
112129
})

packages/openapi/src/adapters/fetch/openapi-payload-codec.ts

Lines changed: 53 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -145,54 +145,69 @@ export class OpenAPIPayloadCodec {
145145
}
146146

147147
async decode(re: Request | Response | Headers | URLSearchParams | FormData): Promise<unknown> {
148-
if (
149-
re instanceof Headers
150-
|| re instanceof URLSearchParams
151-
|| re instanceof FormData
152-
) {
153-
return BracketNotation.deserialize([...re.entries()])
154-
}
148+
try {
149+
if (
150+
re instanceof Headers
151+
|| re instanceof URLSearchParams
152+
|| re instanceof FormData
153+
) {
154+
return BracketNotation.deserialize([...re.entries()])
155+
}
155156

156-
const contentType = re.headers.get('content-type')
157-
const contentDisposition = re.headers.get('content-disposition')
158-
const fileName = contentDisposition ? cd.parse(contentDisposition).parameters.filename : undefined
157+
const contentType = re.headers.get('content-type')
158+
const contentDisposition = re.headers.get('content-disposition')
159+
const fileName = contentDisposition ? cd.parse(contentDisposition).parameters.filename : undefined
159160

160-
if (fileName) {
161-
const blob = await re.blob()
162-
const file = new File([blob], fileName, {
163-
type: blob.type,
164-
})
161+
if (fileName) {
162+
const blob = await re.blob()
163+
const file = new File([blob], fileName, {
164+
type: blob.type,
165+
})
165166

166-
return file
167-
}
167+
return file
168+
}
169+
170+
if (!contentType || contentType.startsWith('application/json')) {
171+
if (!re.body) {
172+
return undefined
173+
}
174+
175+
const text = await re.text()
168176

169-
if (!contentType || contentType.startsWith('application/json')) {
170-
if (!re.body) {
171-
return undefined
177+
if (!text) {
178+
return undefined
179+
}
180+
181+
return JSON.parse(text)
172182
}
173183

174-
return await re.json()
175-
}
184+
if (contentType.startsWith('application/x-www-form-urlencoded')) {
185+
const params = new URLSearchParams(await re.text())
186+
return this.decode(params)
187+
}
176188

177-
if (contentType.startsWith('application/x-www-form-urlencoded')) {
178-
const params = new URLSearchParams(await re.text())
179-
return this.decode(params)
180-
}
189+
if (contentType.startsWith('text/')) {
190+
const text = await re.text()
191+
return text
192+
}
181193

182-
if (contentType.startsWith('text/')) {
183-
const text = await re.text()
184-
return text
185-
}
194+
if (contentType.startsWith('multipart/form-data')) {
195+
const form = await re.formData()
196+
return this.decode(form)
197+
}
186198

187-
if (contentType.startsWith('multipart/form-data')) {
188-
const form = await re.formData()
189-
return this.decode(form)
199+
const blob = await re.blob()
200+
return new File([blob], 'blob', {
201+
type: blob.type,
202+
})
203+
}
204+
catch (e) {
205+
throw new ORPCError({
206+
code: 'BAD_REQUEST',
207+
message: 'Cannot parse request/response. Please check the request/response body and Content-Type header.',
208+
cause: e,
209+
})
190210
}
191-
192-
const blob = await re.blob()
193-
return new File([blob], 'blob', {
194-
type: blob.type,
195-
})
196211
}
197212
}
198213

0 commit comments

Comments
 (0)