Skip to content

Commit

Permalink
Add 'content-type' ResponseHandler (#2363)
Browse files Browse the repository at this point in the history
Co-authored-by: Lenz Weber <mail@lenzw.de>
  • Loading branch information
taylorkline and phryneas committed Jul 8, 2022
1 parent 59dfcd2 commit a60e79d
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 18 deletions.
39 changes: 21 additions & 18 deletions packages/toolkit/src/query/fetchBaseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { BaseQueryApi, BaseQueryFn } from './baseQueryTypes'
import type { MaybePromise, Override } from './tsHelpers'

export type ResponseHandler =
| 'content-type'
| 'json'
| 'text'
| ((response: Response) => Promise<any>)
Expand Down Expand Up @@ -41,24 +42,6 @@ const defaultValidateStatus = (response: Response) =>
const defaultIsJsonContentType = (headers: Headers) =>
/*applicat*/ /ion\/(vnd\.api\+)?json/.test(headers.get('content-type') || '')

const handleResponse = async (
response: Response,
responseHandler: ResponseHandler
) => {
if (typeof responseHandler === 'function') {
return responseHandler(response)
}

if (responseHandler === 'text') {
return response.text()
}

if (responseHandler === 'json') {
const text = await response.text()
return text.length ? JSON.parse(text) : null
}
}

export type FetchBaseQueryError =
| {
/**
Expand Down Expand Up @@ -342,4 +325,24 @@ export function fetchBaseQuery({
meta,
}
}

async function handleResponse(
response: Response,
responseHandler: ResponseHandler
) {
if (typeof responseHandler === 'function') {
return responseHandler(response)
}

if (responseHandler === 'content-type') {
responseHandler = isJsonContentType(response.headers) ? 'json' : 'text'
}

if (responseHandler === 'json') {
const text = await response.text()
return text.length ? JSON.parse(text) : null
}

return response.text()
}
}
114 changes: 114 additions & 0 deletions packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,64 @@ describe('fetchBaseQuery', () => {
})
})

it('success: parse text without error ("content-type" responseHandler)', async () => {
server.use(
rest.get('https://example.com/success', (_, res, ctx) =>
res.once(
ctx.text(`this is not json!`)
// NOTE: MSW sets content-type header as text automatically
)
)
)

const req = baseQuery(
{
url: '/success',
responseHandler: 'content-type',
},
commonBaseQueryApi,
{}
)
expect(req).toBeInstanceOf(Promise)
const res = await req
expect(res).toBeInstanceOf(Object)
expect(res.meta?.response?.headers.get('content-type')).toEqual(
'text/plain'
)
expect(res.meta?.request).toBeInstanceOf(Request)
expect(res.meta?.response).toBeInstanceOf(Object)
expect(res.data).toEqual(`this is not json!`)
})

it('success: parse json without error ("content-type" responseHandler)', async () => {
server.use(
rest.get('https://example.com/success', (_, res, ctx) =>
res.once(
ctx.json(`this will become json!`)
// NOTE: MSW sets content-type header as json automatically
)
)
)

const req = baseQuery(
{
url: '/success',
responseHandler: 'content-type',
},
commonBaseQueryApi,
{}
)
expect(req).toBeInstanceOf(Promise)
const res = await req
expect(res).toBeInstanceOf(Object)
expect(res.meta?.response?.headers.get('content-type')).toEqual(
'application/json'
)
expect(res.meta?.request).toBeInstanceOf(Request)
expect(res.meta?.response).toBeInstanceOf(Object)
expect(res.data).toEqual(`this will become json!`)
})

it('server error: should fail normally with a 500 status ("text" responseHandler)', async () => {
server.use(
rest.get('https://example.com/error', (_, res, ctx) =>
Expand All @@ -204,6 +262,62 @@ describe('fetchBaseQuery', () => {
})
})

it('server error: should fail normally with a 500 status as text ("content-type" responseHandler)', async () => {
const serverResponse = 'Internal Server Error'
server.use(
rest.get('https://example.com/error', (_, res, ctx) =>
res(ctx.status(500), ctx.text(serverResponse))
)
)

const req = baseQuery(
{ url: '/error', responseHandler: 'content-type' },
commonBaseQueryApi,
{}
)
expect(req).toBeInstanceOf(Promise)
const res = await req
expect(res).toBeInstanceOf(Object)
expect(res.meta?.request).toBeInstanceOf(Request)
expect(res.meta?.response).toBeInstanceOf(Object)
expect(res.meta?.response?.headers.get('content-type')).toEqual(
'text/plain'
)
expect(res.error).toEqual({
status: 500,
data: serverResponse,
})
})

it('server error: should fail normally with a 500 status as json ("content-type" responseHandler)', async () => {
const serverResponse = {
errors: { field1: "Password cannot be 'password'" },
}
server.use(
rest.get('https://example.com/error', (_, res, ctx) =>
res(ctx.status(500), ctx.json(serverResponse))
)
)

const req = baseQuery(
{ url: '/error', responseHandler: 'content-type' },
commonBaseQueryApi,
{}
)
expect(req).toBeInstanceOf(Promise)
const res = await req
expect(res).toBeInstanceOf(Object)
expect(res.meta?.request).toBeInstanceOf(Request)
expect(res.meta?.response).toBeInstanceOf(Object)
expect(res.meta?.response?.headers.get('content-type')).toEqual(
'application/json'
)
expect(res.error).toEqual({
status: 500,
data: serverResponse,
})
})

it('server error: should fail gracefully (default="json" responseHandler)', async () => {
server.use(
rest.get('https://example.com/error', (_, res, ctx) =>
Expand Down

0 comments on commit a60e79d

Please sign in to comment.