Skip to content

Commit 270a14f

Browse files
authored
Merge pull request #2823 from reduxjs/draft/inspiring-currying
2 parents 17d1789 + a2fec02 commit 270a14f

File tree

2 files changed

+106
-23
lines changed

2 files changed

+106
-23
lines changed

packages/toolkit/src/query/fetchBaseQuery.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export interface FetchArgs extends CustomRequestInit {
2626
body?: any
2727
responseHandler?: ResponseHandler
2828
validateStatus?: (response: Response, body: any) => boolean
29+
/**
30+
* A number in milliseconds that represents that maximum time a request can take before timing out.
31+
*/
2932
timeout?: number
3033
}
3134

@@ -129,11 +132,8 @@ export type FetchBaseQueryArgs = {
129132
* Defaults to `application/json`;
130133
*/
131134
jsonContentType?: string
132-
/**
133-
* A number in milliseconds that represents that maximum time a request can take before timing out.
134-
*/
135-
timeout?: number
136-
} & RequestInit
135+
} & RequestInit &
136+
Pick<FetchArgs, 'responseHandler' | 'validateStatus' | 'timeout'>
137137

138138
export type FetchBaseQueryMeta = { request: Request; response?: Response }
139139

@@ -189,6 +189,7 @@ export function fetchBaseQuery({
189189
isJsonContentType = defaultIsJsonContentType,
190190
jsonContentType = 'application/json',
191191
timeout: defaultTimeout,
192+
validateStatus: globalValidateStatus,
192193
...baseFetchOptions
193194
}: FetchBaseQueryArgs = {}): BaseQueryFn<
194195
string | FetchArgs,
@@ -212,7 +213,7 @@ export function fetchBaseQuery({
212213
body = undefined,
213214
params = undefined,
214215
responseHandler = 'json' as const,
215-
validateStatus = defaultValidateStatus,
216+
validateStatus = globalValidateStatus ?? defaultValidateStatus,
216217
timeout = defaultTimeout,
217218
...rest
218219
} = typeof arg == 'string' ? { url: arg } : arg

packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx

Lines changed: 99 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,89 @@ describe('fetchBaseQuery', () => {
868868
expect(request.headers['delete']).toBe(defaultHeaders['delete'])
869869
expect(request.headers['delete2']).toBe(defaultHeaders['delete2'])
870870
})
871+
872+
describe('Accepts global arguments', () => {
873+
test('Global responseHandler', async () => {
874+
server.use(
875+
rest.get('https://example.com/success', (_, res, ctx) =>
876+
res.once(ctx.text(`this is not json!`))
877+
)
878+
)
879+
880+
const globalizedBaseQuery = fetchBaseQuery({
881+
baseUrl,
882+
fetchFn: fetchFn as any,
883+
responseHandler: 'text',
884+
})
885+
886+
const req = globalizedBaseQuery(
887+
{ url: '/success', responseHandler: 'text' },
888+
commonBaseQueryApi,
889+
{}
890+
)
891+
expect(req).toBeInstanceOf(Promise)
892+
const res = await req
893+
expect(res).toBeInstanceOf(Object)
894+
expect(res.meta?.request).toBeInstanceOf(Request)
895+
expect(res.meta?.response).toBeInstanceOf(Object)
896+
expect(res.data).toEqual(`this is not json!`)
897+
})
898+
899+
test('Global validateStatus', async () => {
900+
const globalizedBaseQuery = fetchBaseQuery({
901+
baseUrl,
902+
fetchFn: fetchFn as any,
903+
validateStatus: (response, body) =>
904+
response.status === 200 && body.success === false ? false : true,
905+
})
906+
907+
// This is a scenario where an API may always return a 200, but indicates there is an error when success = false
908+
const res = await globalizedBaseQuery(
909+
{
910+
url: '/nonstandard-error',
911+
},
912+
commonBaseQueryApi,
913+
{}
914+
)
915+
916+
expect(res.error).toEqual({
917+
status: 200,
918+
data: {
919+
success: false,
920+
message: 'This returns a 200 but is really an error',
921+
},
922+
})
923+
})
924+
925+
test('Global timeout', async () => {
926+
let reject: () => void
927+
const donePromise = new Promise((resolve, _reject) => {
928+
reject = _reject
929+
})
930+
server.use(
931+
rest.get('https://example.com/empty1', async (req, res, ctx) => {
932+
await Promise.race([waitMs(3000), donePromise])
933+
return res.once(ctx.json({ ...req, headers: req.headers.all() }))
934+
})
935+
)
936+
const globalizedBaseQuery = fetchBaseQuery({
937+
baseUrl,
938+
fetchFn: fetchFn as any,
939+
timeout: 200,
940+
})
941+
942+
const result = await globalizedBaseQuery(
943+
{ url: '/empty1' },
944+
commonBaseQueryApi,
945+
{}
946+
)
947+
expect(result?.error).toEqual({
948+
status: 'TIMEOUT_ERROR',
949+
error: 'AbortError: The user aborted a request.',
950+
})
951+
reject!()
952+
})
953+
})
871954
})
872955

873956
describe('fetchFn', () => {
@@ -950,28 +1033,27 @@ describe('still throws on completely unexpected errors', () => {
9501033
})
9511034

9521035
describe('timeout', () => {
953-
it('throws a timeout error when a request takes longer than specified timeout duration', async () => {
954-
jest.useFakeTimers('legacy')
955-
let result: any
1036+
test('throws a timeout error when a request takes longer than specified timeout duration', async () => {
1037+
let reject: () => void
1038+
const donePromise = new Promise((resolve, _reject) => {
1039+
reject = _reject
1040+
})
1041+
9561042
server.use(
957-
rest.get('https://example.com/empty', (req, res, ctx) =>
958-
res.once(
959-
ctx.delay(3000),
960-
ctx.json({ ...req, headers: req.headers.all() })
961-
)
962-
)
1043+
rest.get('https://example.com/empty2', async (req, res, ctx) => {
1044+
await Promise.race([waitMs(3000), donePromise])
1045+
return res.once(ctx.json({ ...req, headers: req.headers.all() }))
1046+
})
1047+
)
1048+
const result = await baseQuery(
1049+
{ url: '/empty2', timeout: 200 },
1050+
commonBaseQueryApi,
1051+
{}
9631052
)
964-
Promise.resolve(
965-
baseQuery({ url: '/empty', timeout: 2000 }, commonBaseQueryApi, {})
966-
).then((r) => {
967-
result = r
968-
})
969-
await waitMs()
970-
jest.runAllTimers()
971-
await waitMs()
9721053
expect(result?.error).toEqual({
9731054
status: 'TIMEOUT_ERROR',
9741055
error: 'AbortError: The user aborted a request.',
9751056
})
1057+
reject!()
9761058
})
9771059
})

0 commit comments

Comments
 (0)