Merge branch 'master' into ft/3584-operation-container-component

This commit is contained in:
Owen Conti
2017-08-20 08:07:15 -06:00
committed by GitHub
15 changed files with 203 additions and 18 deletions

View File

@@ -22,7 +22,7 @@ The OpenAPI Specification has undergone 5 revisions since initial creation in 20
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
------------------ | ------------ | -------------------------- | -----
3.1.5 | 2017-08-11 | 2.0, 3.0 | [tag v3.1.5](https://github.com/swagger-api/swagger-ui/tree/v3.1.5)
3.1.6 | 2017-08-18 | 2.0, 3.0 | [tag v3.1.6](https://github.com/swagger-api/swagger-ui/tree/v3.1.6)
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10)
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5)
@@ -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

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;AAoyKA;;;;;;AAm/EA;;;;;;;;;;;;;;;;;;;;;;;;;;AAs6TA;;;;;;;;;;;;;;AAs8JA;;;;;;;;;AAogpBA;;;;;AAk1QA;AAm4DA;;;;;;AAo4YA;;;;;;AA8jaA;AAumvBA","sourceRoot":""}
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;AAoyKA;;;;;;AAm/EA;;;;;;;;;;;;;;;;;;;;;;;;;;AAy7TA;;;;;;;;;;;;;;AAs8JA;;;;;;;;;AAwhpBA;;;;;AA41QA;AAm4DA;;;;;;AAo4YA;;;;;;AA8jaA;AAumvBA","sourceRoot":""}

4
dist/swagger-ui.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AA0/cA","sourceRoot":""}
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;;;;;;AA2idA","sourceRoot":""}

View File

@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.1.5",
"version": "3.1.6",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [

View File

@@ -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 (
<div>
{ request && <Curl request={ request }/> }
{ curlRequest && <Curl request={ curlRequest }/> }
<h4>Server response</h4>
<table className="responses-table">
<thead>

View File

@@ -206,6 +206,7 @@ export default class Operation extends PureComponent {
request={ request }
tryItOutResponse={ response }
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
specActions={ specActions }
produces={ produces }

View File

@@ -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 {
<LiveResponse request={ request }
response={ tryItOutResponse }
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
pathMethod={ this.props.pathMethod }
displayRequestDuration={ displayRequestDuration } />
<h4>Responses</h4>
</div>

View File

@@ -41,6 +41,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.
@@ -63,6 +66,9 @@ module.exports = function SwaggerUI(opts) {
let queryConfig = parseSearch()
const domNode = opts.domNode
delete opts.domNode
const constructorConfig = deepExtend({}, defaults, opts, queryConfig)
const storeConfigs = deepExtend({}, constructorConfig.store, {
@@ -106,8 +112,8 @@ module.exports = function SwaggerUI(opts) {
let mergedConfig = deepExtend({}, localConfig, constructorConfig, fetchedConfig || {}, queryConfig)
// deep extend mangles domNode, we need to set it manually
if(opts.domNode) {
mergedConfig.domNode = opts.domNode
if(domNode) {
mergedConfig.domNode = domNode
}
store.setConfigs(mergedConfig)

View File

@@ -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()

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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("<LiveResponse/>", 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: "<response/>",
})
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(<LiveResponse {...props}/>)
// 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])
})
})
})

View File

@@ -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(){