Merge branch 'master' into bug/SWAGGER_JSON-doc-fix

This commit is contained in:
Ron
2017-09-11 05:21:10 -07:00
committed by GitHub
42 changed files with 950 additions and 96 deletions

View File

@@ -14,6 +14,10 @@ or anything that violates the specifications.
<!--- Provide a general summary of the issue in the title above --> <!--- Provide a general summary of the issue in the title above -->
<!---
If you aren't sure what Swagger-UI version, see this guide: https://github.com/swagger-api/swagger-ui/blob/master/docs/version-detection.md
--->
| Q | A | Q | A
| ------------------------------- | ------- | ------------------------------- | -------
@@ -21,7 +25,7 @@ or anything that violates the specifications.
| Which Swagger/OpenAPI version? | | Which Swagger/OpenAPI version? |
| Which Swagger-UI version? | | Which Swagger-UI version? |
| How did you install Swagger-UI? | | How did you install Swagger-UI? |
| Which broswer & version? | | Which browser & version? |
| Which operating system? | | Which operating system? |

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 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.2.0 | 2017-09-08 | 2.0, 3.0 | [tag v3.2.0](https://github.com/swagger-api/swagger-ui/tree/v3.2.0)
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21) 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.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) 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. 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. 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) 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 ### Plugins

View File

@@ -11,7 +11,11 @@
var redirectUrl = oauth2.redirectUrl; var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr; var isValid, qp, arr;
qp = (window.location.hash || location.search).substring(1); if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1);
} else {
qp = location.search.substring(1);
}
arr = qp.split("&") arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';}) arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})

View File

@@ -11,7 +11,11 @@
var redirectUrl = oauth2.redirectUrl; var redirectUrl = oauth2.redirectUrl;
var isValid, qp, arr; var isValid, qp, arr;
qp = (window.location.hash || location.search).substring(1); if (/code|token|error/.test(window.location.hash)) {
qp = window.location.hash.substring(1);
} else {
qp = location.search.substring(1);
}
arr = qp.split("&") arr = qp.split("&")
arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';}) arr.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})

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;;;;;AAu7LA;;;;;;AA65DA;;;;;;;;;;;;;;;;;;;;;;;;;;AA68TA;;;;;;;;;;;;;;AAs8JA;;;;;;;;;AA69pBA;;;;;AA81QA;AAm4DA;;;;;;AAo4YA;;;;;;AA0iaA;AA4lvBA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA21CA;;;;;;AAspFA","sourceRoot":""} {"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA80CA;;;;;;AAqpFA","sourceRoot":""}

2
dist/swagger-ui.css vendored

File diff suppressed because one or more lines are too long

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;;;;;;AAokeA","sourceRoot":""}

BIN
docs/images/swagger-ui2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 KiB

BIN
docs/images/swagger-ui3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 KiB

54
docs/version-detection.md Normal file
View File

@@ -0,0 +1,54 @@
# Detecting your Swagger-UI version
At times, you're going to need to know which version of Swagger-UI you use.
The first step would be to detect which major version you currently use, as the method of detecting the version has changed. If your Swagger-UI has been heavily modified and you cannot detect from the look and feel which major version you use, you'd have to try both methods to get the exact version.
To help you visually detect which version you're using, we've included supporting images.
# Swagger-UI 3.X
![Swagger-UI 3](images/swagger-ui3.png)
Some distinct identifiers to Swagger-UI 3.X:
- The API version appears as a badge next to its title.
- If there are schemes or authorizations, they'd appear in a bar above the operations.
- Try it out functionality is not enabled by default.
- All the response codes in the operations appear at after the parameters.
- There's a models section after the operations.
If you've determined this is the version you have, to find the exact version:
- Open your browser's web console (changes between browsers)
- Type `versions` in the console and execute the call.
- You might need to expand the result, until you get a string similar to `swaggerUi : Object { version: "3.1.6", gitRevision: "g786cd47", gitDirty: true, … }`.
- The version taken from that example would be `3.1.6`.
Note: This functionality was added in 3.0.8. If you're unable to execute it, you're likely to use an older version, and in that case the first step would be to upgrade.
# Swagger-UI 2.X and under
![Swagger-UI 2](images/swagger-ui2.png)
Some distinct identifiers to Swagger-UI 3.X:
- The API version appears at the bottom of the page.
- Schemes are not rendered.
- Authorization, if rendered, will appear next to the navigation bar.
- Try it out functionality is enabled by default.
- The successful response code would appear above the parameters, the rest below them.
- There's no models section after the operations.
If you've determined this is the version you have, to find the exact version:
- Navigate to the sources of the UI. Either on your disk or via the view page source functionality in your browser.
- Find an open the `swagger-ui.js`
- At the top of the page, there would be a comment containing the exact version of swagger-ui. This example shows version `2.2.9`:
```
/**
* swagger-ui - Swagger UI is a dependency-free collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API
* @version v2.2.9
* @link http://swagger.io
* @license Apache-2.0
*/
```

