Merge branch 'master' into bug/3405-default-scheme-change

This commit is contained in:
shockey
2017-07-21 19:13:26 -07:00
committed by GitHub
15 changed files with 443 additions and 121 deletions

1
dist/index.html vendored
View File

@@ -76,6 +76,7 @@ window.onload = function() {
const ui = SwaggerUIBundle({ const ui = SwaggerUIBundle({
url: "http://petstore.swagger.io/v2/swagger.json", url: "http://petstore.swagger.io/v2/swagger.json",
dom_id: '#swagger-ui', dom_id: '#swagger-ui',
deepLinking: true,
presets: [ presets: [
SwaggerUIBundle.presets.apis, SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset SwaggerUIStandalonePreset

View File

@@ -15,7 +15,7 @@ class Path extends React.Component {
return ( return (
<pre className="base-url"> <pre className="base-url">
[ Base url: {host}{basePath}] [ Base URL: {host}{basePath} ]
</pre> </pre>
) )
} }

View File

@@ -129,7 +129,8 @@ export class Select extends React.Component {
value: PropTypes.any, value: PropTypes.any,
onChange: PropTypes.func, onChange: PropTypes.func,
multiple: PropTypes.bool, multiple: PropTypes.bool,
allowEmptyValue: PropTypes.bool allowEmptyValue: PropTypes.bool,
className: PropTypes.string
} }
static defaultProps = { static defaultProps = {
@@ -142,7 +143,7 @@ export class Select extends React.Component {
let value let value
if (props.value !== undefined) { if (props.value) {
value = props.value value = props.value
} else { } else {
value = props.multiple ? [""] : "" value = props.multiple ? [""] : ""
@@ -178,7 +179,7 @@ export class Select extends React.Component {
let value = this.state.value.toJS ? this.state.value.toJS() : this.state.value let value = this.state.value.toJS ? this.state.value.toJS() : this.state.value
return ( return (
<select multiple={ multiple } value={ value } onChange={ this.onChange } > <select className={this.props.className} multiple={ multiple } value={ value } onChange={ this.onChange } >
{ allowEmptyValue ? <option value="">--</option> : null } { allowEmptyValue ? <option value="">--</option> : null }
{ {
allowedValues.map(function (item, key) { allowedValues.map(function (item, key) {

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}> <Collapse isOpened={showModels}>

View File

@@ -57,7 +57,8 @@ export class JsonSchema_string extends Component {
if ( enumValue ) { if ( enumValue ) {
const Select = getComponent("Select") const Select = getComponent("Select")
return (<Select allowedValues={ enumValue } return (<Select className={ errors.length ? "invalid" : ""}
allowedValues={ enumValue }
value={ value } value={ value }
allowEmptyValue={ !required } allowEmptyValue={ !required }
onChange={ this.onEnumChange }/>) onChange={ this.onEnumChange }/>)
@@ -121,6 +122,7 @@ export class JsonSchema_array extends PureComponent {
render() { render() {
let { getComponent, required, schema, fn } = this.props let { getComponent, required, schema, fn } = this.props
let errors = schema.errors || []
let itemSchema = fn.inferSchema(schema.items) let itemSchema = fn.inferSchema(schema.items)
const JsonSchemaForm = getComponent("JsonSchemaForm") const JsonSchemaForm = getComponent("JsonSchemaForm")
@@ -131,19 +133,17 @@ export class JsonSchema_array extends PureComponent {
if ( enumValue ) { if ( enumValue ) {
const Select = getComponent("Select") const Select = getComponent("Select")
return (<Select multiple={ true } return (<Select className={ errors.length ? "invalid" : ""}
multiple={ true }
value={ value } value={ value }
allowedValues={ enumValue } allowedValues={ enumValue }
allowEmptyValue={ !required } allowEmptyValue={ !required }
onChange={ this.onEnumChange }/>) onChange={ this.onEnumChange }/>)
} }
let errors = schema.errors || []
return ( return (
<div> <div>
{ !value || value.count() < 1 ? { !value || value.count() < 1 ? null :
(errors.length ? <span style={{ color: "red", fortWeight: "bold" }}>{ errors[0] }</span> : null) :
value.map( (item,i) => { value.map( (item,i) => {
let schema = Object.assign({}, itemSchema) let schema = Object.assign({}, itemSchema)
if ( errors.length ) { if ( errors.length ) {
@@ -153,12 +153,12 @@ export class JsonSchema_array extends PureComponent {
return ( return (
<div key={i} className="json-schema-form-item"> <div key={i} className="json-schema-form-item">
<JsonSchemaForm fn={fn} getComponent={getComponent} value={item} onChange={(val) => this.onItemChange(val, i)} schema={schema} /> <JsonSchemaForm fn={fn} getComponent={getComponent} value={item} onChange={(val) => this.onItemChange(val, i)} schema={schema} />
<Button className="json-schema-form-item-remove" onClick={()=> this.removeItem(i)} > - </Button> <Button className="btn btn-sm json-schema-form-item-remove" onClick={()=> this.removeItem(i)} > - </Button>
</div> </div>
) )
}).toArray() }).toArray()
} }
<Button className="json-schema-form-item-add" onClick={this.addItem}> Add item </Button> <Button className={`btn btn-sm json-schema-form-item-add ${errors.length ? "invalid" : null}`} onClick={this.addItem}> Add item </Button>
</div> </div>
) )
} }
@@ -170,12 +170,14 @@ export class JsonSchema_boolean extends Component {
onEnumChange = (val) => this.props.onChange(val) onEnumChange = (val) => this.props.onChange(val)
render() { render() {
let { getComponent, required, value } = this.props let { getComponent, value, schema } = this.props
let errors = schema.errors || []
const Select = getComponent("Select") const Select = getComponent("Select")
return (<Select value={ String(value) } return (<Select className={ errors.length ? "invalid" : ""}
value={ String(value) }
allowedValues={ fromJS(["true", "false"]) } allowedValues={ fromJS(["true", "false"]) }
allowEmptyValue={ !required } allowEmptyValue={true}
onChange={ this.onEnumChange }/>) onChange={ this.onEnumChange }/>)
} }
} }

View File

@@ -73,7 +73,7 @@ export const authorizePassword = ( auth ) => ( { authActions } ) => {
let { schema, name, username, password, passwordType, clientId, clientSecret } = auth let { schema, name, username, password, passwordType, clientId, clientSecret } = auth
let form = { let form = {
grant_type: "password", grant_type: "password",
scopes: encodeURIComponent(auth.scopes.join(scopeSeparator)) scope: encodeURIComponent(auth.scopes.join(scopeSeparator))
} }
let query = {} let query = {}
let headers = {} let headers = {}

View File

@@ -41,7 +41,7 @@ export function fromJSOrdered (js) {
return !isObject(js) ? js : return !isObject(js) ? js :
Array.isArray(js) ? Array.isArray(js) ?
Im.Seq(js).map(fromJSOrdered).toList() : Im.Seq(js).map(fromJSOrdered).toList() :
Im.Seq(js).map(fromJSOrdered).toOrderedMap() Im.OrderedMap(js).map(fromJSOrdered)
} }
export function bindToState(obj, state) { export function bindToState(obj, state) {
@@ -468,6 +468,18 @@ export const validateFile = ( val ) => {
} }
} }
export const validateBoolean = ( val ) => {
if ( !(val === "true" || val === "false" || val === true || val === false) ) {
return "Value must be a boolean"
}
}
export const validateString = ( val ) => {
if ( val && typeof val !== "string" ) {
return "Value must be a string"
}
}
// validation of parameters before execute // validation of parameters before execute
export const validateParam = (param, isXml) => { export const validateParam = (param, isXml) => {
let errors = [] let errors = []
@@ -475,52 +487,66 @@ export const validateParam = (param, isXml) => {
let required = param.get("required") let required = param.get("required")
let type = param.get("type") let type = param.get("type")
let stringCheck = type === "string" && !value // If the parameter is required OR the parameter has a value (meaning optional, but filled in)
let arrayCheck = type === "array" && Array.isArray(value) && !value.length // then we should do our validation routine
let listCheck = type === "array" && Im.List.isList(value) && !value.count() if ( required || value ) {
let fileCheck = type === "file" && !(value instanceof win.File) // These checks should evaluate to true if the parameter's value is valid
let stringCheck = type === "string" && value && !validateString(value)
let arrayCheck = type === "array" && Array.isArray(value) && value.length
let listCheck = type === "array" && Im.List.isList(value) && value.count()
let fileCheck = type === "file" && value instanceof win.File
let booleanCheck = type === "boolean" && !validateBoolean(value)
let numberCheck = type === "number" && !validateNumber(value) // validateNumber returns undefined if the value is a number
let integerCheck = type === "integer" && !validateInteger(value) // validateInteger returns undefined if the value is an integer
if ( required && (stringCheck || arrayCheck || listCheck || fileCheck) ) { if ( required && !(stringCheck || arrayCheck || listCheck || fileCheck || booleanCheck || numberCheck || integerCheck) ) {
errors.push("Required field is not provided") errors.push("Required field is not provided")
return errors return errors
} }
if ( value === null || value === undefined ) { if ( type === "string" ) {
return errors let err = validateString(value)
} if (!err) return errors
errors.push(err)
} else if ( type === "boolean" ) {
let err = validateBoolean(value)
if (!err) return errors
errors.push(err)
} else if ( type === "number" ) {
let err = validateNumber(value)
if (!err) return errors
errors.push(err)
} else if ( type === "integer" ) {
let err = validateInteger(value)
if (!err) return errors
errors.push(err)
} else if ( type === "array" ) {
let itemType
if ( type === "number" ) { if ( !value.count() ) { return errors }
let err = validateNumber(value)
if (!err) return errors
errors.push(err)
} else if ( type === "integer" ) {
let err = validateInteger(value)
if (!err) return errors
errors.push(err)
} else if ( type === "array" ) {
let itemType
if ( !value.count() ) { return errors } itemType = param.getIn(["items", "type"])
itemType = param.getIn(["items", "type"]) value.forEach((item, index) => {
let err
value.forEach((item, index) => { if (itemType === "number") {
let err err = validateNumber(item)
} else if (itemType === "integer") {
err = validateInteger(item)
} else if (itemType === "string") {
err = validateString(item)
}
if (itemType === "number") { if ( err ) {
err = validateNumber(item) errors.push({ index: index, error: err})
} else if (itemType === "integer") { }
err = validateInteger(item) })
} } else if ( type === "file" ) {
let err = validateFile(value)
if ( err ) { if (!err) return errors
errors.push({ index: index, error: err}) errors.push(err)
} }
})
} else if ( type === "file" ) {
let err = validateFile(value)
if (!err) return errors
errors.push(err)
} }
return errors return errors

View File

@@ -20,7 +20,7 @@ SwaggerUI({
}) })
``` ```
Or if you're updating the core plugins.. you'll add it to [src/js/bootstrap-plugin](https://github.com/SmartBear/swagger-ux/blob/master/src/js/bootstrap-plugin.js) Or if you're updating the core plugins.. you'll add it to the base preset: [src/core/presets/base.js](https://github.com/swagger-api/swagger-ui/blob/master/src/core/presets/base.js)
Each Plugin is a function that returns an object. That object will get merged with the `system` and later bound to the state. Each Plugin is a function that returns an object. That object will get merged with the `system` and later bound to the state.
Here is an example of each `type` Here is an example of each `type`

View File

@@ -130,7 +130,7 @@ export default class Topbar extends React.Component {
<div className="wrapper"> <div className="wrapper">
<div className="topbar-wrapper"> <div className="topbar-wrapper">
<Link href="#" title="Swagger UX"> <Link href="#" title="Swagger UX">
<img height="30" width="30" src={ Logo } alt="Swagger UX"/> <img height="30" width="30" src={ Logo } alt="Swagger UI"/>
<span>swagger</span> <span>swagger</span>
</Link> </Link>
<form className="download-url-wrapper" onSubmit={formOnSubmit}> <form className="download-url-wrapper" onSubmit={formOnSubmit}>

View File

@@ -14,6 +14,11 @@
@include text_headline(); @include text_headline();
&.btn-sm {
font-size: 12px;
padding: 4px 23px;
}
&[disabled] &[disabled]
{ {
cursor: not-allowed; cursor: not-allowed;
@@ -165,6 +170,9 @@
button button
{ {
cursor: pointer; cursor: pointer;
outline: none; outline: none;
&.invalid {
@include invalidFormElement();
}
} }

View File

@@ -21,6 +21,10 @@ select
background: #f7f7f7; background: #f7f7f7;
} }
&.invalid {
@include invalidFormElement();
}
} }
.opblock-body select .opblock-body select
@@ -53,12 +57,8 @@ input[type=file]
border-radius: 4px; border-radius: 4px;
background: #fff; background: #fff;
&.invalid &.invalid {
{ @include invalidFormElement();
animation: shake .4s 1;
border-color: $_color-delete;
background: lighten($_color-delete, 35%);
} }
} }

View File

@@ -508,6 +508,15 @@ body
{ {
margin: 0; margin: 0;
} }
a
{
@include text_code(#89bf04);
text-decoration: underline;
&:hover {
color: #81b10c;
}
}
} }
} }

View File

@@ -166,3 +166,9 @@ $browser-context: 16;
@warn 'Breakpoint mixin supports: tablet, mobile, desktop'; @warn 'Breakpoint mixin supports: tablet, mobile, desktop';
} }
} }
@mixin invalidFormElement() {
animation: shake .4s 1;
border-color: $_color-delete;
background: lighten($_color-delete, 35%);
}

View File

@@ -97,6 +97,10 @@ table
width: 100%; width: 100%;
max-width: 340px; max-width: 340px;
} }
select {
border-width: 1px;
}
} }
.parameter__name .parameter__name

View File

@@ -1,10 +1,10 @@
/* eslint-env mocha */ /* eslint-env mocha */
import expect from "expect" import expect from "expect"
import { fromJS } from "immutable" import { fromJS } from "immutable"
import { mapToList, validateNumber, validateInteger, validateParam, validateFile } from "core/utils" import { mapToList, validateNumber, validateInteger, validateParam, validateFile, fromJSOrdered } from "core/utils"
import win from "core/window" import win from "core/window"
describe("utils", function(){ describe("utils", function() {
describe("mapToList", function(){ describe("mapToList", function(){
@@ -176,6 +176,7 @@ describe("utils", function(){
let result = null let result = null
it("validates required strings", function() { it("validates required strings", function() {
// invalid string
param = fromJS({ param = fromJS({
required: true, required: true,
type: "string", type: "string",
@@ -183,9 +184,39 @@ describe("utils", function(){
}) })
result = validateParam( param, false ) result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] ) expect( result ).toEqual( ["Required field is not provided"] )
// valid string
param = fromJS({
required: true,
type: "string",
value: "test string"
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
})
it("validates optional strings", function() {
// valid (empty) string
param = fromJS({
required: false,
type: "string",
value: ""
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
// valid string
param = fromJS({
required: false,
type: "string",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}) })
it("validates required files", function() { it("validates required files", function() {
// invalid file
param = fromJS({ param = fromJS({
required: true, required: true,
type: "file", type: "file",
@@ -193,9 +224,48 @@ describe("utils", function(){
}) })
result = validateParam( param, false ) result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] ) expect( result ).toEqual( ["Required field is not provided"] )
// valid file
param = fromJS({
required: true,
type: "file",
value: new win.File()
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
})
it("validates optional files", function() {
// invalid file
param = fromJS({
required: false,
type: "file",
value: "not a file"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be a file"] )
// valid (empty) file
param = fromJS({
required: false,
type: "file",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
// valid file
param = fromJS({
required: false,
type: "file",
value: new win.File()
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}) })
it("validates required arrays", function() { it("validates required arrays", function() {
// invalid (empty) array
param = fromJS({ param = fromJS({
required: true, required: true,
type: "array", type: "array",
@@ -204,75 +274,51 @@ describe("utils", function(){
result = validateParam( param, false ) result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] ) expect( result ).toEqual( ["Required field is not provided"] )
// invalid (not an array)
param = fromJS({ param = fromJS({
required: true, required: true,
type: "array", type: "array",
value: [] value: undefined
}) })
result = validateParam( param, false ) result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] ) expect( result ).toEqual( ["Required field is not provided"] )
})
it("validates numbers", function() { // invalid array, items do not match correct type
// string instead of a number
param = fromJS({ param = fromJS({
required: false, required: true,
type: "number", type: "array",
value: "test" value: [1],
items: {
type: "string"
}
}) })
result = validateParam( param, false ) result = validateParam( param, false )
expect( result ).toEqual( ["Value must be a number"] ) expect( result ).toEqual( [{index: 0, error: "Value must be a string"}] )
// undefined value // valid array, with no 'type' for items
param = fromJS({ param = fromJS({
required: false, required: true,
type: "number", type: "array",
value: undefined value: ["1"]
}) })
result = validateParam( param, false ) result = validateParam( param, false )
expect( result ).toEqual( [] ) expect( result ).toEqual( [] )
// null value // valid array, items match type
param = fromJS({ param = fromJS({
required: false, required: true,
type: "number", type: "array",
value: null value: ["1"],
items: {
type: "string"
}
}) })
result = validateParam( param, false ) result = validateParam( param, false )
expect( result ).toEqual( [] ) expect( result ).toEqual( [] )
}) })
it("validates integers", function() { it("validates optional arrays", function() {
// string instead of integer // valid, empty array
param = fromJS({
required: false,
type: "integer",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be an integer"] )
// undefined value
param = fromJS({
required: false,
type: "integer",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
// null value
param = fromJS({
required: false,
type: "integer",
value: null
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
})
it("validates arrays", function() {
// empty array
param = fromJS({ param = fromJS({
required: false, required: false,
type: "array", type: "array",
@@ -281,7 +327,7 @@ describe("utils", function(){
result = validateParam( param, false ) result = validateParam( param, false )
expect( result ).toEqual( [] ) expect( result ).toEqual( [] )
// numbers // invalid, items do not match correct type
param = fromJS({ param = fromJS({
required: false, required: false,
type: "array", type: "array",
@@ -293,17 +339,236 @@ describe("utils", function(){
result = validateParam( param, false ) result = validateParam( param, false )
expect( result ).toEqual( [{index: 0, error: "Value must be a number"}] ) expect( result ).toEqual( [{index: 0, error: "Value must be a number"}] )
// integers // valid
param = fromJS({ param = fromJS({
required: false, required: false,
type: "array", type: "array",
value: ["not", "numbers"], value: ["test"],
items: { items: {
type: "integer" type: "string"
} }
}) })
result = validateParam( param, false ) result = validateParam( param, false )
expect( result ).toEqual( [{index: 0, error: "Value must be an integer"}, {index: 1, error: "Value must be an integer"}] ) expect( result ).toEqual( [] )
})
it("validates required booleans", function() {
// invalid boolean value
param = fromJS({
required: true,
type: "boolean",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
// invalid boolean value (not a boolean)
param = fromJS({
required: true,
type: "boolean",
value: "test string"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
// valid boolean value
param = fromJS({
required: true,
type: "boolean",
value: "true"
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
// valid boolean value
param = fromJS({
required: true,
type: "boolean",
value: false
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
})
it("validates optional booleans", function() {
// valid (empty) boolean value
param = fromJS({
required: false,
type: "boolean",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
// invalid boolean value (not a boolean)
param = fromJS({
required: false,
type: "boolean",
value: "test string"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be a boolean"] )
// valid boolean value
param = fromJS({
required: false,
type: "boolean",
value: "true"
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
// valid boolean value
param = fromJS({
required: false,
type: "boolean",
value: false
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
})
it("validates required numbers", function() {
// invalid number, string instead of a number
param = fromJS({
required: true,
type: "number",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
// invalid number, undefined value
param = fromJS({
required: true,
type: "number",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
// valid number
param = fromJS({
required: true,
type: "number",
value: 10
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
})
it("validates optional numbers", function() {
// invalid number, string instead of a number
param = fromJS({
required: false,
type: "number",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be a number"] )
// valid (empty) number
param = fromJS({
required: false,
type: "number",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
// valid number
param = fromJS({
required: false,
type: "number",
value: 10
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
})
it("validates required integers", function() {
// invalid integer, string instead of an integer
param = fromJS({
required: true,
type: "integer",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
// invalid integer, undefined value
param = fromJS({
required: true,
type: "integer",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( ["Required field is not provided"] )
// valid integer
param = fromJS({
required: true,
type: "integer",
value: 10
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
})
it("validates optional integers", function() {
// invalid integer, string instead of an integer
param = fromJS({
required: false,
type: "integer",
value: "test"
})
result = validateParam( param, false )
expect( result ).toEqual( ["Value must be an integer"] )
// valid (empty) integer
param = fromJS({
required: false,
type: "integer",
value: undefined
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
// valid number
param = fromJS({
required: false,
type: "integer",
value: 10
})
result = validateParam( param, false )
expect( result ).toEqual( [] )
}) })
}) })
describe("fromJSOrdered", () => {
it("should create an OrderedMap from an object", () => {
const param = {
value: "test"
}
const result = fromJSOrdered(param).toJS()
expect( result ).toEqual( { value: "test" } )
})
it("should not use an object's length property for Map size", () => {
const param = {
length: 5
}
const result = fromJSOrdered(param).toJS()
expect( result ).toEqual( { length: 5 } )
})
it("should create an OrderedMap from an array", () => {
const param = [1, 1, 2, 3, 5, 8]
const result = fromJSOrdered(param).toJS()
expect( result ).toEqual( [1, 1, 2, 3, 5, 8] )
})
})
}) })