Merge branch 'master' into bug/SWAGGER_JSON-doc-fix
This commit is contained in:
6
.github/issue_template.md
vendored
6
.github/issue_template.md
vendored
@@ -14,6 +14,10 @@ or anything that violates the specifications.
|
||||
|
||||
<!--- 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
|
||||
| ------------------------------- | -------
|
||||
@@ -21,7 +25,7 @@ or anything that violates the specifications.
|
||||
| Which Swagger/OpenAPI version? |
|
||||
| Which Swagger-UI version? |
|
||||
| How did you install Swagger-UI? |
|
||||
| Which broswer & version? |
|
||||
| Which browser & version? |
|
||||
| Which operating system? |
|
||||
|
||||
|
||||
|
||||
@@ -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.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)
|
||||
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
|
||||
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
var redirectUrl = oauth2.redirectUrl;
|
||||
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.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
|
||||
|
||||
6
dist/oauth2-redirect.html
vendored
6
dist/oauth2-redirect.html
vendored
@@ -11,7 +11,11 @@
|
||||
var redirectUrl = oauth2.redirectUrl;
|
||||
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.forEach(function (v,i,_arr) { _arr[i] = '"' + v.replace('=', '":"') + '"';})
|
||||
|
||||
22
dist/swagger-ui-bundle.js
vendored
22
dist/swagger-ui-bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/swagger-ui-bundle.js.map
vendored
2
dist/swagger-ui-bundle.js.map
vendored
@@ -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":""}
|
||||
6
dist/swagger-ui-standalone-preset.js
vendored
6
dist/swagger-ui-standalone-preset.js
vendored
File diff suppressed because one or more lines are too long
2
dist/swagger-ui-standalone-preset.js.map
vendored
2
dist/swagger-ui-standalone-preset.js.map
vendored
@@ -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
2
dist/swagger-ui.css
vendored
File diff suppressed because one or more lines are too long
4
dist/swagger-ui.js
vendored
4
dist/swagger-ui.js
vendored
File diff suppressed because one or more lines are too long
2
dist/swagger-ui.js.map
vendored
2
dist/swagger-ui.js.map
vendored
@@ -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
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
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
54
docs/version-detection.md
Normal 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
|
||||
|
||||

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

