Merge branch 'master' into ft/docs

This commit is contained in:
kyle
2017-12-12 14:34:27 -08:00
committed by GitHub
46 changed files with 381 additions and 127 deletions

View File

@@ -22,7 +22,7 @@ The OpenAPI Specification has undergone 5 revisions since initial creation in 20
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
------------------ | ------------ | -------------------------- | -----
3.6.0 | 2017-12-01 | 2.0, 3.0 | [tag v3.5=6.0](https://github.com/swagger-api/swagger-ui/tree/v3.6.0)
3.6.1 | 2017-12-01 | 2.0, 3.0 | [tag v3.6.1](https://github.com/swagger-api/swagger-ui/tree/v3.6.1)
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)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

View File

@@ -47,7 +47,7 @@ Parameter Name | Description
Parameter Name | Description
--- | ---
`deepLinking` | `Boolean=false`. If set to `true`, enables deep linking for tags and operations. See the [Deep Linking documentation](https://github.com/swagger-api/swagger-ui/blob/master/docs/deep-linking.md) for more information.
`deepLinking` | `Boolean=false`. If set to `true`, enables deep linking for tags and operations. See the [Deep Linking documentation](/docs/usage/deep-linking.md) for more information.
`displayOperationId` | `Boolean=false`. Controls the display of operationId in operations list. The default is `false`.
`defaultModelExpandDepth` | `Number=1`. The default expansion depth for models.
`defaultModelRendering` | `String=["example"*, "model"]`. Controls how models are shown when the API is first rendered. (The user can always switch the rendering for a given model by clicking the 'Model' and 'Example Value' links.)

View File

@@ -137,7 +137,7 @@ module.exports = function(rules, options) {
}
},
devtool: specialOptions.sourcemaps ? "nosource-source-map" : null,
devtool: specialOptions.sourcemaps ? "nosource-source-map" : false,
plugins,

View File

@@ -1,6 +1,6 @@
{
"name": "swagger-ui",
"version": "3.6.0",
"version": "3.6.1",
"main": "dist/swagger-ui.js",
"repository": "git@github.com:swagger-api/swagger-ui.git",
"contributors": [
@@ -81,13 +81,14 @@
"scroll-to-element": "^2.0.0",
"serialize-error": "2.0.0",
"shallowequal": "0.2.2",
"swagger-client": "^3.4.0",
"swagger-client": "^3.4.1",
"url-parse": "^1.1.8",
"whatwg-fetch": "0.11.1",
"worker-loader": "^0.7.1",
"xml": "1.0.1",
"xml-but-prettier": "^1.0.1",
"yaml-js": "0.2.0"
"yaml-js": "0.2.0",
"zenscroll": "4.0.0"
},
"devDependencies": {
"autoprefixer": "7.1.1",

View File

@@ -12,11 +12,12 @@ export default class ArrayModel extends Component {
name: PropTypes.string,
required: PropTypes.bool,
expandDepth: PropTypes.number,
specPath: PropTypes.array.isRequired,
depth: PropTypes.number
}
render(){
let { getComponent, getConfigs, schema, depth, expandDepth, name } = this.props
let { getComponent, getConfigs, schema, depth, expandDepth, name, specPath } = this.props
let description = schema.get("description")
let items = schema.get("items")
let title = schema.get("title") || name
@@ -38,7 +39,7 @@ export default class ArrayModel extends Component {
*/
return <span className="model">
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent="[...]">
<ModelCollapse title={titleEl} expanded={ depth <= expandDepth } collapsedContent="[...]">
[
{
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
@@ -47,7 +48,7 @@ export default class ArrayModel extends Component {
!description ? null :
<Markdown source={ description } />
}
<span><Model { ...this.props } getConfigs={ getConfigs } name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
<span><Model { ...this.props } getConfigs={ getConfigs } specPath={[...specPath, "items"]} name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
]
</ModelCollapse>
</span>

View File

@@ -73,7 +73,7 @@ const ThrownErrorItem = ( { error, jumpToLine } ) => {
<span style={{ whiteSpace: "pre-line", "maxWidth": "100%" }}>
{ error.get("message") }
</span>
<div>
<div style={{ "text-decoration": "underline", "cursor": "pointer" }}>
{ errorLine && jumpToLine ? <a onClick={jumpToLine.bind(null, errorLine)}>Jump to line { errorLine }</a> : null }
</div>
</div>

View File

@@ -61,7 +61,18 @@ export default class BaseLayout extends React.Component {
const isSpecEmpty = !specSelectors.specStr()
if(isSpecEmpty) {
return <h4>No spec provided.</h4>
let loadingMessage
if(isLoading) {
loadingMessage = <div className="loading"></div>
} else {
loadingMessage = <h4>No API definition provided.</h4>
}
return <div className="swagger-ui">
<div className="loading-container">
{loadingMessage}
</div>
</div>
}
return (

View File

@@ -4,31 +4,47 @@ import PropTypes from "prop-types"
export default class ModelCollapse extends Component {
static propTypes = {
collapsedContent: PropTypes.any,
collapsed: PropTypes.bool,
expanded: PropTypes.bool,
children: PropTypes.any,
title: PropTypes.element
title: PropTypes.element,
modelName: PropTypes.string,
onToggle: PropTypes.func.isRequired
}
static defaultProps = {
collapsedContent: "{...}",
collapsed: true,
expanded: false,
title: null
}
constructor(props, context) {
super(props, context)
let { collapsed, collapsedContent } = this.props
let { expanded, collapsedContent } = this.props
this.state = {
collapsed: collapsed !== undefined ? collapsed : ModelCollapse.defaultProps.collapsed,
expanded : expanded,
collapsedContent: collapsedContent || ModelCollapse.defaultProps.collapsedContent
}
}
componentWillReceiveProps(nextProps){
if(this.props.expanded!= nextProps.expanded){
this.setState({expanded: nextProps.expanded})
}
}
toggleCollapsed=()=>{
if(this.props.onToggle){
this.props.onToggle(this.props.modelName,!this.state.expanded)
}
this.setState({
collapsed: !this.state.collapsed
expanded: !this.state.expanded
})
}
@@ -38,10 +54,10 @@ export default class ModelCollapse extends Component {
<span>
{ title && <span onClick={this.toggleCollapsed} style={{ "cursor": "pointer" }}>{title}</span> }
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
<span className={ "model-toggle" + ( this.state.collapsed ? " collapsed" : "" ) }></span>
<span className={ "model-toggle" + ( this.state.expanded ? "" : " collapsed" ) }></span>
</span>
{ this.state.collapsed ? this.state.collapsedContent : this.props.children }
{ this.state.expanded ? this.props.children :this.state.collapsedContent }
</span>
)
}
}
}

View File

@@ -8,7 +8,8 @@ export default class ModelExample extends React.Component {
schema: PropTypes.object.isRequired,
example: PropTypes.any.isRequired,
isExecute: PropTypes.bool,
getConfigs: PropTypes.func.isRequired
getConfigs: PropTypes.func.isRequired,
specPath: PropTypes.array.isRequired,
}
constructor(props, context) {
@@ -32,7 +33,7 @@ export default class ModelExample extends React.Component {
}
render() {
let { getComponent, specSelectors, schema, example, isExecute, getConfigs } = this.props
let { getComponent, specSelectors, schema, example, isExecute, getConfigs, specPath } = this.props
let { defaultModelExpandDepth } = getConfigs()
const ModelWrapper = getComponent("ModelWrapper")
@@ -54,7 +55,8 @@ export default class ModelExample extends React.Component {
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
expandDepth={ defaultModelExpandDepth } />
expandDepth={ defaultModelExpandDepth }
specPath={specPath} />
}

View File

@@ -1,22 +1,41 @@
import React, { Component, } from "react"
import PropTypes from "prop-types"
//import layoutActions from "actions/layout"
export default class ModelWrapper extends Component {
export default class ModelComponent extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
name: PropTypes.string,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
expandDepth: PropTypes.number
expandDepth: PropTypes.number,
layoutActions: PropTypes.object,
layoutSelectors: PropTypes.object.isRequired
}
onToggle = (name,isShown) => {
// If this prop is present, we'll have deepLinking for it
if(this.props.layoutActions) {
this.props.layoutActions.show(["models", name],isShown)
}
}
render(){
let { getComponent, getConfigs } = this.props
const Model = getComponent("Model")
let expanded
if(this.props.layoutSelectors) {
// If this is prop is present, we'll have deepLinking for it
expanded = this.props.layoutSelectors.isShown(["models",this.props.name])
}
return <div className="model-box">
<Model { ...this.props } getConfigs={ getConfigs } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
<Model { ...this.props } getConfigs={ getConfigs } expanded={expanded} depth={ 1 } onToggle={ this.onToggle } expandDepth={ this.props.expandDepth || 0 }/>
</div>
}
}

View File

@@ -12,7 +12,8 @@ export default class Model extends PureComponent {
isRef: PropTypes.bool,
required: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number
depth: PropTypes.number,
specPath: PropTypes.array.isRequired,
}
getModelName =( ref )=> {
@@ -31,7 +32,7 @@ export default class Model extends PureComponent {
}
render () {
let { getComponent, getConfigs, specSelectors, schema, required, name, isRef } = this.props
let { getComponent, getConfigs, specSelectors, schema, required, name, isRef, specPath } = this.props
const ObjectModel = getComponent("ObjectModel")
const ArrayModel = getComponent("ArrayModel")
const PrimitiveModel = getComponent("PrimitiveModel")
@@ -55,6 +56,7 @@ export default class Model extends PureComponent {
case "object":
return <ObjectModel
className="object" { ...this.props }
specPath={specPath}
getConfigs={ getConfigs }
schema={ schema }
name={ name }

View File

@@ -15,6 +15,7 @@ export default class Models extends Component {
let definitions = specSelectors.definitions()
let { docExpansion, defaultModelExpandDepth } = getConfigs()
let showModels = layoutSelectors.isShown("models", docExpansion === "full" || docExpansion === "list" )
const specPathBase = specSelectors.isOAS3() ? ["components", "schemas"] : ["definitions"]
const ModelWrapper = getComponent("ModelWrapper")
const Collapse = getComponent("Collapse")
@@ -31,13 +32,17 @@ export default class Models extends Component {
<Collapse isOpened={showModels}>
{
definitions.entrySeq().map( ( [ name, model ])=>{
return <div className="model-container" key={ `models-section-${name}` }>
return <div id={ `model-${name}` } className="model-container" key={ `models-section-${name}` }>
<ModelWrapper name={ name }
expandDepth={ defaultModelExpandDepth }
schema={ model }
specPath={[...specPathBase, name]}
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }/>
specSelectors={ specSelectors }
getConfigs = {getConfigs}
layoutSelectors = {layoutSelectors}
layoutActions = {layoutActions}/>
</div>
}).toArray()
}

View File

@@ -10,17 +10,20 @@ export default class ObjectModel extends Component {
schema: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
expanded: PropTypes.bool,
onToggle: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
isRef: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number
depth: PropTypes.number,
specPath: PropTypes.object.isRequired
}
render(){
let { schema, name, isRef, getComponent, getConfigs, depth, expandDepth, ...otherProps } = this.props
let { specSelectors } = otherProps
let { isOAS3 } = specSelectors
let { schema, name, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props
let { specSelectors,expandDepth } = otherProps
const { isOAS3 } = specSelectors
if(!schema) {
return null
@@ -39,14 +42,13 @@ export default class ObjectModel extends Component {
const Model = getComponent("Model")
const ModelCollapse = getComponent("ModelCollapse")
const JumpToPathSection = ({ name }) => {
const path = isOAS3 && isOAS3() ? `components.schemas.${name}` : `definitions.${name}`
return <span className="model-jump-to-path"><JumpToPath path={path} /></span>
const JumpToPathSection = () => {
return <span className="model-jump-to-path"><JumpToPath specPath={specPath} /></span>
}
const collapsedContent = (<span>
<span>{ braceOpen }</span>...<span>{ braceClose }</span>
{
isRef ? <JumpToPathSection name={ name }/> : ""
isRef ? <JumpToPathSection /> : ""
}
</span>)
@@ -60,10 +62,16 @@ export default class ObjectModel extends Component {
</span>
return <span className="model">
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent={ collapsedContent }>
<ModelCollapse
modelName={name}
title={titleEl}
onToggle = {onToggle}
expanded={ expanded ? true : depth <= expandDepth }
collapsedContent={ collapsedContent }>
<span className="brace-open object">{ braceOpen }</span>
{
!isRef ? null : <JumpToPathSection name={ name }/>
!isRef ? null : <JumpToPathSection />
}
<span className="inner-object">
{
@@ -94,6 +102,7 @@ export default class ObjectModel extends Component {
<Model key={ `object-${name}-${key}_${value}` } { ...otherProps }
required={ isRequired }
getComponent={ getComponent }
specPath={[...specPath, "properties", key]}
getConfigs={ getConfigs }
schema={ value }
depth={ depth + 1 } />
@@ -132,6 +141,7 @@ export default class ObjectModel extends Component {
<td>
<Model { ...otherProps } required={ false }
getComponent={ getComponent }
specPath={[...specPath, "additionalProperties"]}
getConfigs={ getConfigs }
schema={ additionalProperties }
depth={ depth + 1 } />
@@ -146,6 +156,7 @@ export default class ObjectModel extends Component {
{anyOf.map((schema, k) => {
return <div key={k}><Model { ...otherProps } required={ false }
getComponent={ getComponent }
specPath={[...specPath, "anyOf", k]}
getConfigs={ getConfigs }
schema={ schema }
depth={ depth + 1 } /></div>
@@ -161,6 +172,7 @@ export default class ObjectModel extends Component {
{oneOf.map((schema, k) => {
return <div key={k}><Model { ...otherProps } required={ false }
getComponent={ getComponent }
specPath={[...specPath, "oneOf", k]}
getConfigs={ getConfigs }
schema={ schema }
depth={ depth + 1 } /></div>
@@ -177,6 +189,7 @@ export default class ObjectModel extends Component {
<Model { ...otherProps }
required={ false }
getComponent={ getComponent }
specPath={[...specPath, "not"]}
getConfigs={ getConfigs }
schema={ not }
depth={ depth + 1 } />

View File

@@ -6,6 +6,7 @@ import { Iterable } from "immutable"
export default class Operation extends PureComponent {
static propTypes = {
specPath: PropTypes.array.isRequired,
operation: PropTypes.instanceOf(Iterable).isRequired,
response: PropTypes.instanceOf(Iterable),
request: PropTypes.instanceOf(Iterable),
@@ -31,11 +32,13 @@ export default class Operation extends PureComponent {
static defaultProps = {
operation: null,
response: null,
request: null
request: null,
specPath: []
}
render() {
let {
specPath,
response,
request,
toggleShown,
@@ -57,7 +60,6 @@ export default class Operation extends PureComponent {
let {
isShown,
isAuthorized,
jumpToKey,
path,
method,
op,
@@ -114,6 +116,8 @@ export default class Operation extends PureComponent {
return (
<div className={deprecated ? "opblock opblock-deprecated" : isShown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
<div className={`opblock-summary opblock-summary-${method}`} onClick={toggleShown} >
{/*TODO: convert this into a component, that can be wrapped
and pulled in with getComponent */}
<span className="opblock-summary-method">{method.toUpperCase()}</span>
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
<a
@@ -122,7 +126,7 @@ export default class Operation extends PureComponent {
href={isDeepLinkingEnabled ? `#/${isShownKey.join("/")}` : null}>
<span>{path}</span>
</a>
<JumpToPath path={jumpToKey} />
<JumpToPath path={specPath} /> {/*TODO: use wrapComponents here, swagger-ui doesn't care about jumpToPath */}
</span>
{ !showSummary ? null :
@@ -170,6 +174,7 @@ export default class Operation extends PureComponent {
<Parameters
parameters={parameters}
specPath={[...specPath, "parameters"]}
operation={operation}
onChangeKey={onChangeKey}
onTryoutClick = { onTryoutClick }
@@ -243,6 +248,7 @@ export default class Operation extends PureComponent {
specActions={ specActions }
produces={ produces }
producesValue={ operation.get("produces_value") }
specPath={[...specPath, "responses"]}
path={ path }
method={ method }
displayRequestDuration={ displayRequestDuration }

View File

@@ -2,6 +2,13 @@ import React from "react"
import PropTypes from "prop-types"
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
const SWAGGER2_OPERATION_METHODS = [
"get", "put", "post", "delete", "options", "head", "patch"
]
const OAS3_OPERATION_METHODS = SWAGGER2_OPERATION_METHODS.concat(["trace"])
export default class Operations extends React.Component {
static propTypes = {
@@ -112,9 +119,24 @@ export default class Operations extends React.Component {
operations.map( op => {
const path = op.get("path")
const method = op.get("method")
const specPath = ["paths", path, method]
// FIXME: (someday) this logic should probably be in a selector,
// but doing so would require further opening up
// selectors to the plugin system, to allow for dynamic
// overriding of low-level selectors that other selectors
// rely on. --KS, 12/17
const validMethods = specSelectors.isOAS3() ?
OAS3_OPERATION_METHODS : SWAGGER2_OPERATION_METHODS
if(validMethods.indexOf(method) === -1) {
return null
}
return <OperationContainer
key={`${path}-${method}`}
specPath={specPath}
op={op}
path={path}
method={method}

View File

@@ -14,7 +14,8 @@ export default class ParameterRow extends Component {
onChangeConsumes: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
getConfigs: PropTypes.func.isRequired
getConfigs: PropTypes.func.isRequired,
specPath: PropTypes.array.isRequired,
}
constructor(props, context) {
@@ -69,7 +70,7 @@ export default class ParameterRow extends Component {
}
render() {
let {param, onChange, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod} = this.props
let {param, onChange, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod, specPath} = this.props
let { isOAS3 } = specSelectors
@@ -138,6 +139,7 @@ export default class ParameterRow extends Component {
{
bodyParam && schema ? <ModelExample getComponent={ getComponent }
specPath={[...specPath, "schema"]}
getConfigs={ getConfigs }
isExecute={ isExecute }
specSelectors={ specSelectors }

View File

@@ -20,7 +20,8 @@ export default class Parameters extends Component {
onCancelClick: PropTypes.func,
onChangeKey: PropTypes.array,
pathMethod: PropTypes.array.isRequired,
getConfigs: PropTypes.func.isRequired
getConfigs: PropTypes.func.isRequired,
specPath: PropTypes.array.isRequired,
}
@@ -30,6 +31,7 @@ export default class Parameters extends Component {
tryItOutEnabled: false,
allowTryItOut: true,
onChangeKey: [],
specPath: [],
}
onChange = ( param, value, isXml ) => {
@@ -58,6 +60,7 @@ export default class Parameters extends Component {
parameters,
allowTryItOut,
tryItOutEnabled,
specPath,
fn,
getComponent,
@@ -92,8 +95,10 @@ export default class Parameters extends Component {
</thead>
<tbody>
{
eachMap(parameters, (parameter) => (
<ParameterRow fn={ fn }
eachMap(parameters, (parameter, i) => (
<ParameterRow
fn={ fn }
specPath={[...specPath, i]}
getComponent={ getComponent }
getConfigs={ getConfigs }
param={ parameter }

View File

@@ -47,6 +47,7 @@ export default class Response extends React.Component {
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
specPath: PropTypes.array.isRequired,
fn: PropTypes.object.isRequired,
contentType: PropTypes.string,
controlsAcceptHeader: PropTypes.bool,
@@ -72,6 +73,7 @@ export default class Response extends React.Component {
code,
response,
className,
specPath,
fn,
getComponent,
getConfigs,
@@ -94,16 +96,19 @@ export default class Response extends React.Component {
const ContentType = getComponent("contentType")
var sampleResponse
var schema
var schema, specPathWithPossibleSchema
if(isOAS3()) {
let oas3SchemaForContentType = response.getIn(["content", this.state.responseContentType, "schema"])
const schemaPath = ["content", this.state.responseContentType, "schema"]
const oas3SchemaForContentType = response.getIn(schemaPath)
sampleResponse = oas3SchemaForContentType ? getSampleSchema(oas3SchemaForContentType.toJS(), this.state.responseContentType, {
includeReadOnly: true
}) : null
schema = oas3SchemaForContentType ? inferSchema(oas3SchemaForContentType.toJS()) : null
specPathWithPossibleSchema = oas3SchemaForContentType ? schemaPath : specPath
} else {
schema = inferSchema(response.toJS())
schema = inferSchema(response.toJS()) // TODO: don't convert back and forth. Lets just stick with immutable for inferSchema
specPathWithPossibleSchema = response.has("schema") ? [...specPath, "schema"] : specPath
sampleResponse = schema ? getSampleSchema(schema, contentType, {
includeReadOnly: true,
includeWriteOnly: true // writeOnly has no filtering effect in swagger 2.0
@@ -145,6 +150,7 @@ export default class Response extends React.Component {
{ example ? (
<ModelExample
specPath={specPathWithPossibleSchema}
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }

View File

@@ -17,6 +17,7 @@ export default class Responses extends React.Component {
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
specPath: PropTypes.array.isRequired,
fn: PropTypes.object.isRequired
}
@@ -60,7 +61,8 @@ export default class Responses extends React.Component {
specSelectors,
fn,
producesValue,
displayRequestDuration
displayRequestDuration,
specPath,
} = this.props
let defaultCode = defaultStatusCode( responses )
@@ -114,9 +116,11 @@ export default class Responses extends React.Component {
<tbody>
{
responses.entrySeq().map( ([code, response]) => {
let className = tryItOutResponse && tryItOutResponse.get("status") == code ? "response_current" : ""
return (
<Response key={ code }
specPath={[...specPath, code]}
isDefault={defaultCode === code}
fn={fn}
className={ className }

View File

@@ -31,6 +31,7 @@ export default class OperationContainer extends PureComponent {
request: PropTypes.instanceOf(Iterable),
security: PropTypes.instanceOf(Iterable),
isDeepLinkingEnabled: PropTypes.bool.isRequired,
specPath: PropTypes.array.isRequired,
getComponent: PropTypes.func.isRequired,
authActions: PropTypes.object,
@@ -141,6 +142,7 @@ export default class OperationContainer extends PureComponent {
displayOperationId,
displayRequestDuration,
isDeepLinkingEnabled,
specPath,
specSelectors,
specActions,
getComponent,
@@ -187,6 +189,7 @@ export default class OperationContainer extends PureComponent {
onTryoutClick={this.onTryoutClick}
onCancelClick={this.onCancelClick}
onExecute={this.onExecute}
specPath={specPath}
specActions={ specActions }
specSelectors={ specSelectors }

View File

@@ -3,11 +3,12 @@ import deepExtend from "deep-extend"
import System from "core/system"
import win from "core/window"
import ApisPreset from "core/presets/apis"
import * as AllPlugins from "core/plugins/all"
import { parseSearch } from "core/utils"
if (process.env.NODE_ENV !== "production") {
window.Perf = require("react-addons-perf")
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
win.Perf = require("react-addons-perf")
}
// eslint-disable-next-line no-undef

View File

@@ -1,8 +1,7 @@
import scrollTo from "scroll-to-element"
import zenscroll from "zenscroll"
import { escapeDeepLinkPath } from "core/utils"
const SCROLL_OFFSET = -5
let hasHashBeenParsed = false
let hasHashBeenParsed = false //TODO this forces code to only run once which may prevent scrolling if page not refreshed
export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) => {
@@ -12,7 +11,6 @@ export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args)
if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") {
return
}
if(window.location.hash && !hasHashBeenParsed ) {
let hash = window.location.hash.slice(1) // # is first character
@@ -30,21 +28,23 @@ export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args)
let [tag, operationId] = hash.split("/")
let swaggerUI = document.querySelector(".swagger-ui")
let myScroller = zenscroll.createScroller(swaggerUI)
if(tag && operationId) {
// Pre-expand and scroll to the operation
layoutActions.show(["operations-tag", tag], true)
layoutActions.show(["operations", tag, operationId], true)
scrollTo(`#operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`, {
offset: SCROLL_OFFSET
})
let target = document.getElementById(`operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`)
myScroller.to(target)
} else if(tag) {
// Pre-expand and scroll to the tag
layoutActions.show(["operations-tag", tag], true)
scrollTo(`#operations-tag-${escapeDeepLinkPath(tag)}`, {
offset: SCROLL_OFFSET
})
let target = document.getElementById(`operations-tag-${escapeDeepLinkPath(tag)}`)
myScroller.to(target)
}
}

View File

@@ -37,17 +37,3 @@ export function changeMode(thing, mode="") {
payload: {thing, mode}
}
}
// export function onlyShow(thing, shown=true) {
// thing = normalizeArray(thing)
// if(thing.length < 2)
// throw new Error("layoutActions.onlyShow only works, when `thing` is an array with length > 1")
// return {
// type: ONLY_SHOW,
// payload: {thing, shown}
// }
// }

View File

@@ -1,3 +1,4 @@
import { fromJS } from "immutable"
import {
UPDATE_LAYOUT,
UPDATE_FILTER,
@@ -12,9 +13,14 @@ export default {
[UPDATE_FILTER]: (state, action) => state.set("filter", action.payload),
[SHOW]: (state, action) => {
let thing = action.payload.thing
let shown = action.payload.shown
return state.setIn(["shown"].concat(thing), shown)
const isShown = action.payload.shown
// This is one way to serialize an array, another (preferred) is to convert to json-pointer
// TODO: use json-pointer serilization instead of fromJS(...), for performance
const thingToShow = fromJS(action.payload.thing)
// This is a map of paths to bools
// eg: [one, two] => true
// eg: [one] => false
return state.update("shown", fromJS({}), a => a.set(thingToShow, isShown))
},
[UPDATE_MODE]: (state, action) => {
@@ -24,4 +30,3 @@ export default {
}
}

View File

@@ -1,5 +1,6 @@
import { createSelector } from "reselect"
import { normalizeArray } from "core/utils"
import { fromJS } from "immutable"
const state = state => state
@@ -9,7 +10,7 @@ export const currentFilter = state => state.get("filter")
export const isShown = (state, thing, def) => {
thing = normalizeArray(thing)
return Boolean(state.getIn(["shown", ...thing], def))
return state.get("shown", fromJS({})).get(fromJS(thing), def)
}
export const whatMode = (state, thing, def="") => {
@@ -21,4 +22,3 @@ export const showSummary = createSelector(
state,
state => !isShown(state, "editor")
)

View File

@@ -10,6 +10,7 @@ const RequestBody = ({
specSelectors,
contentType,
isExecute,
specPath,
onChange
}) => {
const Markdown = getComponent("Markdown")
@@ -37,6 +38,7 @@ const RequestBody = ({
expandDepth={1}
isExecute={isExecute}
schema={mediaTypeValue.get("schema")}
specPath={[...specPath, "content", contentType]}
example={<RequestBodyEditor
requestBody={requestBody}
onChange={onChange}
@@ -56,7 +58,8 @@ RequestBody.propTypes = {
specSelectors: PropTypes.object.isRequired,
contentType: PropTypes.string,
isExecute: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired
onChange: PropTypes.func.isRequired,
specPath: PropTypes.array.isRequired
}
export default RequestBody

View File

@@ -6,7 +6,7 @@ export function isOAS3(jsSpec) {
return false
}
return oasVersion.startsWith("3.0.0")
return oasVersion.startsWith("3")
}
export function isSwagger2(jsSpec) {

View File

@@ -29,6 +29,7 @@ class Parameters extends Component {
fn: PropTypes.object.isRequired,
tryItOutEnabled: PropTypes.bool,
allowTryItOut: PropTypes.bool,
specPath: PropTypes.array.isRequired,
onTryoutClick: PropTypes.func,
onCancelClick: PropTypes.func,
onChangeKey: PropTypes.array,
@@ -92,6 +93,7 @@ class Parameters extends Component {
oas3Actions,
oas3Selectors,
pathMethod,
specPath,
operation
} = this.props
@@ -105,6 +107,8 @@ class Parameters extends Component {
const { isOAS3 } = specSelectors
const requestBody = operation.get("requestBody")
const requestBodySpecPath = [...specPath.slice(0, -1), "requestBody"] // remove the "parameters" part
return (
<div className="opblock-section">
<div className="opblock-section-header">
@@ -136,9 +140,10 @@ class Parameters extends Component {
</thead>
<tbody>
{
eachMap(parameters, (parameter) => (
eachMap(parameters, (parameter, i) => (
<ParameterRow fn={ fn }
getComponent={ getComponent }
specPath={[...specPath, i]}
getConfigs={ getConfigs }
param={ parameter }
key={ parameter.get( "name" ) }
@@ -175,6 +180,7 @@ class Parameters extends Component {
</div>
<div className="opblock-description-wrapper">
<RequestBody
specPath={requestBodySpecPath}
requestBody={requestBody}
isExecute={isExecute}
onChange={(value) => {

View File

@@ -17,7 +17,7 @@ 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"
export const ClEAR_VALIDATE_PARAMS = "spec_clear_validate_param"
export const CLEAR_VALIDATE_PARAMS = "spec_clear_validate_param"
export const UPDATE_OPERATION_VALUE = "spec_update_operation_value"
export const UPDATE_RESOLVED = "spec_update_resolved"
export const SET_SCHEME = "set_scheme"
@@ -161,7 +161,7 @@ export const validateParams = ( payload, isOAS3 ) =>{
export function clearValidateParams( payload ){
return {
type: ClEAR_VALIDATE_PARAMS,
type: CLEAR_VALIDATE_PARAMS,
payload:{ pathMethod: payload }
}
}

View File

@@ -15,7 +15,7 @@ import {
UPDATE_OPERATION_VALUE,
CLEAR_RESPONSE,
CLEAR_REQUEST,
ClEAR_VALIDATE_PARAMS,
CLEAR_VALIDATE_PARAMS,
SET_SCHEME
} from "./actions"
@@ -64,7 +64,7 @@ export default {
})
})
},
[ClEAR_VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {
[CLEAR_VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
return parameters.withMutations( parameters => {
for ( let i = 0, len = parameters.count(); i < len; i++ ) {

View File

@@ -4,8 +4,6 @@ import { fromJS, Set, Map, OrderedMap, List } from "immutable"
const DEFAULT_TAG = "default"
const OPERATION_METHODS = ["get", "put", "post", "delete", "options", "head", "patch"]
const state = state => {
return state || Map()
}
@@ -97,9 +95,6 @@ export const operations = createSelector(
return {}
}
path.forEach((operation, method) => {
if(OPERATION_METHODS.indexOf(method) === -1) {
return
}
list = list.push(fromJS({
path: pathName,
method,

View File

@@ -607,7 +607,10 @@ export const getSampleSchema = (schema, contentType="", config={}) => {
export const parseSearch = () => {
let map = {}
let search = window.location.search
let search = win.location.search
if(!search)
return {}
if ( search != "" ) {
let params = search.substr(1).split("&")

View File

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

View File

@@ -1,3 +1,5 @@
import React from "react"
import PropTypes from "prop-types"
@@ -32,17 +34,21 @@ export default class StandaloneLayout extends React.Component {
{ Topbar ? <Topbar /> : null }
{ loadingStatus === "loading" &&
<div className="info">
<h4 className="title">Loading...</h4>
<div className="loading-container">
<div className="loading"></div>
</div>
</div>
}
{ loadingStatus === "failed" &&
<div className="info">
<h4 className="title">Failed to load spec.</h4>
<div className="loading-container">
<h4 className="title">Failed to load API definition.</h4>
</div>
</div>
}
{ loadingStatus === "failedConfig" &&
<div className="info" style={{ maxWidth: "880px", marginLeft: "auto", marginRight: "auto", textAlign: "center" }}>
<h4 className="title">Failed to load config.</h4>
<h4 className="title">Failed to load remote configuration.</h4>
</div>
}
{ !loadingStatus || loadingStatus === "success" && <BaseLayout /> }

View File

@@ -653,6 +653,11 @@
.loading-container
{
padding: 40px 0 60px;
margin-top: 1em;
min-height: 1px;
display: flex;
justify-content: center;
.loading
{
position: relative;

View File

@@ -11,6 +11,7 @@
select
{
min-width: 130px;
max-width: 100%;
}
}

View File

@@ -18,6 +18,7 @@ describe("<Models/>", function(){
return components[c]
},
specSelectors: {
isOAS3: () => false,
definitions: function() {
return fromJS({
def1: {},
@@ -42,7 +43,7 @@ describe("<Models/>", function(){
// Then should render tabs
expect(wrapper.find("ModelCollapse").length).toEqual(1)
expect(wrapper.find("ModelComponent").length).toBeGreaterThan(0)
expect(wrapper.find("ModelWrapper").length).toBeGreaterThan(0)
wrapper.find("ModelComponent").forEach((modelWrapper) => {
expect(modelWrapper.props().expandDepth).toBe(0)
})

View File

@@ -25,6 +25,7 @@ describe("<ObjectModel />", function() {
}
},
isRef : false,
specPath: [],
schema: Immutable.fromJS(
{
"properties": {

View File

@@ -0,0 +1,123 @@
/* eslint-env mocha */
import React from "react"
import expect, { createSpy } from "expect"
import { render } from "enzyme"
import { fromJS } from "immutable"
import Operations from "components/operations"
import {Collapse} from "components/layout-utils"
const components = {
Collapse,
OperationContainer: ({ path, method }) => <span className="mocked-op" id={`${path}-${method}`} />
}
describe("<Operations/>", function(){
it("should render a Swagger2 `get` method, but not a `trace` or `foo` method", function(){
let props = {
fn: {},
specActions: {},
layoutActions: {},
getComponent: (name)=> {
return components[name] || null
},
getConfigs: () => {
return {}
},
specSelectors: {
isOAS3() { return false },
taggedOperations() {
return fromJS({
"default": {
"operations": [
{
"path": "/pets/{id}",
"method": "get"
},
{
"path": "/pets/{id}",
"method": "trace"
},
{
"path": "/pets/{id}",
"method": "foo"
},
]
}
})
},
},
layoutSelectors: {
currentFilter() {
return null
},
isShown() {
return true
},
show() {
return true
}
}
}
let wrapper = render(<Operations {...props}/>)
expect(wrapper.find("span.mocked-op").length).toEqual(1)
expect(wrapper.find("span.mocked-op").eq(0).attr("id")).toEqual("/pets/{id}-get")
})
it("should render an OAS3 `get` and `trace` method, but not a `foo` method", function(){
let props = {
fn: {},
specActions: {},
layoutActions: {},
getComponent: (name)=> {
return components[name] || null
},
getConfigs: () => {
return {}
},
specSelectors: {
isOAS3() { return true },
taggedOperations() {
return fromJS({
"default": {
"operations": [
{
"path": "/pets/{id}",
"method": "get"
},
{
"path": "/pets/{id}",
"method": "trace"
},
{
"path": "/pets/{id}",
"method": "foo"
},
]
}
})
},
},
layoutSelectors: {
currentFilter() {
return null
},
isShown() {
return true
},
show() {
return true
}
}
}
let wrapper = render(<Operations {...props}/>)
expect(wrapper.find("span.mocked-op").length).toEqual(2)
expect(wrapper.find("span.mocked-op").eq(0).attr("id")).toEqual("/pets/{id}-get")
expect(wrapper.find("span.mocked-op").eq(1).attr("id")).toEqual("/pets/{id}-trace")
})
})