View File

@@ -1,6 +1,6 @@
{ {
"name": "swagger-ui", "name": "swagger-ui",
"version": "3.1.5", "version": "3.2.0",
"main": "dist/swagger-ui.js", "main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git", "repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [ "contributors": [
@@ -75,7 +75,7 @@
"scroll-to-element": "^2.0.0", "scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0", "serialize-error": "2.0.0",
"shallowequal": "0.2.2", "shallowequal": "0.2.2",
"swagger-client": "3.0.20", "swagger-client": "3.1.0",
"url-parse": "^1.1.8", "url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1", "whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1", "worker-loader": "^0.7.1",

View File

@@ -8,6 +8,8 @@ export default class BaseLayout extends React.Component {
errActions: PropTypes.object.isRequired, errActions: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired, specActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired,
oas3Selectors: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired, layoutSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired getComponent: PropTypes.func.isRequired
@@ -19,7 +21,14 @@ export default class BaseLayout extends React.Component {
} }
render() { render() {
let { specSelectors, specActions, getComponent, layoutSelectors } = this.props let {
specSelectors,
specActions,
getComponent,
layoutSelectors,
oas3Selectors,
oas3Actions
} = this.props
let info = specSelectors.info() let info = specSelectors.info()
let url = specSelectors.url() let url = specSelectors.url()
@@ -28,6 +37,7 @@ export default class BaseLayout extends React.Component {
let securityDefinitions = specSelectors.securityDefinitions() let securityDefinitions = specSelectors.securityDefinitions()
let externalDocs = specSelectors.externalDocs() let externalDocs = specSelectors.externalDocs()
let schemes = specSelectors.schemes() let schemes = specSelectors.schemes()
let servers = specSelectors.servers()
let Info = getComponent("info") let Info = getComponent("info")
let Operations = getComponent("operations", true) let Operations = getComponent("operations", true)
@@ -35,6 +45,7 @@ export default class BaseLayout extends React.Component {
let AuthorizeBtn = getComponent("authorizeBtn", true) let AuthorizeBtn = getComponent("authorizeBtn", true)
let Row = getComponent("Row") let Row = getComponent("Row")
let Col = getComponent("Col") let Col = getComponent("Col")
let Servers = getComponent("Servers")
let Errors = getComponent("errors", true) let Errors = getComponent("errors", true)
let isLoading = specSelectors.loadingStatus() === "loading" let isLoading = specSelectors.loadingStatus() === "loading"
@@ -82,6 +93,22 @@ export default class BaseLayout extends React.Component {
</div> </div>
) : null } ) : null }
{ servers && servers.size ? (
<div className="server-container">
<Col className="servers wrapper" mobile={12}>
<Servers
servers={servers}
currentServer={oas3Selectors.selectedServer()}
setSelectedServer={oas3Actions.setSelectedServer}
setServerVariableValue={oas3Actions.setServerVariableValue}
getServerVariable={oas3Selectors.serverVariableValue}
getEffectiveServerValue={oas3Selectors.serverEffectiveValue}
/>
</Col>
</div>
) : null}
{ {
filter === null || filter === false ? null : filter === null || filter === false ? null :
<div className="filter-container"> <div className="filter-container">

View File

@@ -29,13 +29,18 @@ Duration.propTypes = {
export default class LiveResponse extends React.Component { export default class LiveResponse extends React.Component {
static propTypes = { static propTypes = {
response: PropTypes.object.isRequired, response: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
pathMethod: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
displayRequestDuration: PropTypes.bool.isRequired displayRequestDuration: PropTypes.bool.isRequired,
getConfigs: PropTypes.func.isRequired
} }
render() { 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 status = response.get("status")
const url = response.get("url") const url = response.get("url")
const headers = response.get("headers").toJS() const headers = response.get("headers").toJS()
@@ -55,7 +60,7 @@ export default class LiveResponse extends React.Component {
return ( return (
<div> <div>
{ request && <Curl request={ request }/> } { curlRequest && <Curl request={ curlRequest }/> }
<h4>Server response</h4> <h4>Server response</h4>
<table className="responses-table"> <table className="responses-table">
<thead> <thead>

View File

@@ -163,8 +163,8 @@ export default class Operation extends PureComponent {
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } > <span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
<a <a
className="nostyle" className="nostyle"
onClick={(e) => e.preventDefault()} onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
href={ isDeepLinkingEnabled ? `#/${isShownKey[1]}/${isShownKey[2]}` : ""} > href={isDeepLinkingEnabled ? `#/${isShownKey[1]}/${isShownKey[2]}` : null}>
<span>{path}</span> <span>{path}</span>
</a> </a>
<JumpToPath path={jumpToKey} /> <JumpToPath path={jumpToKey} />
@@ -263,6 +263,7 @@ export default class Operation extends PureComponent {
request={ request } request={ request }
tryItOutResponse={ response } tryItOutResponse={ response }
getComponent={ getComponent } getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors } specSelectors={ specSelectors }
specActions={ specActions } specActions={ specActions }
produces={ produces } produces={ produces }

View File

@@ -81,8 +81,8 @@ export default class Operations extends React.Component {
id={isShownKey.join("-")}> id={isShownKey.join("-")}>
<a <a
className="nostyle" className="nostyle"
onClick={(e) => e.preventDefault()} onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
href={ isDeepLinkingEnabled ? `#/${tag}` : ""}> href= {isDeepLinkingEnabled ? `#/${tag}` : null}>
<span>{tag}</span> <span>{tag}</span>
</a> </a>
{ !tagDescription ? null : { !tagDescription ? null :

View File

@@ -16,7 +16,8 @@ export default class Responses extends React.Component {
specActions: PropTypes.object.isRequired, specActions: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired, pathMethod: PropTypes.array.isRequired,
displayRequestDuration: PropTypes.bool.isRequired, displayRequestDuration: PropTypes.bool.isRequired,
fn: PropTypes.object.isRequired fn: PropTypes.object.isRequired,
getConfigs: PropTypes.func.isRequired
} }
static defaultProps = { static defaultProps = {
@@ -29,7 +30,7 @@ export default class Responses extends React.Component {
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val) onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
render() { 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 ) let defaultCode = defaultStatusCode( responses )
const ContentType = getComponent( "contentType" ) const ContentType = getComponent( "contentType" )
@@ -57,6 +58,9 @@ export default class Responses extends React.Component {
<LiveResponse request={ request } <LiveResponse request={ request }
response={ tryItOutResponse } response={ tryItOutResponse }
getComponent={ getComponent } getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
pathMethod={ this.props.pathMethod }
displayRequestDuration={ displayRequestDuration } /> displayRequestDuration={ displayRequestDuration } />
<h4>Responses</h4> <h4>Responses</h4>
</div> </div>

View File

@@ -42,6 +42,9 @@ module.exports = function SwaggerUI(opts) {
displayOperationId: false, displayOperationId: false,
displayRequestDuration: false, displayRequestDuration: false,
deepLinking: 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. // 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. // Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest.
@@ -64,6 +67,9 @@ module.exports = function SwaggerUI(opts) {
let queryConfig = parseSearch() let queryConfig = parseSearch()
const domNode = opts.domNode
delete opts.domNode
const constructorConfig = deepExtend({}, defaults, opts, queryConfig) const constructorConfig = deepExtend({}, defaults, opts, queryConfig)
const storeConfigs = deepExtend({}, constructorConfig.store, { const storeConfigs = deepExtend({}, constructorConfig.store, {
@@ -107,8 +113,8 @@ module.exports = function SwaggerUI(opts) {
let mergedConfig = deepExtend({}, localConfig, constructorConfig, fetchedConfig || {}, queryConfig) let mergedConfig = deepExtend({}, localConfig, constructorConfig, fetchedConfig || {}, queryConfig)
// deep extend mangles domNode, we need to set it manually // deep extend mangles domNode, we need to set it manually
if(opts.domNode) { if(domNode) {
mergedConfig.domNode = opts.domNode mergedConfig.domNode = domNode
} }
store.setConfigs(mergedConfig) store.setConfigs(mergedConfig)

View File

@@ -0,0 +1,35 @@
// Actions conform to FSA (flux-standard-actions)
// {type: string,payload: Any|Error, meta: obj, error: bool}
export const UPDATE_SELECTED_SERVER = "oas3_set_servers"
export const UPDATE_REQUEST_BODY_VALUE = "oas3_set_request_body_value"
export const UPDATE_REQUEST_CONTENT_TYPE = "oas3_set_request_content_type"
export const UPDATE_SERVER_VARIABLE_VALUE = "oas3_set_server_variable_value"
export function setSelectedServer (selectedServerUrl) {
return {
type: UPDATE_SELECTED_SERVER,
payload: selectedServerUrl
}
}
export function setRequestBodyValue ({ value, pathMethod }) {
return {
type: UPDATE_REQUEST_BODY_VALUE,
payload: { value, pathMethod }
}
}
export function setRequestContentType ({ value, pathMethod }) {
return {
type: UPDATE_REQUEST_CONTENT_TYPE,
payload: { value, pathMethod }
}
}
export function setServerVariableValue ({ server, key, val }) {
return {
type: UPDATE_SERVER_VARIABLE_VALUE,
payload: { server, key, val }
}
}

View File

@@ -1,9 +1,13 @@
import Callbacks from "./callbacks" import Callbacks from "./callbacks"
import RequestBody from "./request-body" import RequestBody from "./request-body"
import OperationLink from "./operation-link.jsx" import OperationLink from "./operation-link.jsx"
import Servers from "./servers"
import RequestBodyEditor from "./request-body-editor"
export default { export default {
Callbacks, Callbacks,
RequestBody, RequestBody,
Servers,
RequestBodyEditor,
operationLink: OperationLink operationLink: OperationLink
} }

View File

@@ -0,0 +1,114 @@
import React, { PureComponent } from "react"
import PropTypes from "prop-types"
import { fromJS } from "immutable"
import { getSampleSchema } from "core/utils"
const NOOP = Function.prototype
export default class RequestBodyEditor extends PureComponent {
static propTypes = {
requestBody: PropTypes.object.isRequired,
mediaType: PropTypes.string.isRequired,
onChange: PropTypes.func,
getComponent: PropTypes.func.isRequired,
isExecute: PropTypes.bool,
specSelectors: PropTypes.object.isRequired,
};
static defaultProps = {
mediaType: "application/json",
requestBody: fromJS({}),
onChange: NOOP,
};
constructor(props, context) {
super(props, context)
this.state = {
isEditBox: false,
value: ""
}
}
componentDidMount() {
this.setValueToSample.call(this)
}
componentWillReceiveProps(nextProps) {
if(this.props.mediaType !== nextProps.mediaType) {
// media type was changed
this.setValueToSample(nextProps.mediaType)
}
if(!this.props.isExecute && nextProps.isExecute) {
// we just entered execute mode,
// so enable editing for convenience
this.setState({ isEditBox: true })
}
}
setValueToSample = (explicitMediaType) => {
this.onChange(this.sample(explicitMediaType))
}
sample = (explicitMediaType) => {
let { requestBody, mediaType } = this.props
let schema = requestBody.getIn(["content", explicitMediaType || mediaType, "schema"]).toJS()
return getSampleSchema(schema, explicitMediaType || mediaType, {
includeWriteOnly: true
})
}
onChange = (value) => {
this.setState({value})
this.props.onChange(value)
}
handleOnChange = e => {
const { mediaType } = this.props
const isJson = /json/i.test(mediaType)
const inputValue = isJson ? e.target.value.trim() : e.target.value
this.onChange(inputValue)
}
toggleIsEditBox = () => this.setState( state => ({isEditBox: !state.isEditBox}))
render() {
let {
isExecute,
getComponent,
} = this.props
const Button = getComponent("Button")
const TextArea = getComponent("TextArea")
const HighlightCode = getComponent("highlightCode")
let { value, isEditBox } = this.state
return (
<div className="body-param">
{
isEditBox && isExecute
? <TextArea className={"body-param__text"} value={value} onChange={ this.handleOnChange }/>
: (value && <HighlightCode className="body-param__example"
value={ value }/>)
}
<div className="body-param-options">
{
!isExecute ? null
: <div className="body-param-edit">
<Button className={isEditBox ? "btn cancel body-param__example-edit" : "btn edit body-param__example-edit"}
onClick={this.toggleIsEditBox}>{ isEditBox ? "Cancel" : "Edit"}
</Button>
</div>
}
</div>
</div>
)
}
}

View File

@@ -2,13 +2,18 @@ import React from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes" import ImPropTypes from "react-immutable-proptypes"
import { OrderedMap } from "immutable" import { OrderedMap } from "immutable"
import { getSampleSchema } from "core/utils"
const RequestBody = ({
const RequestBody = ({ requestBody, getComponent, specSelectors, contentType }) => { requestBody,
getComponent,
specSelectors,
contentType,
isExecute,
onChange
}) => {
const Markdown = getComponent("Markdown") const Markdown = getComponent("Markdown")
const ModelExample = getComponent("modelExample") const ModelExample = getComponent("modelExample")
const HighlightCode = getComponent("highlightCode") const RequestBodyEditor = getComponent("RequestBodyEditor")
const requestBodyDescription = (requestBody && requestBody.get("description")) || null const requestBodyDescription = (requestBody && requestBody.get("description")) || null
const requestBodyContent = (requestBody && requestBody.get("content")) || new OrderedMap() const requestBodyContent = (requestBody && requestBody.get("content")) || new OrderedMap()
@@ -16,10 +21,6 @@ const RequestBody = ({ requestBody, getComponent, specSelectors, contentType })
const mediaTypeValue = requestBodyContent.get(contentType) const mediaTypeValue = requestBodyContent.get(contentType)
const sampleSchema = getSampleSchema(mediaTypeValue.get("schema").toJS(), contentType, {
includeWriteOnly: true
})
return <div> return <div>
{ requestBodyDescription && { requestBodyDescription &&
<Markdown source={requestBodyDescription} /> <Markdown source={requestBodyDescription} />
@@ -28,8 +29,16 @@ const RequestBody = ({ requestBody, getComponent, specSelectors, contentType })
getComponent={ getComponent } getComponent={ getComponent }
specSelectors={ specSelectors } specSelectors={ specSelectors }
expandDepth={1} expandDepth={1}
isExecute={isExecute}
schema={mediaTypeValue.get("schema")} schema={mediaTypeValue.get("schema")}
example={<HighlightCode value={sampleSchema} />} example={<RequestBodyEditor
requestBody={requestBody}
onChange={onChange}
mediaType={contentType}
getComponent={getComponent}
isExecute={isExecute}
specSelectors={specSelectors}
/>}
/> />
</div> </div>
} }
@@ -38,7 +47,9 @@ RequestBody.propTypes = {
requestBody: ImPropTypes.orderedMap.isRequired, requestBody: ImPropTypes.orderedMap.isRequired,
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired,
contentType: PropTypes.string.isRequired contentType: PropTypes.string.isRequired,
isExecute: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired
} }
export default RequestBody export default RequestBody

View File

@@ -0,0 +1,155 @@
import React from "react"
import { OrderedMap } from "immutable"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
export default class Servers extends React.Component {
static propTypes = {
servers: ImPropTypes.list.isRequired,
currentServer: PropTypes.string.isRequired,
setSelectedServer: PropTypes.func.isRequired,
setServerVariableValue: PropTypes.func.isRequired,
getServerVariable: PropTypes.func.isRequired,
getEffectiveServerValue: PropTypes.func.isRequired
}
componentDidMount() {
let { servers } = this.props
//fire 'change' event to set default 'value' of select
this.setServer(servers.first().get("url"))
}
componentWillReceiveProps(nextProps) {
let {
servers,
setServerVariableValue,
getServerVariable
} = this.props
if(this.props.currentServer !== nextProps.currentServer) {
// Server has changed, we may need to set default values
let currentServerDefinition = servers
.find(v => v.get("url") === nextProps.currentServer) || OrderedMap()
let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
currentServerVariableDefs.map((val, key) => {
let currentValue = getServerVariable(nextProps.currentServer, key)
// only set the default value if the user hasn't set one yet
if(!currentValue) {
setServerVariableValue({
server: nextProps.currentServer,
key,
val: val.get("default") || ""
})
}
})
}
}
onServerChange =( e ) => {
this.setServer( e.target.value )
// set default variable values
}
onServerVariableValueChange = ( e ) => {
let {
setServerVariableValue,
currentServer
} = this.props
let variableName = e.target.getAttribute("data-variable")
let newVariableValue = e.target.value
if(typeof setServerVariableValue === "function") {
setServerVariableValue({
server: currentServer,
key: variableName,
val: newVariableValue
})
}
}
setServer = ( value ) => {
let { setSelectedServer } = this.props
setSelectedServer(value)
}
render() {
let { servers,
currentServer,
getServerVariable,
getEffectiveServerValue
} = this.props
let currentServerDefinition = servers.find(v => v.get("url") === currentServer) || OrderedMap()
let currentServerVariableDefs = currentServerDefinition.get("variables") || OrderedMap()
let shouldShowVariableUI = currentServerVariableDefs.size !== 0
return (
<div>
<label htmlFor="servers">
<span className="servers-title">Servers</span>
<select onChange={ this.onServerChange }>
{ servers.valueSeq().map(
( server ) =>
<option
value={ server.get("url") }
key={ server.get("url") }>
{ server.get("url") }
</option>
).toArray()}
</select>
</label>
{ shouldShowVariableUI ?
<div>
<h4>Server variables</h4>
<div className={"computed-url"}>
Computed URL:
<code>
{getEffectiveServerValue(currentServer)}
</code>
</div>
<table>
<tbody>
{
currentServerVariableDefs.map((val, name) => {
return <tr key={name}>
<td>{name}</td>
<td>
{ val.get("enum") ?
<select data-variable={name} onChange={this.onServerVariableValueChange}>
{val.get("enum").map(enumValue => {
return <option
selected={enumValue === getServerVariable(currentServer, name)}
key={enumValue}
value={enumValue}>
{enumValue}
</option>
})}
</select> :
<input
type={"text"}
value={getServerVariable(currentServer, name) || ""}
onChange={this.onServerVariableValueChange}
data-variable={name}
></input>
}
</td>
</tr>
})
}
</tbody>
</table>
</div>: null
}
</div>
)
}
}

View File

@@ -1,8 +1,12 @@
// import reducers from "./reducers" // import reducers from "./reducers"
// import * as actions from "./actions" // import * as actions from "./actions"
import * as wrapSelectors from "./wrap-selectors" import * as specWrapSelectors from "./spec-extensions/wrap-selectors"
import * as specSelectors from "./spec-extensions/selectors"
import components from "./components" import components from "./components"
import wrapComponents from "./wrap-components" import wrapComponents from "./wrap-components"
import * as oas3Actions from "./actions"
import * as oas3Selectors from "./selectors"
import oas3Reducers from "./reducers"
export default function() { export default function() {
return { return {
@@ -10,7 +14,13 @@ export default function() {
wrapComponents, wrapComponents,
statePlugins: { statePlugins: {
spec: { spec: {
wrapSelectors wrapSelectors: specWrapSelectors,
selectors: specSelectors
},
oas3: {
actions: oas3Actions,
reducers: oas3Reducers,
selectors: oas3Selectors,
} }
} }
} }

View File

@@ -0,0 +1,23 @@
import {
UPDATE_SELECTED_SERVER,
UPDATE_REQUEST_BODY_VALUE,
UPDATE_REQUEST_CONTENT_TYPE,
UPDATE_SERVER_VARIABLE_VALUE
} from "./actions"
export default {
[UPDATE_SELECTED_SERVER]: (state, { payload: selectedServerUrl } ) =>{
return state.setIn( [ "selectedServer" ], selectedServerUrl)
},
[UPDATE_REQUEST_BODY_VALUE]: (state, { payload: { value, pathMethod } } ) =>{
let [path, method] = pathMethod
return state.setIn( [ "requestData", path, method, "bodyValue" ], value)
},
[UPDATE_REQUEST_CONTENT_TYPE]: (state, { payload: { value, pathMethod } } ) =>{
let [path, method] = pathMethod
return state.setIn( [ "requestData", path, method, "requestContentType" ], value)
},
[UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, key, val } } ) =>{
return state.setIn( [ "serverVariableValues", server, key ], val)
},
}

View File

@@ -0,0 +1,53 @@
import { OrderedMap } from "immutable"
import { isOAS3 as isOAS3Helper } from "./helpers"
// Helpers
function onlyOAS3(selector) {
return (...args) => (system) => {
const spec = system.getSystem().specSelectors.specJson()
if(isOAS3Helper(spec)) {
return selector(...args)
} else {
return null
}
}
}
export const selectedServer = onlyOAS3(state => {
return state.getIn(["selectedServer"]) || ""
}
)
export const requestBodyValue = onlyOAS3((state, path, method) => {
return state.getIn(["requestData", path, method, "bodyValue"]) || null
}
)
export const requestContentType = onlyOAS3((state, path, method) => {
return state.getIn(["requestData", path, method, "requestContentType"]) || null
}
)
export const serverVariableValue = onlyOAS3((state, server, key) => {
return state.getIn(["serverVariableValues", server, key]) || null
}
)
export const serverVariables = onlyOAS3((state, server) => {
return state.getIn(["serverVariableValues", server]) || OrderedMap()
}
)
export const serverEffectiveValue = onlyOAS3((state, server) => {
let varValues = state.getIn(["serverVariableValues", server]) || OrderedMap()
let str = server
varValues.map((val, key) => {
str = str.replace(new RegExp(`{${key}}`, "g"), val)
})
return str
}
)

View File

@@ -0,0 +1,50 @@
import { createSelector } from "reselect"
import { Map } from "immutable"
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
// Helpers
function onlyOAS3(selector) {
return () => (system, ...args) => {
const spec = system.getSystem().specSelectors.specJson()
if(isOAS3Helper(spec)) {
return selector(...args)
} else {
return null
}
}
}
const state = state => {
return state || Map()
}
const specJson = createSelector(
state,
spec => spec.get("json", Map())
)
const specResolved = createSelector(
state,
spec => spec.get("resolved", Map())
)
const spec = state => {
let res = specResolved(state)
if(res.count() < 1)
res = specJson(state)
return res
}
// New selectors
export const servers = onlyOAS3(createSelector(
spec,
spec => spec.getIn(["servers"]) || Map()
))
export const isSwagger2 = (ori, system) => () => {
const spec = system.getSystem().specSelectors.specJson()
return isSwagger2Helper(spec)
}

View File

@@ -1,6 +1,6 @@
import { createSelector } from "reselect" import { createSelector } from "reselect"
import { Map } from "immutable" import { Map } from "immutable"
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "./helpers" import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
// Helpers // Helpers
@@ -56,6 +56,11 @@ export const schemes = OAS3NullSelector
// New selectors // New selectors
export const servers = onlyOAS3(createSelector(
spec,
spec => spec.getIn(["servers"]) || Map()
))
export const isOAS3 = (ori, system) => () => { export const isOAS3 = (ori, system) => () => {
const spec = system.getSystem().specSelectors.specJson() const spec = system.getSystem().specSelectors.specJson()
return isOAS3Helper(spec) return isOAS3Helper(spec)

View File

@@ -3,7 +3,6 @@ import parameters from "./parameters"
import VersionStamp from "./version-stamp" import VersionStamp from "./version-stamp"
import OnlineValidatorBadge from "./online-validator-badge" import OnlineValidatorBadge from "./online-validator-badge"
import Model from "./model" import Model from "./model"
import TryItOutButton from "./try-it-out-button"
export default { export default {
Markdown, Markdown,
@@ -11,5 +10,4 @@ export default {
VersionStamp, VersionStamp,
model: Model, model: Model,
onlineValidatorBadge: OnlineValidatorBadge, onlineValidatorBadge: OnlineValidatorBadge,
TryItOutButton
} }

View File

@@ -13,8 +13,7 @@ class Parameters extends Component {
super(props) super(props)
this.state = { this.state = {
callbackVisible: false, callbackVisible: false,
parametersVisible: true, parametersVisible: true
requestBodyContentType: ""
} }
} }
@@ -24,6 +23,8 @@ class Parameters extends Component {
operation: PropTypes.object.isRequired, operation: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
oas3Selectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired, fn: PropTypes.object.isRequired,
tryItOutEnabled: PropTypes.bool, tryItOutEnabled: PropTypes.bool,
allowTryItOut: PropTypes.bool, allowTryItOut: PropTypes.bool,
@@ -86,6 +87,8 @@ class Parameters extends Component {
fn, fn,
getComponent, getComponent,
specSelectors, specSelectors,
oas3Actions,
oas3Selectors,
pathMethod, pathMethod,
operation operation
} = this.props } = this.props
@@ -159,16 +162,22 @@ class Parameters extends Component {
<h4 className={`opblock-title parameter__name ${requestBody.get("required") && "required"}`}>Request body</h4> <h4 className={`opblock-title parameter__name ${requestBody.get("required") && "required"}`}>Request body</h4>
<label> <label>
<ContentType <ContentType
value={this.state.requestBodyContentType} value={oas3Selectors.requestContentType(...pathMethod)}
contentTypes={ requestBody.get("content").keySeq() } contentTypes={ requestBody.get("content").keySeq() }
onChange={(val) => this.setState({ requestBodyContentType: val })} onChange={(value) => {
oas3Actions.setRequestContentType({ value, pathMethod })
}}
className="body-param-content-type" /> className="body-param-content-type" />
</label> </label>
</div> </div>
<div className="opblock-description-wrapper"> <div className="opblock-description-wrapper">
<RequestBody <RequestBody
requestBody={requestBody} requestBody={requestBody}
contentType={this.state.requestBodyContentType}/> isExecute={isExecute}
onChange={(value) => {
oas3Actions.setRequestBodyValue({ value, pathMethod })
}}
contentType={oas3Selectors.requestContentType(...pathMethod)}/>
</div> </div>
</div> </div>
} }

View File

@@ -1,5 +0,0 @@
import { OAS3ComponentWrapFactory } from "../helpers"
export default OAS3ComponentWrapFactory(() => {
return null
})

View File

@@ -1,6 +1,7 @@
import YAML from "js-yaml" import YAML from "js-yaml"
import parseUrl from "url-parse" import parseUrl from "url-parse"
import serializeError from "serialize-error" import serializeError from "serialize-error"
import { isJSONObject } from "core/utils"
// Actions conform to FSA (flux-standard-actions) // Actions conform to FSA (flux-standard-actions)
// {type: string,payload: Any|Error, meta: obj, error: bool} // {type: string,payload: Any|Error, meta: obj, error: bool}
@@ -12,6 +13,7 @@ export const UPDATE_PARAM = "spec_update_param"
export const VALIDATE_PARAMS = "spec_validate_param" export const VALIDATE_PARAMS = "spec_validate_param"
export const SET_RESPONSE = "spec_set_response" export const SET_RESPONSE = "spec_set_response"
export const SET_REQUEST = "spec_set_request" 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 LOG_REQUEST = "spec_log_request"
export const CLEAR_RESPONSE = "spec_clear_response" export const CLEAR_RESPONSE = "spec_clear_response"
export const CLEAR_REQUEST = "spec_clear_request" export const CLEAR_REQUEST = "spec_clear_request"
@@ -177,6 +179,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 // This is for debugging, remove this comment if you depend on this action
export const logRequest = (req) => { export const logRequest = (req) => {
return { return {
@@ -187,8 +196,10 @@ export const logRequest = (req) => {
// Actually fire the request via fn.execute // Actually fire the request via fn.execute
// (For debugging) and ease of testing // (For debugging) and ease of testing
export const executeRequest = (req) => ({fn, specActions, specSelectors}) => { export const executeRequest = (req) =>
({fn, specActions, specSelectors, getConfigs, oas3Selectors}) => {
let { pathName, method, operation } = req let { pathName, method, operation } = req
let { requestInterceptor, responseInterceptor } = getConfigs()
let op = operation.toJS() let op = operation.toJS()
@@ -202,11 +213,35 @@ export const executeRequest = (req) => ({fn, specActions, specSelectors}) => {
req.operationId = fn.opId(op, pathName, method) req.operationId = fn.opId(op, pathName, method)
} }
if(specSelectors.isOAS3()) {
// OAS3 request feature support
req.server = oas3Selectors.selectedServer()
req.serverVariables = oas3Selectors.serverVariables(req.server).toJS()
req.requestContentType = oas3Selectors.requestContentType(pathName, method)
const requestBody = oas3Selectors.requestBodyValue(pathName, method)
if(isJSONObject(requestBody)) {
req.requestBody = JSON.parse(requestBody)
} else {
req.requestBody = requestBody
}
}
let parsedRequest = Object.assign({}, req) let parsedRequest = Object.assign({}, req)
parsedRequest = fn.buildRequest(parsedRequest) parsedRequest = fn.buildRequest(parsedRequest)
specActions.setRequest(req.pathName, req.method, parsedRequest) 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 // track duration of request
const startTime = Date.now() const startTime = Date.now()
@@ -215,7 +250,11 @@ export const executeRequest = (req) => ({fn, specActions, specSelectors}) => {
res.duration = Date.now() - startTime res.duration = Date.now() - startTime
specActions.setResponse(req.pathName, req.method, res) specActions.setResponse(req.pathName, req.method, res)
} ) } )
.catch( err => specActions.setResponse(req.pathName, req.method, { error: true, err: serializeError(err) } ) ) .catch(
err => specActions.setResponse(req.pathName, req.method, {
error: true, err: serializeError(err)
})
)
} }

View File

@@ -10,6 +10,7 @@ import {
VALIDATE_PARAMS, VALIDATE_PARAMS,
SET_RESPONSE, SET_RESPONSE,
SET_REQUEST, SET_REQUEST,
SET_MUTATED_REQUEST,
UPDATE_RESOLVED, UPDATE_RESOLVED,
UPDATE_OPERATION_VALUE, UPDATE_OPERATION_VALUE,
CLEAR_RESPONSE, CLEAR_RESPONSE,
@@ -101,6 +102,10 @@ export default {
return state.setIn( [ "requests", path, method ], fromJSOrdered(req)) 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 } }) => { [UPDATE_OPERATION_VALUE]: (state, { payload: { path, value, key } }) => {
let operationPath = ["resolved", "paths", ...path] let operationPath = ["resolved", "paths", ...path]
if(!state.getIn(operationPath)) { if(!state.getIn(operationPath)) {

View File

@@ -237,6 +237,11 @@ export const requests = createSelector(
state => state.get( "requests", Map() ) state => state.get( "requests", Map() )
) )
export const mutatedRequests = createSelector(
state,
state => state.get( "mutatedRequests", Map() )
)
export const responseFor = (state, path, method) => { export const responseFor = (state, path, method) => {
return responses(state).getIn([path, method], null) return responses(state).getIn([path, method], null)
} }
@@ -245,6 +250,10 @@ export const requestFor = (state, path, method) => {
return requests(state).getIn([path, method], null) return requests(state).getIn([path, method], null)
} }
export const mutatedRequestFor = (state, path, method) => {
return mutatedRequests(state).getIn([path, method], null)
}
export const allowTryItOutFor = () => { export const allowTryItOutFor = () => {
// This is just a hook for now. // This is just a hook for now.
return true return true

View File

@@ -13,6 +13,25 @@ const DEFAULT_REPONSE_KEY = "default"
export const isImmutable = (maybe) => Im.Iterable.isIterable(maybe) export const isImmutable = (maybe) => Im.Iterable.isIterable(maybe)
export function isJSONObject (str) {
try {
var o = JSON.parse(str)
// Handle non-exception-throwing cases:
// Neither JSON.parse(false) or JSON.parse(1234) throw errors, hence the type-checking,
// but... JSON.parse(null) returns null, and typeof null === "object",
// so we must check for that, too. Thankfully, null is falsey, so this suffices:
if (o && typeof o === "object") {
return o
}
}
catch (e) {
// do nothing
}
return false
}
export function objectify (thing) { export function objectify (thing) {
if(!isObject(thing)) if(!isObject(thing))
return {} return {}

View File

@@ -1,2 +1,5 @@
// Promise global, Used ( at least ) by 'whatwg-fetch'. And required by IE 11 // Promise global, Used ( at least ) by 'whatwg-fetch'. And required by IE 11
if(!window.Promise) {
require("core-js/fn/promise") require("core-js/fn/promise")
}

View File

@@ -636,6 +636,77 @@
} }
} }
.server-container
{
margin: 0 0 20px 0;
padding: 30px 0;
background: #fff;
box-shadow: 0 1px 2px 0 rgba(0,0,0,.15);
.computed-url {
margin: 2em 0;
code {
color: grey;
display: inline-block;
padding: 4px;
font-size: 16px;
margin: 0 1em;
font-style: italic;
}
}
.servers
{
display: flex;
align-items: center;
.servers-title {
margin-right: 1em;
}
> label
{
font-size: 12px;
display: flex;
flex-direction: column;
margin: -20px 15px 0 0;
@include text_headline();
select
{
min-width: 130px;
}
}
table {
tr {
width: 30em;
}
td {
display: inline-block;
max-width: 15em;
vertical-align: middle;
padding-top: 10px;
padding-bottom: 10px;
&:first-of-type {
padding-right: 2em;
}
input {
width: 100%;
height: 100%;
}
}
}
}
}
.loading-container .loading-container
{ {
@@ -726,12 +797,12 @@ section
a.nostyle { a.nostyle {
text-decoration: inherit; text-decoration: inherit;
color: inherit; color: inherit;
cursor: auto; cursor: pointer;
display: inline; display: inline;
&:visited { &:visited {
text-decoration: inherit; text-decoration: inherit;
color: inherit; color: inherit;
cursor: auto; cursor: pointer;
} }
} }

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,55 @@ 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({}),
isOAS3: () => false
},
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(){ xit("should call specActions.setResponse, when fn.execute resolves", function(){