|
||||
|
||||
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
|
||||
*/
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "swagger-ui",
|
||||
"version": "3.1.5",
|
||||
"version": "3.2.0",
|
||||
"main": "dist/swagger-ui.js",
|
||||
"repository": "git@github.com:swagger-api/swagger-ui.git",
|
||||
"contributors": [
|
||||
@@ -75,7 +75,7 @@
|
||||
"scroll-to-element": "^2.0.0",
|
||||
"serialize-error": "2.0.0",
|
||||
"shallowequal": "0.2.2",
|
||||
"swagger-client": "3.0.20",
|
||||
"swagger-client": "3.1.0",
|
||||
"url-parse": "^1.1.8",
|
||||
"whatwg-fetch": "0.11.1",
|
||||
"worker-loader": "^0.7.1",
|
||||
|
||||
@@ -8,6 +8,8 @@ export default class BaseLayout extends React.Component {
|
||||
errActions: PropTypes.object.isRequired,
|
||||
specActions: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
oas3Selectors: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
layoutSelectors: PropTypes.object.isRequired,
|
||||
layoutActions: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired
|
||||
@@ -19,7 +21,14 @@ export default class BaseLayout extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let { specSelectors, specActions, getComponent, layoutSelectors } = this.props
|
||||
let {
|
||||
specSelectors,
|
||||
specActions,
|
||||
getComponent,
|
||||
layoutSelectors,
|
||||
oas3Selectors,
|
||||
oas3Actions
|
||||
} = this.props
|
||||
|
||||
let info = specSelectors.info()
|
||||
let url = specSelectors.url()
|
||||
@@ -28,6 +37,7 @@ export default class BaseLayout extends React.Component {
|
||||
let securityDefinitions = specSelectors.securityDefinitions()
|
||||
let externalDocs = specSelectors.externalDocs()
|
||||
let schemes = specSelectors.schemes()
|
||||
let servers = specSelectors.servers()
|
||||
|
||||
let Info = getComponent("info")
|
||||
let Operations = getComponent("operations", true)
|
||||
@@ -35,6 +45,7 @@ export default class BaseLayout extends React.Component {
|
||||
let AuthorizeBtn = getComponent("authorizeBtn", true)
|
||||
let Row = getComponent("Row")
|
||||
let Col = getComponent("Col")
|
||||
let Servers = getComponent("Servers")
|
||||
let Errors = getComponent("errors", true)
|
||||
|
||||
let isLoading = specSelectors.loadingStatus() === "loading"
|
||||
@@ -82,6 +93,22 @@ export default class BaseLayout extends React.Component {
|
||||
</div>
|
||||
) : 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 :
|
||||
<div className="filter-container">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -163,8 +163,8 @@ export default class Operation extends PureComponent {
|
||||
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
|
||||
<a
|
||||
className="nostyle"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
href={ isDeepLinkingEnabled ? `#/${isShownKey[1]}/${isShownKey[2]}` : ""} >
|
||||
onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
|
||||
href={isDeepLinkingEnabled ? `#/${isShownKey[1]}/${isShownKey[2]}` : null}>
|
||||
<span>{path}</span>
|
||||
</a>
|
||||
<JumpToPath path={jumpToKey} />
|
||||
@@ -263,6 +263,7 @@ export default class Operation extends PureComponent {
|
||||
request={ request }
|
||||
tryItOutResponse={ response }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
specSelectors={ specSelectors }
|
||||
specActions={ specActions }
|
||||
produces={ produces }
|
||||
|
||||
@@ -81,8 +81,8 @@ export default class Operations extends React.Component {
|
||||
id={isShownKey.join("-")}>
|
||||
<a
|
||||
className="nostyle"
|
||||
onClick={(e) => e.preventDefault()}
|
||||
href={ isDeepLinkingEnabled ? `#/${tag}` : ""}>
|
||||
onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
|
||||
href= {isDeepLinkingEnabled ? `#/${tag}` : null}>
|
||||
<span>{tag}</span>
|
||||
</a>
|
||||
{ !tagDescription ? null :
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.
|
||||
@@ -64,6 +67,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, {
|
||||
@@ -107,8 +113,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)
|
||||
|
||||
35
src/core/plugins/oas3/actions.js
Normal file
35
src/core/plugins/oas3/actions.js
Normal 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 }
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
import Callbacks from "./callbacks"
|
||||
import RequestBody from "./request-body"
|
||||
import OperationLink from "./operation-link.jsx"
|
||||
import Servers from "./servers"
|
||||
import RequestBodyEditor from "./request-body-editor"
|
||||
|
||||
export default {
|
||||
Callbacks,
|
||||
RequestBody,
|
||||
Servers,
|
||||
RequestBodyEditor,
|
||||
operationLink: OperationLink
|
||||
}
|
||||
|
||||
114
src/core/plugins/oas3/components/request-body-editor.jsx
Normal file
114
src/core/plugins/oas3/components/request-body-editor.jsx
Normal 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>
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,18 @@ import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { OrderedMap } from "immutable"
|
||||
import { getSampleSchema } from "core/utils"
|
||||
|
||||
|
||||
const RequestBody = ({ requestBody, getComponent, specSelectors, contentType }) => {
|
||||
const RequestBody = ({
|
||||
requestBody,
|
||||
getComponent,
|
||||
specSelectors,
|
||||
contentType,
|
||||
isExecute,
|
||||
onChange
|
||||
}) => {
|
||||
const Markdown = getComponent("Markdown")
|
||||
const ModelExample = getComponent("modelExample")
|
||||
const HighlightCode = getComponent("highlightCode")
|
||||
const RequestBodyEditor = getComponent("RequestBodyEditor")
|
||||
|
||||
const requestBodyDescription = (requestBody && requestBody.get("description")) || null
|
||||
const requestBodyContent = (requestBody && requestBody.get("content")) || new OrderedMap()
|
||||
@@ -16,10 +21,6 @@ const RequestBody = ({ requestBody, getComponent, specSelectors, contentType })
|
||||
|
||||
const mediaTypeValue = requestBodyContent.get(contentType)
|
||||
|
||||
const sampleSchema = getSampleSchema(mediaTypeValue.get("schema").toJS(), contentType, {
|
||||
includeWriteOnly: true
|
||||
})
|
||||
|
||||
return <div>
|
||||
{ requestBodyDescription &&
|
||||
<Markdown source={requestBodyDescription} />
|
||||
@@ -28,8 +29,16 @@ const RequestBody = ({ requestBody, getComponent, specSelectors, contentType })
|
||||
getComponent={ getComponent }
|
||||
specSelectors={ specSelectors }
|
||||
expandDepth={1}
|
||||
isExecute={isExecute}
|
||||
schema={mediaTypeValue.get("schema")}
|
||||
example={<HighlightCode value={sampleSchema} />}
|
||||
example={<RequestBodyEditor
|
||||
requestBody={requestBody}
|
||||
onChange={onChange}
|
||||
mediaType={contentType}
|
||||
getComponent={getComponent}
|
||||
isExecute={isExecute}
|
||||
specSelectors={specSelectors}
|
||||
/>}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
@@ -38,7 +47,9 @@ RequestBody.propTypes = {
|
||||
requestBody: ImPropTypes.orderedMap.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
contentType: PropTypes.string.isRequired
|
||||
contentType: PropTypes.string.isRequired,
|
||||
isExecute: PropTypes.bool.isRequired,
|
||||
onChange: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default RequestBody
|
||||
|
||||
155
src/core/plugins/oas3/components/servers.jsx
Normal file
155
src/core/plugins/oas3/components/servers.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
// import reducers from "./reducers"
|
||||
// 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 wrapComponents from "./wrap-components"
|
||||
import * as oas3Actions from "./actions"
|
||||
import * as oas3Selectors from "./selectors"
|
||||
import oas3Reducers from "./reducers"
|
||||
|
||||
export default function() {
|
||||
return {
|
||||
@@ -10,7 +14,13 @@ export default function() {
|
||||
wrapComponents,
|
||||
statePlugins: {
|
||||
spec: {
|
||||
wrapSelectors
|
||||
wrapSelectors: specWrapSelectors,
|
||||
selectors: specSelectors
|
||||
},
|
||||
oas3: {
|
||||
actions: oas3Actions,
|
||||
reducers: oas3Reducers,
|
||||
selectors: oas3Selectors,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
23
src/core/plugins/oas3/reducers.js
Normal file
23
src/core/plugins/oas3/reducers.js
Normal 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)
|
||||
},
|
||||
}
|
||||
53
src/core/plugins/oas3/selectors.js
Normal file
53
src/core/plugins/oas3/selectors.js
Normal 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
|
||||
}
|
||||
)
|
||||
50
src/core/plugins/oas3/spec-extensions/selectors.js
Normal file
50
src/core/plugins/oas3/spec-extensions/selectors.js
Normal 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)
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createSelector } from "reselect"
|
||||
import { Map } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "./helpers"
|
||||
import { isOAS3 as isOAS3Helper, isSwagger2 as isSwagger2Helper } from "../helpers"
|
||||
|
||||
|
||||
// Helpers
|
||||
@@ -56,6 +56,11 @@ export const schemes = OAS3NullSelector
|
||||
|
||||
// New selectors
|
||||
|
||||
export const servers = onlyOAS3(createSelector(
|
||||
spec,
|
||||
spec => spec.getIn(["servers"]) || Map()
|
||||
))
|
||||
|
||||
export const isOAS3 = (ori, system) => () => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
return isOAS3Helper(spec)
|
||||
@@ -3,7 +3,6 @@ import parameters from "./parameters"
|
||||
import VersionStamp from "./version-stamp"
|
||||
import OnlineValidatorBadge from "./online-validator-badge"
|
||||
import Model from "./model"
|
||||
import TryItOutButton from "./try-it-out-button"
|
||||
|
||||
export default {
|
||||
Markdown,
|
||||
@@ -11,5 +10,4 @@ export default {
|
||||
VersionStamp,
|
||||
model: Model,
|
||||
onlineValidatorBadge: OnlineValidatorBadge,
|
||||
TryItOutButton
|
||||
}
|
||||
|
||||
@@ -13,8 +13,7 @@ class Parameters extends Component {
|
||||
super(props)
|
||||
this.state = {
|
||||
callbackVisible: false,
|
||||
parametersVisible: true,
|
||||
requestBodyContentType: ""
|
||||
parametersVisible: true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +23,8 @@ class Parameters extends Component {
|
||||
operation: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
oas3Selectors: PropTypes.object.isRequired,
|
||||
fn: PropTypes.object.isRequired,
|
||||
tryItOutEnabled: PropTypes.bool,
|
||||
allowTryItOut: PropTypes.bool,
|
||||
@@ -86,6 +87,8 @@ class Parameters extends Component {
|
||||
fn,
|
||||
getComponent,
|
||||
specSelectors,
|
||||
oas3Actions,
|
||||
oas3Selectors,
|
||||
pathMethod,
|
||||
operation
|
||||
} = this.props
|
||||
@@ -159,16 +162,22 @@ class Parameters extends Component {
|
||||
<h4 className={`opblock-title parameter__name ${requestBody.get("required") && "required"}`}>Request body</h4>
|
||||
<label>
|
||||
<ContentType
|
||||
value={this.state.requestBodyContentType}
|
||||
value={oas3Selectors.requestContentType(...pathMethod)}
|
||||
contentTypes={ requestBody.get("content").keySeq() }
|
||||
onChange={(val) => this.setState({ requestBodyContentType: val })}
|
||||
onChange={(value) => {
|
||||
oas3Actions.setRequestContentType({ value, pathMethod })
|
||||
}}
|
||||
className="body-param-content-type" />
|
||||
</label>
|
||||
</div>
|
||||
<div className="opblock-description-wrapper">
|
||||
<RequestBody
|
||||
requestBody={requestBody}
|
||||
contentType={this.state.requestBodyContentType}/>
|
||||
isExecute={isExecute}
|
||||
onChange={(value) => {
|
||||
oas3Actions.setRequestBodyValue({ value, pathMethod })
|
||||
}}
|
||||
contentType={oas3Selectors.requestContentType(...pathMethod)}/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
|
||||
export default OAS3ComponentWrapFactory(() => {
|
||||
return null
|
||||
})
|
||||
@@ -1,6 +1,7 @@
|
||||
import YAML from "js-yaml"
|
||||
import parseUrl from "url-parse"
|
||||
import serializeError from "serialize-error"
|
||||
import { isJSONObject } from "core/utils"
|
||||
|
||||
// Actions conform to FSA (flux-standard-actions)
|
||||
// {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 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 +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
|
||||
export const logRequest = (req) => {
|
||||
return {
|
||||
@@ -187,8 +196,10 @@ 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, oas3Selectors}) => {
|
||||
let { pathName, method, operation } = req
|
||||
let { requestInterceptor, responseInterceptor } = getConfigs()
|
||||
|
||||
let op = operation.toJS()
|
||||
|
||||
@@ -202,11 +213,35 @@ export const executeRequest = (req) => ({fn, specActions, specSelectors}) => {
|
||||
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)
|
||||
parsedRequest = fn.buildRequest(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
|
||||
const startTime = Date.now()
|
||||
|
||||
@@ -215,8 +250,12 @@ export const executeRequest = (req) => ({fn, specActions, specSelectors}) => {
|
||||
res.duration = Date.now() - startTime
|
||||
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)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// I'm using extras as a way to inject properties into the final, `execute` method - It's not great. Anyone have a better idea? @ponelat
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,6 +13,25 @@ const DEFAULT_REPONSE_KEY = "default"
|
||||
|
||||
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) {
|
||||
if(!isObject(thing))
|
||||
return {}
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
// Promise global, Used ( at least ) by 'whatwg-fetch'. And required by IE 11
|
||||
require("core-js/fn/promise")
|
||||
|
||||
if(!window.Promise) {
|
||||
require("core-js/fn/promise")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -726,12 +797,12 @@ section
|
||||
a.nostyle {
|
||||
text-decoration: inherit;
|
||||
color: inherit;
|
||||
cursor: auto;
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
|
||||
&:visited {
|
||||
text-decoration: inherit;
|
||||
color: inherit;
|
||||
cursor: auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
85
test/components/live-response.js
Normal file
85
test/components/live-response.js
Normal 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])
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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(){
|
||||
|
||||
Reference in New Issue
Block a user