diff --git a/README.md b/README.md
index c543541a..898bd718 100644
--- a/README.md
+++ b/README.md
@@ -160,6 +160,9 @@ displayRequestDuration | Controls the display of the request duration (in millis
maxDisplayedTags | If set, limits the number of tagged operations displayed to at most this many. The default is to show all operations.
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.
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)
+requestInterceptor | MUST be a function. Function to intercept try-it-out requests. Accepts one argument requestInterceptor(request) and must return the potentially modified request.
+responseInterceptor | MUST be a function. Function to intercept try-it-out responses. Accepts one argument responseInterceptor(response) and must return the potentially modified response.
+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.
### Plugins
diff --git a/src/core/components/live-response.jsx b/src/core/components/live-response.jsx
index 2a52d7bf..e9e3fa2d 100644
--- a/src/core/components/live-response.jsx
+++ b/src/core/components/live-response.jsx
@@ -29,13 +29,18 @@ Duration.propTypes = {
export default class LiveResponse extends React.Component {
static propTypes = {
response: PropTypes.object.isRequired,
+ specSelectors: PropTypes.object.isRequired,
+ pathMethod: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
- displayRequestDuration: PropTypes.bool.isRequired
+ displayRequestDuration: PropTypes.bool.isRequired,
+ getConfigs: PropTypes.func.isRequired
}
render() {
- const { request, response, getComponent, displayRequestDuration } = this.props
+ const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, pathMethod } = this.props
+ const { showMutatedRequest } = getConfigs()
+ const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(pathMethod[0], pathMethod[1]) : specSelectors.requestFor(pathMethod[0], pathMethod[1])
const status = response.get("status")
const url = response.get("url")
const headers = response.get("headers").toJS()
@@ -55,7 +60,7 @@ export default class LiveResponse extends React.Component {
return (
- { request &&
}
+ { curlRequest &&
}
Server response
diff --git a/src/core/components/operation.jsx b/src/core/components/operation.jsx
index 4f6534db..b84b40c6 100644
--- a/src/core/components/operation.jsx
+++ b/src/core/components/operation.jsx
@@ -263,6 +263,7 @@ export default class Operation extends PureComponent {
request={ request }
tryItOutResponse={ response }
getComponent={ getComponent }
+ getConfigs={ getConfigs }
specSelectors={ specSelectors }
specActions={ specActions }
produces={ produces }
diff --git a/src/core/components/responses.jsx b/src/core/components/responses.jsx
index 88ab0c92..e365a44b 100644
--- a/src/core/components/responses.jsx
+++ b/src/core/components/responses.jsx
@@ -16,7 +16,8 @@ export default class Responses extends React.Component {
specActions: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
displayRequestDuration: PropTypes.bool.isRequired,
- fn: PropTypes.object.isRequired
+ fn: PropTypes.object.isRequired,
+ getConfigs: PropTypes.func.isRequired
}
static defaultProps = {
@@ -29,7 +30,7 @@ export default class Responses extends React.Component {
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
render() {
- let { responses, request, tryItOutResponse, getComponent, specSelectors, fn, producesValue, displayRequestDuration } = this.props
+ let { responses, request, tryItOutResponse, getComponent, getConfigs, specSelectors, fn, producesValue, displayRequestDuration } = this.props
let defaultCode = defaultStatusCode( responses )
const ContentType = getComponent( "contentType" )
@@ -57,6 +58,9 @@ export default class Responses extends React.Component {
Responses
diff --git a/src/core/index.js b/src/core/index.js
index 9d9c0ac3..924ef14d 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -42,6 +42,9 @@ module.exports = function SwaggerUI(opts) {
displayOperationId: false,
displayRequestDuration: false,
deepLinking: false,
+ requestInterceptor: (a => a),
+ responseInterceptor: (a => a),
+ showMutatedRequest: true,
// Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance.
// Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest.
diff --git a/src/core/plugins/spec/actions.js b/src/core/plugins/spec/actions.js
index 31671b87..34f47d7c 100644
--- a/src/core/plugins/spec/actions.js
+++ b/src/core/plugins/spec/actions.js
@@ -12,6 +12,7 @@ export const UPDATE_PARAM = "spec_update_param"
export const VALIDATE_PARAMS = "spec_validate_param"
export const SET_RESPONSE = "spec_set_response"
export const SET_REQUEST = "spec_set_request"
+export const SET_MUTATED_REQUEST = "spec_set_mutated_request"
export const LOG_REQUEST = "spec_log_request"
export const CLEAR_RESPONSE = "spec_clear_response"
export const CLEAR_REQUEST = "spec_clear_request"
@@ -177,6 +178,13 @@ export const setRequest = ( path, method, req ) => {
}
}
+export const setMutatedRequest = ( path, method, req ) => {
+ return {
+ payload: { path, method, req },
+ type: SET_MUTATED_REQUEST
+ }
+}
+
// This is for debugging, remove this comment if you depend on this action
export const logRequest = (req) => {
return {
@@ -187,8 +195,9 @@ export const logRequest = (req) => {
// Actually fire the request via fn.execute
// (For debugging) and ease of testing
-export const executeRequest = (req) => ({fn, specActions, specSelectors}) => {
+export const executeRequest = (req) => ({fn, specActions, specSelectors, getConfigs}) => {
let { pathName, method, operation } = req
+ let { requestInterceptor, responseInterceptor } = getConfigs()
let op = operation.toJS()
@@ -207,6 +216,16 @@ export const executeRequest = (req) => ({fn, specActions, specSelectors}) => {
specActions.setRequest(req.pathName, req.method, parsedRequest)
+ let requestInterceptorWrapper = function(r) {
+ let mutatedRequest = requestInterceptor.apply(this, [r])
+ let parsedMutatedRequest = Object.assign({}, mutatedRequest)
+ specActions.setMutatedRequest(req.pathName, req.method, parsedMutatedRequest)
+ return mutatedRequest
+ }
+
+ req.requestInterceptor = requestInterceptorWrapper
+ req.responseInterceptor = responseInterceptor
+
// track duration of request
const startTime = Date.now()
diff --git a/src/core/plugins/spec/reducers.js b/src/core/plugins/spec/reducers.js
index d9670d7b..e76c2d6a 100644
--- a/src/core/plugins/spec/reducers.js
+++ b/src/core/plugins/spec/reducers.js
@@ -3,13 +3,14 @@ import { fromJSOrdered, validateParam } from "core/utils"
import win from "../../window"
import {
- UPDATE_SPEC,
+ UPDATE_SPEC,
UPDATE_URL,
UPDATE_JSON,
UPDATE_PARAM,
VALIDATE_PARAMS,
SET_RESPONSE,
SET_REQUEST,
+ SET_MUTATED_REQUEST,
UPDATE_RESOLVED,
UPDATE_OPERATION_VALUE,
CLEAR_RESPONSE,
@@ -101,6 +102,10 @@ export default {
return state.setIn( [ "requests", path, method ], fromJSOrdered(req))
},
+ [SET_MUTATED_REQUEST]: (state, { payload: { req, path, method } } ) =>{
+ return state.setIn( [ "mutatedRequests", path, method ], fromJSOrdered(req))
+ },
+
[UPDATE_OPERATION_VALUE]: (state, { payload: { path, value, key } }) => {
let operationPath = ["resolved", "paths", ...path]
if(!state.getIn(operationPath)) {
diff --git a/src/core/plugins/spec/selectors.js b/src/core/plugins/spec/selectors.js
index fbc12764..a92ef4b3 100644
--- a/src/core/plugins/spec/selectors.js
+++ b/src/core/plugins/spec/selectors.js
@@ -237,6 +237,11 @@ export const requests = createSelector(
state => state.get( "requests", Map() )
)
+export const mutatedRequests = createSelector(
+ state,
+ state => state.get( "mutatedRequests", Map() )
+)
+
export const responseFor = (state, path, method) => {
return responses(state).getIn([path, method], null)
}
@@ -245,6 +250,10 @@ export const requestFor = (state, path, method) => {
return requests(state).getIn([path, method], null)
}
+export const mutatedRequestFor = (state, path, method) => {
+ return mutatedRequests(state).getIn([path, method], null)
+}
+
export const allowTryItOutFor = () => {
// This is just a hook for now.
return true
diff --git a/test/components/live-response.js b/test/components/live-response.js
new file mode 100644
index 00000000..dbaadb6f
--- /dev/null
+++ b/test/components/live-response.js
@@ -0,0 +1,85 @@
+/* eslint-env mocha */
+import React from "react"
+import { fromJSOrdered } from "core/utils"
+import expect, { createSpy } from "expect"
+import { shallow } from "enzyme"
+import Curl from "components/curl"
+import LiveResponse from "components/live-response"
+import ResponseBody from "components/response-body"
+
+describe("", function(){
+ let request = fromJSOrdered({
+ credentials: "same-origin",
+ headers: {
+ accept: "application/xml"
+ },
+ url: "http://petstore.swagger.io/v2/pet/1"
+ })
+
+ let mutatedRequest = fromJSOrdered({
+ credentials: "same-origin",
+ headers: {
+ accept: "application/xml",
+ mutated: "header"
+ },
+ url: "http://petstore.swagger.io/v2/pet/1"
+ })
+
+ let requests = {
+ request: request,
+ mutatedRequest: mutatedRequest
+ }
+
+ const tests = [
+ { showMutatedRequest: true, expected: { request: "mutatedRequest", requestForCalls: 0, mutatedRequestForCalls: 1 } },
+ { showMutatedRequest: false, expected: { request: "request", requestForCalls: 1, mutatedRequestForCalls: 0 } }
+ ]
+
+ tests.forEach(function(test) {
+ it("passes " + test.expected.request + " to Curl when showMutatedRequest = " + test.showMutatedRequest, function() {
+
+ // Given
+
+ let response = fromJSOrdered({
+ status: 200,
+ url: "http://petstore.swagger.io/v2/pet/1",
+ headers: {},
+ text: "",
+ })
+
+ let mutatedRequestForSpy = createSpy().andReturn(mutatedRequest)
+ let requestForSpy = createSpy().andReturn(request)
+
+ let components = {
+ curl: Curl,
+ responseBody: ResponseBody
+ }
+
+ let props = {
+ response: response,
+ specSelectors: {
+ mutatedRequestFor: mutatedRequestForSpy,
+ requestFor: requestForSpy,
+ },
+ pathMethod: [ "/one", "get" ],
+ getComponent: (c) => {
+ return components[c]
+ },
+ displayRequestDuration: true,
+ getConfigs: () => ({ showMutatedRequest: test.showMutatedRequest })
+ }
+
+ // When
+ let wrapper = shallow()
+
+ // Then
+ expect(mutatedRequestForSpy.calls.length).toEqual(test.expected.mutatedRequestForCalls)
+ expect(requestForSpy.calls.length).toEqual(test.expected.requestForCalls)
+
+ const curl = wrapper.find(Curl)
+ expect(curl.length).toEqual(1)
+ expect(curl.props().request).toBe(requests[test.expected.request])
+
+ })
+ })
+})
diff --git a/test/core/plugins/spec/actions.js b/test/core/plugins/spec/actions.js
index 3d254615..dd0305a6 100644
--- a/test/core/plugins/spec/actions.js
+++ b/test/core/plugins/spec/actions.js
@@ -93,6 +93,54 @@ describe("spec plugin - actions", function(){
})
})
+ it("should pass requestInterceptor/responseInterceptor to fn.execute", function(){
+ // Given
+ let configs = {
+ requestInterceptor: createSpy(),
+ responseInterceptor: createSpy()
+ }
+ const system = {
+ fn: {
+ buildRequest: createSpy(),
+ execute: createSpy().andReturn(Promise.resolve())
+ },
+ specActions: {
+ executeRequest: createSpy(),
+ setMutatedRequest: createSpy(),
+ setRequest: createSpy()
+ },
+ specSelectors: {
+ spec: () => fromJS({}),
+ parameterValues: () => fromJS({}),
+ contentTypeValues: () => fromJS({}),
+ url: () => fromJS({})
+ },
+ getConfigs: () => configs
+ }
+ // When
+ let executeFn = executeRequest({
+ pathName: "/one",
+ method: "GET",
+ operation: fromJS({operationId: "getOne"})
+ })
+ let res = executeFn(system)
+
+ // Then
+ expect(system.fn.execute.calls.length).toEqual(1)
+ expect(system.fn.execute.calls[0].arguments[0]).toIncludeKey("requestInterceptor")
+ expect(system.fn.execute.calls[0].arguments[0]).toInclude({
+ responseInterceptor: configs.responseInterceptor
+ })
+ expect(system.specActions.setMutatedRequest.calls.length).toEqual(0)
+ expect(system.specActions.setRequest.calls.length).toEqual(1)
+
+
+ let wrappedRequestInterceptor = system.fn.execute.calls[0].arguments[0].requestInterceptor
+ wrappedRequestInterceptor(system.fn.execute.calls[0].arguments[0])
+ expect(configs.requestInterceptor.calls.length).toEqual(1)
+ expect(system.specActions.setMutatedRequest.calls.length).toEqual(1)
+ expect(system.specActions.setRequest.calls.length).toEqual(1)
+ })
})
xit("should call specActions.setResponse, when fn.execute resolves", function(){