Skip to content

Commit 668827b

Browse files
authored
Merge pull request #3534 from gilbode/ft/request_interceptor
Add support for requestInterceptor / responseInterceptor.
2 parents c88c8c3 + 087ed20 commit 668827b

File tree

10 files changed

+189
-7
lines changed

10 files changed

+189
-7
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ displayRequestDuration | Controls the display of the request duration (in millis
160160
maxDisplayedTags | If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations.
161161
filter | If set, enables filtering. The top bar will show an edit box that you can use to filter the tagged operations that are shown. Can be true/false to enable or disable, or an explicit filter string in which case filtering will be enabled using that string as the filter expression. Filtering is case sensitive matching the filter expression anywhere inside the tag.
162162
deepLinking | If set to `true`, enables dynamic deep linking for tags and operations. [Docs](https://github.com/swagger-api/swagger-ui/blob/master/docs/deep-linking.md)
163+
requestInterceptor | MUST be a function. Function to intercept try-it-out requests. Accepts one argument requestInterceptor(request) and must return the potentially modified request.
164+
responseInterceptor | MUST be a function. Function to intercept try-it-out responses. Accepts one argument responseInterceptor(response) and must return the potentially modified response.
165+
showMutatedRequest | If set to `true` (the default), uses the mutated request returned from a rquestInterceptor to produce the curl command in the UI, otherwise the request before the requestInterceptor was applied is used.
163166

164167
### Plugins
165168

src/core/components/live-response.jsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,18 @@ Duration.propTypes = {
2929
export default class LiveResponse extends React.Component {
3030
static propTypes = {
3131
response: PropTypes.object.isRequired,
32+
specSelectors: PropTypes.object.isRequired,
33+
pathMethod: PropTypes.object.isRequired,
3234
getComponent: PropTypes.func.isRequired,
33-
displayRequestDuration: PropTypes.bool.isRequired
35+
displayRequestDuration: PropTypes.bool.isRequired,
36+
getConfigs: PropTypes.func.isRequired
3437
}
3538

3639
render() {
37-
const { request, response, getComponent, displayRequestDuration } = this.props
40+
const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, pathMethod } = this.props
41+
const { showMutatedRequest } = getConfigs()
3842

43+
const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(pathMethod[0], pathMethod[1]) : specSelectors.requestFor(pathMethod[0], pathMethod[1])
3944
const status = response.get("status")
4045
const url = response.get("url")
4146
const headers = response.get("headers").toJS()
@@ -55,7 +60,7 @@ export default class LiveResponse extends React.Component {
5560

5661
return (
5762
<div>
58-
{ request && <Curl request={ request }/> }
63+
{ curlRequest && <Curl request={ curlRequest }/> }
5964
<h4>Server response</h4>
6065
<table className="responses-table">
6166
<thead>

src/core/components/operation.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ export default class Operation extends PureComponent {
263263
request={ request }
264264
tryItOutResponse={ response }
265265
getComponent={ getComponent }
266+
getConfigs={ getConfigs }
266267
specSelectors={ specSelectors }
267268
specActions={ specActions }
268269
produces={ produces }

src/core/components/responses.jsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ export default class Responses extends React.Component {
1616
specActions: PropTypes.object.isRequired,
1717
pathMethod: PropTypes.array.isRequired,
1818
displayRequestDuration: PropTypes.bool.isRequired,
19-
fn: PropTypes.object.isRequired
19+
fn: PropTypes.object.isRequired,
20+
getConfigs: PropTypes.func.isRequired
2021
}
2122

2223
static defaultProps = {
@@ -29,7 +30,7 @@ export default class Responses extends React.Component {
2930
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
3031

3132
render() {
32-
let { responses, request, tryItOutResponse, getComponent, specSelectors, fn, producesValue, displayRequestDuration } = this.props
33+
let { responses, request, tryItOutResponse, getComponent, getConfigs, specSelectors, fn, producesValue, displayRequestDuration } = this.props
3334
let defaultCode = defaultStatusCode( responses )
3435

3536
const ContentType = getComponent( "contentType" )
@@ -57,6 +58,9 @@ export default class Responses extends React.Component {
5758
<LiveResponse request={ request }
5859
response={ tryItOutResponse }
5960
getComponent={ getComponent }
61+
getConfigs={ getConfigs }
62+
specSelectors={ specSelectors }
63+
pathMethod={ this.props.pathMethod }
6064
displayRequestDuration={ displayRequestDuration } />
6165
<h4>Responses</h4>
6266
</div>

src/core/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ module.exports = function SwaggerUI(opts) {
4242
displayOperationId: false,
4343
displayRequestDuration: false,
4444
deepLinking: false,
45+
requestInterceptor: (a => a),
46+
responseInterceptor: (a => a),
47+
showMutatedRequest: true,
4548

4649
// Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance.
4750
// Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest.

src/core/plugins/spec/actions.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export const UPDATE_PARAM = "spec_update_param"
1212
export const VALIDATE_PARAMS = "spec_validate_param"
1313
export const SET_RESPONSE = "spec_set_response"
1414
export const SET_REQUEST = "spec_set_request"
15+
export const SET_MUTATED_REQUEST = "spec_set_mutated_request"
1516
export const LOG_REQUEST = "spec_log_request"
1617
export const CLEAR_RESPONSE = "spec_clear_response"
1718
export const CLEAR_REQUEST = "spec_clear_request"
@@ -177,6 +178,13 @@ export const setRequest = ( path, method, req ) => {
177178
}
178179
}
179180

181+
export const setMutatedRequest = ( path, method, req ) => {
182+
return {
183+
payload: { path, method, req },
184+
type: SET_MUTATED_REQUEST
185+
}
186+
}
187+
180188
// This is for debugging, remove this comment if you depend on this action
181189
export const logRequest = (req) => {
182190
return {
@@ -187,8 +195,9 @@ export const logRequest = (req) => {
187195

188196
// Actually fire the request via fn.execute
189197
// (For debugging) and ease of testing
190-
export const executeRequest = (req) => ({fn, specActions, specSelectors}) => {
198+
export const executeRequest = (req) => ({fn, specActions, specSelectors, getConfigs}) => {
191199
let { pathName, method, operation } = req
200+
let { requestInterceptor, responseInterceptor } = getConfigs()
192201

193202
let op = operation.toJS()
194203

@@ -207,6 +216,16 @@ export const executeRequest = (req) => ({fn, specActions, specSelectors}) => {
207216

208217
specActions.setRequest(req.pathName, req.method, parsedRequest)
209218

219+
let requestInterceptorWrapper = function(r) {
220+
let mutatedRequest = requestInterceptor.apply(this, [r])
221+
let parsedMutatedRequest = Object.assign({}, mutatedRequest)
222+
specActions.setMutatedRequest(req.pathName, req.method, parsedMutatedRequest)
223+
return mutatedRequest
224+
}
225+
226+
req.requestInterceptor = requestInterceptorWrapper
227+
req.responseInterceptor = responseInterceptor
228+
210229
// track duration of request
211230
const startTime = Date.now()
212231

src/core/plugins/spec/reducers.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import { fromJSOrdered, validateParam } from "core/utils"
33
import win from "../../window"
44

55
import {
6-
UPDATE_SPEC,
6+
UPDATE_SPEC,
77
UPDATE_URL,
88
UPDATE_JSON,
99
UPDATE_PARAM,
1010
VALIDATE_PARAMS,
1111
SET_RESPONSE,
1212
SET_REQUEST,
13+
SET_MUTATED_REQUEST,
1314
UPDATE_RESOLVED,
1415
UPDATE_OPERATION_VALUE,
1516
CLEAR_RESPONSE,
@@ -101,6 +102,10 @@ export default {
101102
return state.setIn( [ "requests", path, method ], fromJSOrdered(req))
102103
},
103104

105+
[SET_MUTATED_REQUEST]: (state, { payload: { req, path, method } } ) =>{
106+
return state.setIn( [ "mutatedRequests", path, method ], fromJSOrdered(req))
107+
},
108+
104109
[UPDATE_OPERATION_VALUE]: (state, { payload: { path, value, key } }) => {
105110
let operationPath = ["resolved", "paths", ...path]
106111
if(!state.getIn(operationPath)) {

src/core/plugins/spec/selectors.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@ export const requests = createSelector(
237237
state => state.get( "requests", Map() )
238238
)
239239

240+
export const mutatedRequests = createSelector(
241+
state,
242+
state => state.get( "mutatedRequests", Map() )
243+
)
244+
240245
export const responseFor = (state, path, method) => {
241246
return responses(state).getIn([path, method], null)
242247
}
@@ -245,6 +250,10 @@ export const requestFor = (state, path, method) => {
245250
return requests(state).getIn([path, method], null)
246251
}
247252

253+
export const mutatedRequestFor = (state, path, method) => {
254+
return mutatedRequests(state).getIn([path, method], null)
255+
}
256+
248257
export const allowTryItOutFor = () => {
249258
// This is just a hook for now.
250259
return true

test/components/live-response.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* eslint-env mocha */
2+
import React from "react"
3+
import { fromJSOrdered } from "core/utils"
4+
import expect, { createSpy } from "expect"
5+
import { shallow } from "enzyme"
6+
import Curl from "components/curl"
7+
import LiveResponse from "components/live-response"
8+
import ResponseBody from "components/response-body"
9+
10+
describe("<LiveResponse/>", function(){
11+
let request = fromJSOrdered({
12+
credentials: "same-origin",
13+
headers: {
14+
accept: "application/xml"
15+
},
16+
url: "http://petstore.swagger.io/v2/pet/1"
17+
})
18+
19+
let mutatedRequest = fromJSOrdered({
20+
credentials: "same-origin",
21+
headers: {
22+
accept: "application/xml",
23+
mutated: "header"
24+
},
25+
url: "http://petstore.swagger.io/v2/pet/1"
26+
})
27+
28+
let requests = {
29+
request: request,
30+
mutatedRequest: mutatedRequest
31+
}
32+
33+
const tests = [
34+
{ showMutatedRequest: true, expected: { request: "mutatedRequest", requestForCalls: 0, mutatedRequestForCalls: 1 } },
35+
{ showMutatedRequest: false, expected: { request: "request", requestForCalls: 1, mutatedRequestForCalls: 0 } }
36+
]
37+
38+
tests.forEach(function(test) {
39+
it("passes " + test.expected.request + " to Curl when showMutatedRequest = " + test.showMutatedRequest, function() {
40+
41+
// Given
42+
43+
let response = fromJSOrdered({
44+
status: 200,
45+
url: "http://petstore.swagger.io/v2/pet/1",
46+
headers: {},
47+
text: "<response/>",
48+
})
49+
50+
let mutatedRequestForSpy = createSpy().andReturn(mutatedRequest)
51+
let requestForSpy = createSpy().andReturn(request)
52+
53+
let components = {
54+
curl: Curl,
55+
responseBody: ResponseBody
56+
}
57+
58+
let props = {
59+
response: response,
60+
specSelectors: {
61+
mutatedRequestFor: mutatedRequestForSpy,
62+
requestFor: requestForSpy,
63+
},
64+
pathMethod: [ "/one", "get" ],
65+
getComponent: (c) => {
66+
return components[c]
67+
},
68+
displayRequestDuration: true,
69+
getConfigs: () => ({ showMutatedRequest: test.showMutatedRequest })
70+
}
71+
72+
// When
73+
let wrapper = shallow(<LiveResponse {...props}/>)
74+
75+
// Then
76+
expect(mutatedRequestForSpy.calls.length).toEqual(test.expected.mutatedRequestForCalls)
77+
expect(requestForSpy.calls.length).toEqual(test.expected.requestForCalls)
78+
79+
const curl = wrapper.find(Curl)
80+
expect(curl.length).toEqual(1)
81+
expect(curl.props().request).toBe(requests[test.expected.request])
82+
83+
})
84+
})
85+
})

test/core/plugins/spec/actions.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,54 @@ describe("spec plugin - actions", function(){
9393
})
9494
})
9595

96+
it("should pass requestInterceptor/responseInterceptor to fn.execute", function(){
97+
// Given
98+
let configs = {
99+
requestInterceptor: createSpy(),
100+
responseInterceptor: createSpy()
101+
}
102+
const system = {
103+
fn: {
104+
buildRequest: createSpy(),
105+
execute: createSpy().andReturn(Promise.resolve())
106+
},
107+
specActions: {
108+
executeRequest: createSpy(),
109+
setMutatedRequest: createSpy(),
110+
setRequest: createSpy()
111+
},
112+
specSelectors: {
113+
spec: () => fromJS({}),
114+
parameterValues: () => fromJS({}),
115+
contentTypeValues: () => fromJS({}),
116+
url: () => fromJS({})
117+
},
118+
getConfigs: () => configs
119+
}
120+
// When
121+
let executeFn = executeRequest({
122+
pathName: "/one",
123+
method: "GET",
124+
operation: fromJS({operationId: "getOne"})
125+
})
126+
let res = executeFn(system)
127+
128+
// Then
129+
expect(system.fn.execute.calls.length).toEqual(1)
130+
expect(system.fn.execute.calls[0].arguments[0]).toIncludeKey("requestInterceptor")
131+
expect(system.fn.execute.calls[0].arguments[0]).toInclude({
132+
responseInterceptor: configs.responseInterceptor
133+
})
134+
expect(system.specActions.setMutatedRequest.calls.length).toEqual(0)
135+
expect(system.specActions.setRequest.calls.length).toEqual(1)
136+
137+
138+
let wrappedRequestInterceptor = system.fn.execute.calls[0].arguments[0].requestInterceptor
139+
wrappedRequestInterceptor(system.fn.execute.calls[0].arguments[0])
140+
expect(configs.requestInterceptor.calls.length).toEqual(1)
141+
expect(system.specActions.setMutatedRequest.calls.length).toEqual(1)
142+
expect(system.specActions.setRequest.calls.length).toEqual(1)
143+
})
96144
})
97145

98146
xit("should call specActions.setResponse, when fn.execute resolves", function(){

0 commit comments

Comments
 (0)