Skip to content

Commit 1e9f8f0

Browse files
rg1shockey
authored andcommitted
Support asynchronous responseInterceptor (#1261)
* Let responseInterceptor properly return a Promise and use the new result to determine error. * Rewrite http function to use async/await as requested. It does make the responseInterceptor code much easier to read ;) * Support asynchronous requestInterceptor aswell, including tests. Fixes #1249.
1 parent 1796331 commit 1e9f8f0

File tree

3 files changed

+84
-29
lines changed

3 files changed

+84
-29
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ Swagger.http(request)
9595

9696
// Interceptors
9797
Swagger.http({
98-
requestInterceptor: (req: Request) => Request
99-
responseInterceptor: (res: Response) => Response
98+
requestInterceptor: (req: Request) => Request | Promise<Request>
99+
responseInterceptor: (res: Response) => Response | Promise<Response>
100100
})
101101

102102
// Custom Fetch

src/http.js

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import 'cross-fetch/polyfill'
1+
import 'cross-fetch/polyfill' /* global fetch */
22
import qs from 'qs'
33
import jsYaml from 'js-yaml'
44
import isString from 'lodash/isString'
@@ -11,7 +11,7 @@ export const self = {
1111

1212
// Handles fetch-like syntax and the case where there is only one object passed-in
1313
// (which will have the URL as a property). Also serilizes the response.
14-
export default function http(url, request = {}) {
14+
export default async function http(url, request = {}) {
1515
if (typeof url === 'object') {
1616
request = url
1717
url = request.url
@@ -25,7 +25,7 @@ export default function http(url, request = {}) {
2525
self.mergeInQueryOrForm(request)
2626

2727
if (request.requestInterceptor) {
28-
request = request.requestInterceptor(request) || request
28+
request = await request.requestInterceptor(request) || request
2929
}
3030

3131
// for content-type=multipart\/form-data remove content-type from request before fetch
@@ -37,31 +37,27 @@ export default function http(url, request = {}) {
3737
}
3838

3939
// eslint-disable-next-line no-undef
40-
return (request.userFetch || fetch)(request.url, request).then((res) => {
41-
const serialized = self.serializeRes(res, url, request).then((_res) => {
42-
if (request.responseInterceptor) {
43-
_res = request.responseInterceptor(_res) || _res
44-
}
45-
return _res
46-
})
47-
48-
if (!res.ok) {
49-
const error = new Error(res.statusText)
50-
error.statusCode = error.status = res.status
51-
return serialized.then(
52-
(_res) => {
53-
error.response = _res
54-
throw error
55-
},
56-
(resError) => {
57-
error.responseError = resError
58-
throw error
59-
}
60-
)
40+
let res
41+
try {
42+
res = await (request.userFetch || fetch)(request.url, request)
43+
res = await self.serializeRes(res, url, request)
44+
if (request.responseInterceptor) {
45+
res = await request.responseInterceptor(res) || res
6146
}
62-
63-
return serialized
64-
})
47+
}
48+
catch (resError) {
49+
const error = new Error(res.statusText)
50+
error.statusCode = error.status = res.status
51+
error.responseError = resError
52+
throw error
53+
}
54+
if (!res.ok) {
55+
const error = new Error(res.statusText)
56+
error.statusCode = error.status = res.status
57+
error.response = res
58+
throw error
59+
}
60+
return res
6561
}
6662

6763
// exported for testing

test/http.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,46 @@ describe('http', () => {
6060
)
6161
})
6262

63+
it('should call request interceptor', () => {
64+
xapp = xmock()
65+
xapp.get('http://swagger.io', (req, res) => res.status(req.requestHeaders.mystatus).send('hi'))
66+
67+
return http({
68+
url: 'http://swagger.io',
69+
requestInterceptor: (req) => {
70+
req.headers.mystatus = 200
71+
return req
72+
}
73+
})
74+
.then(
75+
(res) => {
76+
expect(res.status).toEqual(200)
77+
}
78+
)
79+
})
80+
81+
it('should allow the requestInterceptor to return a promise', () => {
82+
xapp = xmock()
83+
xapp.get('http://swagger.io', (req, res) => res.status(req.requestHeaders.mystatus).send('hi'))
84+
85+
return http({
86+
url: 'http://swagger.io',
87+
requestInterceptor: (req) => {
88+
return new Promise((resolve, reject) => {
89+
setTimeout(() => {
90+
req.headers.mystatus = 200
91+
resolve(req)
92+
}, 20)
93+
})
94+
}
95+
})
96+
.then(
97+
(res) => {
98+
expect(res.status).toEqual(200)
99+
}
100+
)
101+
})
102+
63103
it('should apply responseInterceptor to error responses', () => {
64104
xapp = xmock()
65105
xapp.get('http://swagger.io', (req, res) => res.status(400).send('hi'))
@@ -80,6 +120,25 @@ describe('http', () => {
80120
)
81121
})
82122

123+
it('should allow the responseInterceptor to return a promise for a final response', () => {
124+
xapp = xmock()
125+
xapp.get('http://swagger.io', (req, res) => res.status(400).send('doit'))
126+
xapp.get('http://example.com', (req, res) => res.send('hi'))
127+
128+
return http({
129+
url: 'http://swagger.io',
130+
responseInterceptor: (res) => {
131+
return http({
132+
url: 'http://example.com'
133+
})
134+
}
135+
})
136+
.then((res) => {
137+
expect(res.status).toEqual(200)
138+
expect(res.text).toEqual('hi')
139+
})
140+
})
141+
83142
it('should set responseError on responseInterceptor Error', () => {
84143
xapp = xmock()
85144
xapp.get('http://swagger.io', (req, res) => res.status(400).send('hi'))

0 commit comments

Comments
 (0)