Merge branch 'master' into bug/3136-operation-scheme-update

This commit is contained in:
shockey
2017-06-29 15:39:11 -07:00
committed by GitHub
19 changed files with 222 additions and 125 deletions

View File

@@ -126,9 +126,13 @@ If you'd like to use the bundle files via npm, check out the [`swagger-ui-dist`
#### Parameters #### Parameters
Parameters with dots in their names are single strings used to organize subordinate parameters, and are not indicative of a nested structure.
Parameter Name | Description Parameter Name | Description
--- | --- --- | ---
url | The url pointing to API definition (normally `swagger.json` or `swagger.yaml`). url | The url pointing to API definition (normally `swagger.json` or `swagger.yaml`). Will be ignored if `urls` or `spec` is used.
urls | An array of API definition objects (`{url: "<url>", name: "<name>"}`) used by Topbar plugin. When used and Topbar plugin is enabled, the `url` parameter will not be parsed. Names and URLs must be unique among all items in this array, since they're used as identifiers.
urls.primaryName | When using `urls`, you can use this subparameter. If the value matches the name of a spec provided in `urls`, that spec will be displayed when Swagger-UI loads, instead of defaulting to the first spec in `urls`.
spec | A JSON object describing the OpenAPI Specification. When used, the `url` parameter will not be parsed. This is useful for testing manually-generated specifications without hosting them. spec | A JSON object describing the OpenAPI Specification. When used, the `url` parameter will not be parsed. This is useful for testing manually-generated specifications without hosting them.
validatorUrl | By default, Swagger-UI attempts to validate specs against swagger.io's online validator. You can use this parameter to set a different validator URL, for example for locally deployed validators ([Validator Badge](https://github.com/swagger-api/validator-badge)). Setting it to `null` will disable validation. validatorUrl | By default, Swagger-UI attempts to validate specs against swagger.io's online validator. You can use this parameter to set a different validator URL, for example for locally deployed validators ([Validator Badge](https://github.com/swagger-api/validator-badge)). Setting it to `null` will disable validation.
dom_id | The id of a dom element inside which SwaggerUi will put the user interface for swagger. dom_id | The id of a dom element inside which SwaggerUi will put the user interface for swagger.
@@ -143,7 +147,7 @@ displayOperationId | Controls the display of operationId in operations list. The
### Plugins ### Plugins
#### Topbar plugin #### Topbar plugin
Topbar plugin enables top bar with input for spec path and explore button. By default the plugin is enabled, and to disable it you need to remove Topbar plugin from presets in `src/standalone/index.js`: Topbar plugin enables top bar with input for spec path and explore button or a dropdown if `urls` is used. By default the plugin is enabled, and to disable it you need to remove Topbar plugin from presets in `src/standalone/index.js`:
``` ```
let preset = [ let preset = [

1
dist/index.html vendored
View File

@@ -71,6 +71,7 @@
<script src="./swagger-ui-standalone-preset.js"> </script> <script src="./swagger-ui-standalone-preset.js"> </script>
<script> <script>
window.onload = function() { window.onload = function() {
// Build a system // Build a system
const ui = SwaggerUIBundle({ const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json", url: "http://petstore.swagger.io/v2/swagger.json",

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;AAitFA;;;;;;AAoIA;AAy2FA;AA8iCA;AA6lJA;AA8vIA;AA8iFA;AAi4GA;AA8jFA;AAu0FA;AA89CA;AAy9CA;AA+qCA;AAs0EA;;;;;AAw/CA;AA8zJA;;;;;;;;;;;;;;AAyoFA;AA+lIA;AA4oJA;AAqvHA;AAsvGA;AA2iEA;AAy5DA;AA65DA;AAAA;;;;;;AAk8GA;AA2nHA;;;;;AAkoDA;AAgsFA;AA6kDA;AAq3CA;AA4wFA;AAk3CA;AA8iFA;;;;;;;;;AA2qEA;AA2zIA;AAu7FA;AAmrFA;AAwiHA","sourceRoot":""} {"version":3,"file":"swagger-ui-bundle.js","sources":["webpack:///swagger-ui-bundle.js"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;AA+4CA;;;;;;AAoIA;AAy2FA;AA8iCA;AA6lJA;AA81IA;AAwuGA;AA01FA;AAkjFA;AAs3FA;AAi+CA;AA29CA;AAmtCA;AAuyEA;;;;;AAwiDA;AA8zJA;;;;;;;;;;;;;;AAyoFA;AA+lIA;AA4oJA;AAqvHA;AA8nGA;AA+iEA;AAw3DA;AA4nDA;AAknBA;;;;;;AAg1FA;AAggGA;;;;;AA23CA;AAgsFA;AA6kDA;AA01CA;AA8yFA;AA61CA;AA0jFA;;;;;;;;;AA6pEA;AA2zIA;AAu7FA;AAmrFA;AAq/EA","sourceRoot":""}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;AA+uFA;;;;;AAyOA;AAo7GA;AAw0FA;;;;;;AAmZA;AAivFA;AAu+CA;AAo+CA;AAirCA;AAuyEA","sourceRoot":""} {"version":3,"file":"swagger-ui-standalone-preset.js","sources":["webpack:///swagger-ui-standalone-preset.js"],"mappings":"AAAA;;;;;AA+tEA;AAo7GA;AAw0FA;;;;;;AAmZA;AAivFA;AAu+CA;AAo+CA;AAirCA;AAuyEA","sourceRoot":""}

18
dist/swagger-ui.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
{"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;AAylFA;;;;;;AAg5CA;AA2pHA;AA6qIA;AAi+FA;AAyvDA;AAmzCA;AA+xCA","sourceRoot":""} {"version":3,"file":"swagger-ui.js","sources":["webpack:///swagger-ui.js"],"mappings":"AAAA;AA+nEA;;;;;;AAmgBA;AA2pHA;AA0pIA;AA09FA;AA0+CA;AAs0CA;AAgvCA","sourceRoot":""}

View File

@@ -24,8 +24,8 @@ export default class Models extends Component {
return <section className={ showModels ? "models is-open" : "models"}> return <section className={ showModels ? "models is-open" : "models"}>
<h4 onClick={() => layoutActions.show("models", !showModels)}> <h4 onClick={() => layoutActions.show("models", !showModels)}>
<span>Models</span> <span>Models</span>
<svg width="20" height="20"> <svg className="arrow" width="20" height="20">
<use xlinkHref="#large-arrow" /> <use xlinkHref={showModels ? "#large-arrow-down" : "#large-arrow"} />
</svg> </svg>
</h4> </h4>
<Collapse isOpened={showModels} animated> <Collapse isOpened={showModels} animated>

View File

@@ -1,11 +1,10 @@
import React, { PropTypes } from "react" import React, { PureComponent, PropTypes } from "react"
import shallowCompare from "react-addons-shallow-compare"
import { getList } from "core/utils" import { getList } from "core/utils"
import * as CustomPropTypes from "core/proptypes" import * as CustomPropTypes from "core/proptypes"
//import "less/opblock" //import "less/opblock"
export default class Operation extends React.Component { export default class Operation extends PureComponent {
static propTypes = { static propTypes = {
path: PropTypes.string.isRequired, path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired, method: PropTypes.string.isRequired,
@@ -70,10 +69,6 @@ export default class Operation extends React.Component {
} }
} }
shouldComponentUpdate(props, state) {
return shallowCompare(this, props, state)
}
toggleShown =() => { toggleShown =() => {
let { layoutActions, isShownKey } = this.props let { layoutActions, isShownKey } = this.props
layoutActions.show(isShownKey, !this.isShown()) layoutActions.show(isShownKey, !this.isShown())

View File

@@ -1,11 +1,10 @@
import React, { Component, PropTypes } from "react" import React, { PureComponent, PropTypes } from "react"
import shallowCompare from "react-addons-shallow-compare"
import { fromJS, List } from "immutable" import { fromJS, List } from "immutable"
import { getSampleSchema } from "core/utils" import { getSampleSchema } from "core/utils"
const NOOP = Function.prototype const NOOP = Function.prototype
export default class ParamBody extends Component { export default class ParamBody extends PureComponent {
static propTypes = { static propTypes = {
param: PropTypes.object, param: PropTypes.object,
@@ -41,10 +40,6 @@ export default class ParamBody extends Component {
this.updateValues.call(this, this.props) this.updateValues.call(this, this.props)
} }
shouldComponentUpdate(props, state) {
return shallowCompare(this, props, state)
}
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
this.updateValues.call(this, nextProps) this.updateValues.call(this, nextProps)
} }

View File

@@ -5,14 +5,16 @@ import { getSampleSchema } from "core/utils"
const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => { const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
if ( examples && examples.size ) { if ( examples && examples.size ) {
return examples.entrySeq().map( ([ key, example ]) => { return examples.entrySeq().map( ([ key, example ]) => {
let exampleValue let exampleValue = example
if ( example.toJS ) {
try { try {
exampleValue = example && example.toJS ? example.toJS() : example exampleValue = JSON.stringify(example.toJS(), null, 2)
exampleValue = JSON.stringify(exampleValue, null, 2)
} }
catch(e) { catch(e) {
exampleValue = String(example) exampleValue = String(example)
} }
}
return (<div key={ key }> return (<div key={ key }>
<h5>{ key }</h5> <h5>{ key }</h5>
<HighlightCode className="example" value={ exampleValue } /> <HighlightCode className="example" value={ exampleValue } />

View File

@@ -6,7 +6,7 @@ import ApisPreset from "core/presets/apis"
import * as AllPlugins from "core/plugins/all" import * as AllPlugins from "core/plugins/all"
import { parseSeach, filterConfigs } from "core/utils" import { parseSeach, filterConfigs } from "core/utils"
const CONFIGS = [ "url", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion", const CONFIGS = [ "url", "urls", "urls.primaryName", "spec", "validatorUrl", "onComplete", "onFailure", "authorizations", "docExpansion",
"apisSorter", "operationsSorter", "supportedSubmitMethods", "dom_id", "defaultModelRendering", "oauth2RedirectUrl", "apisSorter", "operationsSorter", "supportedSubmitMethods", "dom_id", "defaultModelRendering", "oauth2RedirectUrl",
"showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" ] "showRequestHeaders", "custom", "modelPropertyMacro", "parameterMacro", "displayOperationId" ]
@@ -23,6 +23,7 @@ module.exports = function SwaggerUI(opts) {
dom_id: null, dom_id: null,
spec: {}, spec: {},
url: "", url: "",
urls: null,
layout: "BaseLayout", layout: "BaseLayout",
docExpansion: "list", docExpansion: "list",
validatorUrl: "https://online.swagger.io/validator", validatorUrl: "https://online.swagger.io/validator",

View File

@@ -1,5 +1,4 @@
import React, { PropTypes, Component } from "react" import React, { PropTypes, PureComponent, Component } from "react"
import shallowCompare from "react-addons-shallow-compare"
import { List, fromJS } from "immutable" import { List, fromJS } from "immutable"
//import "less/json-schema-form" //import "less/json-schema-form"
@@ -74,7 +73,7 @@ export class JsonSchema_string extends Component {
} }
} }
export class JsonSchema_array extends Component { export class JsonSchema_array extends PureComponent {
static propTypes = JsonSchemaPropShape static propTypes = JsonSchemaPropShape
static defaultProps = JsonSchemaDefaultProps static defaultProps = JsonSchemaDefaultProps
@@ -89,10 +88,6 @@ export class JsonSchema_array extends Component {
this.setState({value: props.value}) this.setState({value: props.value})
} }
shouldComponentUpdate(props, state) {
return shallowCompare(this, props, state)
}
onChange = () => this.props.onChange(this.state.value) onChange = () => this.props.onChange(this.state.value)
onItemChange = (itemVal, i) => { onItemChange = (itemVal, i) => {

View File

@@ -9,7 +9,7 @@ const primitives = {
"number": () => 0, "number": () => 0,
"number_float": () => 0.0, "number_float": () => 0.0,
"integer": () => 0, "integer": () => 0,
"boolean": () => true "boolean": (schema) => typeof schema.default === "boolean" ? schema.default : true
} }
const primitive = (schema) => { const primitive = (schema) => {
@@ -74,6 +74,10 @@ export const sampleFromSchema = (schema, config={}) => {
return normalizeArray(schema["enum"])[0] return normalizeArray(schema["enum"])[0]
} }
if (type === "file") {
return
}
return primitive(schema) return primitive(schema)
} }

View File

@@ -7,7 +7,7 @@ export default class Topbar extends React.Component {
constructor(props, context) { constructor(props, context) {
super(props, context) super(props, context)
this.state = { url: props.specSelectors.url() } this.state = { url: props.specSelectors.url(), selectedIndex: 0 }
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
@@ -19,14 +19,68 @@ export default class Topbar extends React.Component {
this.setState({url: value}) this.setState({url: value})
} }
downloadUrl = (e) => { loadSpec = (url) => {
this.props.specActions.updateUrl(this.state.url) this.props.specActions.updateUrl(url)
this.props.specActions.download(this.state.url) this.props.specActions.download(url)
}
onUrlSelect =(e)=> {
let url = e.target.value || e.target.href
this.loadSpec(url)
this.setSelectedUrl(url)
e.preventDefault() e.preventDefault()
} }
downloadUrl = (e) => {
this.loadSpec(this.state.url)
e.preventDefault()
}
setSelectedUrl = (selectedUrl) => {
const configs = this.props.getConfigs()
const urls = configs.urls || []
if(urls && urls.length) {
if(selectedUrl)
{
urls.forEach((spec, i) => {
if(spec.url === selectedUrl)
{
this.setState({selectedIndex: i})
}
})
}
}
}
componentWillMount() {
const configs = this.props.getConfigs()
const urls = configs.urls || []
if(urls && urls.length) {
let primaryName = configs["urls.primaryName"]
if(primaryName)
{
urls.forEach((spec, i) => {
if(spec.name === primaryName)
{
this.setState({selectedIndex: i})
}
})
}
}
}
componentDidMount() {
const urls = this.props.getConfigs().urls || []
if(urls && urls.length) {
this.loadSpec(urls[this.state.selectedIndex].url)
}
}
render() { render() {
let { getComponent, specSelectors } = this.props let { getComponent, specSelectors, getConfigs } = this.props
const Button = getComponent("Button") const Button = getComponent("Button")
const Link = getComponent("Link") const Link = getComponent("Link")
@@ -36,6 +90,31 @@ export default class Topbar extends React.Component {
let inputStyle = {} let inputStyle = {}
if(isFailed) inputStyle.color = "red" if(isFailed) inputStyle.color = "red"
if(isLoading) inputStyle.color = "#aaa" if(isLoading) inputStyle.color = "#aaa"
const { urls } = getConfigs()
let control = []
let formOnSubmit = null
if(urls) {
let rows = []
urls.forEach((link, i) => {
rows.push(<option key={i} value={link.url}>{link.name}</option>)
})
control.push(
<label className="select-label" htmlFor="select"><span>Select a spec</span>
<select id="select" disabled={isLoading} onChange={ this.onUrlSelect } value={urls[this.state.selectedIndex].url}>
{rows}
</select>
</label>
)
}
else {
formOnSubmit = this.downloadUrl
control.push(<input className="download-url-input" type="text" onChange={ this.onUrlChange } value={this.state.url} disabled={isLoading} style={inputStyle} />)
control.push(<Button className="download-url-button" onClick={ this.downloadUrl }>Explore</Button>)
}
return ( return (
<div className="topbar"> <div className="topbar">
<div className="wrapper"> <div className="wrapper">
@@ -44,14 +123,12 @@ export default class Topbar extends React.Component {
<img height="30" width="30" src={ Logo } alt="Swagger UX"/> <img height="30" width="30" src={ Logo } alt="Swagger UX"/>
<span>swagger</span> <span>swagger</span>
</Link> </Link>
<form className="download-url-wrapper" onSubmit={this.downloadUrl}> <form className="download-url-wrapper" onSubmit={formOnSubmit}>
<input className="download-url-input" type="text" onChange={ this.onUrlChange } value={this.state.url} disabled={isLoading} style={inputStyle} /> {control}
<Button className="download-url-button" onClick={ this.downloadUrl }>Explore</Button>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
) )
} }
} }
@@ -59,5 +136,6 @@ export default class Topbar extends React.Component {
Topbar.propTypes = { Topbar.propTypes = {
specSelectors: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired, specActions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired
} }

View File

@@ -1,11 +1,10 @@
.swagger-ui { .swagger-ui {
.topbar { .topbar {
background-color: #89bf04; background-color: #89bf04;
} }
.topbar-wrapper { .topbar-wrapper {
padding: 0.7em padding: 0.7em;
} }
.topbar-logo__img { .topbar-logo__img {

View File

@@ -95,14 +95,7 @@ section.models
h4 h4
{ {
margin: 0 0 5px 0; margin: 0 0 5px 0;
border-bottom: 1px solid rgba(#3b4151, .3); border-bottom: 1px solid rgba(#3b4151, .3);
svg
{
transform: rotate(90deg);
}
} }
} }
h4 h4

View File

@@ -6,7 +6,6 @@
.topbar-wrapper .topbar-wrapper
{ {
display: flex; display: flex;
align-items: center; align-items: center;
} }
a a
@@ -15,13 +14,13 @@
font-weight: bold; font-weight: bold;
display: flex; display: flex;
align-items: center;
flex: 1;
max-width: 300px; max-width: 300px;
text-decoration: none; text-decoration: none;
flex: 1;
align-items: center;
@include text_headline(#fff); @include text_headline(#fff);
span span
@@ -34,8 +33,8 @@
.download-url-wrapper .download-url-wrapper
{ {
display: flex; display: flex;
flex: 3; flex: 3;
justify-content: flex-end;
input[type=text] input[type=text]
{ {
@@ -48,6 +47,38 @@
outline: none; outline: none;
} }
.select-label
{
display: flex;
align-items: center;
width: 100%;
max-width: 600px;
margin: 0;
span
{
font-size: 16px;
flex: 1;
padding: 0 10px 0 0;
text-align: right;
}
select
{
flex: 2;
width: 100%;
border: 2px solid #547f00;
outline: none;
box-shadow: none;
}
}
.download-url-button .download-url-button
{ {
font-size: 16px; font-size: 16px;