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

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 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`. `displayOperationId` | `Boolean=false`. Controls the display of operationId in operations list. The default is `false`.
`defaultModelExpandDepth` | `Number=1`. The default expansion depth for models. `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.) `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, plugins,

View File

@@ -1,6 +1,6 @@
{ {
"name": "swagger-ui", "name": "swagger-ui",
"version": "3.6.0", "version": "3.6.1",
"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": [
@@ -81,13 +81,14 @@
"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.4.0", "swagger-client": "^3.4.1",
"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",
"xml": "1.0.1", "xml": "1.0.1",
"xml-but-prettier": "^1.0.1", "xml-but-prettier": "^1.0.1",
"yaml-js": "0.2.0" "yaml-js": "0.2.0",
"zenscroll": "4.0.0"
}, },
"devDependencies": { "devDependencies": {
"autoprefixer": "7.1.1", "autoprefixer": "7.1.1",

View File

@@ -12,11 +12,12 @@ export default class ArrayModel extends Component {
name: PropTypes.string, name: PropTypes.string,
required: PropTypes.bool, required: PropTypes.bool,
expandDepth: PropTypes.number, expandDepth: PropTypes.number,
specPath: PropTypes.array.isRequired,
depth: PropTypes.number depth: PropTypes.number
} }
render(){ 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 description = schema.get("description")
let items = schema.get("items") let items = schema.get("items")
let title = schema.get("title") || name let title = schema.get("title") || name
@@ -38,7 +39,7 @@ export default class ArrayModel extends Component {
*/ */
return <span className="model"> 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 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 : !description ? null :
<Markdown source={ description } /> <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> </ModelCollapse>
</span> </span>

View File

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

View File

@@ -61,7 +61,18 @@ export default class BaseLayout extends React.Component {
const isSpecEmpty = !specSelectors.specStr() const isSpecEmpty = !specSelectors.specStr()
if(isSpecEmpty) { 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 ( return (

View File

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

View File

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

View File

@@ -1,22 +1,41 @@
import React, { Component, } from "react" import React, { Component, } from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
//import layoutActions from "actions/layout"
export default class ModelWrapper extends Component {
export default class ModelComponent extends Component {
static propTypes = { static propTypes = {
schema: PropTypes.object.isRequired, schema: PropTypes.object.isRequired,
name: PropTypes.string, name: PropTypes.string,
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired, getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.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(){ render(){
let { getComponent, getConfigs } = this.props let { getComponent, getConfigs } = this.props
const Model = getComponent("Model") 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"> 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> </div>
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,6 +2,13 @@ import React from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import { createDeepLinkPath, sanitizeUrl } from "core/utils" 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 { export default class Operations extends React.Component {
static propTypes = { static propTypes = {
@@ -112,9 +119,24 @@ export default class Operations extends React.Component {
operations.map( op => { operations.map( op => {
const path = op.get("path") const path = op.get("path")
const method = op.get("method") 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 return <OperationContainer
key={`${path}-${method}`} key={`${path}-${method}`}
specPath={specPath}
op={op} op={op}
path={path} path={path}
method={method} method={method}

View File

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

View File

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

View File

@@ -47,6 +47,7 @@ export default class Response extends React.Component {
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired, getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired,
specPath: PropTypes.array.isRequired,
fn: PropTypes.object.isRequired, fn: PropTypes.object.isRequired,
contentType: PropTypes.string, contentType: PropTypes.string,
controlsAcceptHeader: PropTypes.bool, controlsAcceptHeader: PropTypes.bool,
@@ -72,6 +73,7 @@ export default class Response extends React.Component {
code, code,
response, response,
className, className,
specPath,
fn, fn,
getComponent, getComponent,
getConfigs, getConfigs,
@@ -94,16 +96,19 @@ export default class Response extends React.Component {
const ContentType = getComponent("contentType") const ContentType = getComponent("contentType")
var sampleResponse var sampleResponse
var schema var schema, specPathWithPossibleSchema
if(isOAS3()) { 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, { sampleResponse = oas3SchemaForContentType ? getSampleSchema(oas3SchemaForContentType.toJS(), this.state.responseContentType, {
includeReadOnly: true includeReadOnly: true
}) : null }) : null
schema = oas3SchemaForContentType ? inferSchema(oas3SchemaForContentType.toJS()) : null schema = oas3SchemaForContentType ? inferSchema(oas3SchemaForContentType.toJS()) : null
specPathWithPossibleSchema = oas3SchemaForContentType ? schemaPath : specPath
} else { } 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, { sampleResponse = schema ? getSampleSchema(schema, contentType, {
includeReadOnly: true, includeReadOnly: true,
includeWriteOnly: true // writeOnly has no filtering effect in swagger 2.0 includeWriteOnly: true // writeOnly has no filtering effect in swagger 2.0
@@ -145,6 +150,7 @@ export default class Response extends React.Component {
{ example ? ( { example ? (
<ModelExample <ModelExample
specPath={specPathWithPossibleSchema}
getComponent={ getComponent } getComponent={ getComponent }
getConfigs={ getConfigs } getConfigs={ getConfigs }
specSelectors={ specSelectors } specSelectors={ specSelectors }

View File

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

View File

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

View File

@@ -3,11 +3,12 @@ import deepExtend from "deep-extend"
import System from "core/system" import System from "core/system"
import win from "core/window" import win from "core/window"
import ApisPreset from "core/presets/apis" import ApisPreset from "core/presets/apis"
import * as AllPlugins from "core/plugins/all" import * as AllPlugins from "core/plugins/all"
import { parseSearch } from "core/utils" import { parseSearch } from "core/utils"
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
window.Perf = require("react-addons-perf") win.Perf = require("react-addons-perf")
} }
// eslint-disable-next-line no-undef // 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" import { escapeDeepLinkPath } from "core/utils"
const SCROLL_OFFSET = -5 let hasHashBeenParsed = false //TODO this forces code to only run once which may prevent scrolling if page not refreshed
let hasHashBeenParsed = false
export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) => { export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) => {
@@ -12,7 +11,6 @@ export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args)
if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") { if(!isDeepLinkingEnabled || isDeepLinkingEnabled === "false") {
return return
} }
if(window.location.hash && !hasHashBeenParsed ) { if(window.location.hash && !hasHashBeenParsed ) {
let hash = window.location.hash.slice(1) // # is first character 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 [tag, operationId] = hash.split("/")
let swaggerUI = document.querySelector(".swagger-ui")
let myScroller = zenscroll.createScroller(swaggerUI)
if(tag && operationId) { if(tag && operationId) {
// Pre-expand and scroll to the operation // Pre-expand and scroll to the operation
layoutActions.show(["operations-tag", tag], true) layoutActions.show(["operations-tag", tag], true)
layoutActions.show(["operations", tag, operationId], true) layoutActions.show(["operations", tag, operationId], true)
scrollTo(`#operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`, { let target = document.getElementById(`operations-${escapeDeepLinkPath(tag)}-${escapeDeepLinkPath(operationId)}`)
offset: SCROLL_OFFSET myScroller.to(target)
})
} else if(tag) { } else if(tag) {
// Pre-expand and scroll to the tag // Pre-expand and scroll to the tag
layoutActions.show(["operations-tag", tag], true) layoutActions.show(["operations-tag", tag], true)
scrollTo(`#operations-tag-${escapeDeepLinkPath(tag)}`, { let target = document.getElementById(`operations-tag-${escapeDeepLinkPath(tag)}`)
offset: SCROLL_OFFSET myScroller.to(target)
})
} }
} }

View File

@@ -37,17 +37,3 @@ export function changeMode(thing, mode="") {
payload: {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 { import {
UPDATE_LAYOUT, UPDATE_LAYOUT,
UPDATE_FILTER, UPDATE_FILTER,
@@ -12,9 +13,14 @@ export default {
[UPDATE_FILTER]: (state, action) => state.set("filter", action.payload), [UPDATE_FILTER]: (state, action) => state.set("filter", action.payload),
[SHOW]: (state, action) => { [SHOW]: (state, action) => {
let thing = action.payload.thing const isShown = action.payload.shown
let shown = action.payload.shown // This is one way to serialize an array, another (preferred) is to convert to json-pointer
return state.setIn(["shown"].concat(thing), shown) // 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) => { [UPDATE_MODE]: (state, action) => {
@@ -24,4 +30,3 @@ export default {
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ import {
UPDATE_OPERATION_VALUE, UPDATE_OPERATION_VALUE,
CLEAR_RESPONSE, CLEAR_RESPONSE,
CLEAR_REQUEST, CLEAR_REQUEST,
ClEAR_VALIDATE_PARAMS, CLEAR_VALIDATE_PARAMS,
SET_SCHEME SET_SCHEME
} from "./actions" } 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 state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
return parameters.withMutations( parameters => { return parameters.withMutations( parameters => {
for ( let i = 0, len = parameters.count(); i < len; i++ ) { 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 DEFAULT_TAG = "default"
const OPERATION_METHODS = ["get", "put", "post", "delete", "options", "head", "patch"]
const state = state => { const state = state => {
return state || Map() return state || Map()
} }
@@ -97,9 +95,6 @@ export const operations = createSelector(
return {} return {}
} }
path.forEach((operation, method) => { path.forEach((operation, method) => {
if(OPERATION_METHODS.indexOf(method) === -1) {
return
}
list = list.push(fromJS({ list = list.push(fromJS({
path: pathName, path: pathName,
method, method,

View File

@@ -607,7 +607,10 @@ export const getSampleSchema = (schema, contentType="", config={}) => {
export const parseSearch = () => { export const parseSearch = () => {
let map = {} let map = {}
let search = window.location.search let search = win.location.search
if(!search)
return {}
if ( search != "" ) { if ( search != "" ) {
let params = search.substr(1).split("&") 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 // 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") require("core-js/fn/promise")
} }

View File

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

View File

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

View File

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

View File

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

View File

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