Merge branch 'master' of github.com:swagger-api/swagger-ui into feature/small-tweaks

This commit is contained in:
Kyle Shockey
2017-12-15 17:22:03 -08:00
126 changed files with 4107 additions and 1028 deletions

View File

@@ -7,15 +7,17 @@ export default class ArrayModel extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
required: PropTypes.bool,
expandDepth: PropTypes.number,
specPath: PropTypes.array.isRequired,
depth: PropTypes.number
}
render(){
let { getComponent, schema, depth, expandDepth, name } = this.props
let { getComponent, getConfigs, schema, depth, expandDepth, name, specPath } = this.props
let description = schema.get("description")
let items = schema.get("items")
let title = schema.get("title") || name
@@ -24,30 +26,29 @@ export default class ArrayModel extends Component {
const Markdown = getComponent("Markdown")
const ModelCollapse = getComponent("ModelCollapse")
const Model = getComponent("Model")
const Property = getComponent("Property")
const titleEl = title &&
<span className="model-title">
<span className="model-title__text">{ title }</span>
</span>
/*
/*
Note: we set `name={null}` in <Model> below because we don't want
the name of the current Model passed (and displayed) as the name of the array element 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 ] ) => <span key={`${key}-${v}`} style={ propStyle }>
<br />{ key }: { String(v) }</span>)
: null
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
}
{
!description ? null :
<Markdown source={ description } />
}
<span><Model { ...this.props } name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
<span><Model { ...this.props } getConfigs={ getConfigs } specPath={[...specPath, "items"]} name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
]
</ModelCollapse>
</span>

View File

@@ -60,6 +60,9 @@ export default class ApiKeyAuth extends React.Component {
<Row>
<Markdown source={ schema.get("description") } />
</Row>
<Row>
<p>Name: <code>{ schema.get("name") }</code></p>
</Row>
<Row>
<p>In: <code>{ schema.get("in") }</code></p>
</Row>

View File

@@ -1,25 +1,23 @@
import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
export default class AuthorizeOperationBtn extends React.Component {
static propTypes = {
isAuthorized: PropTypes.bool.isRequired,
onClick: PropTypes.func
}
onClick =(e) => {
e.stopPropagation()
let { onClick } = this.props
let { security, authActions, authSelectors } = this.props
let definitions = authSelectors.getDefinitionsByNames(security)
authActions.showDefinitions(definitions)
if(onClick) {
onClick()
}
}
render() {
let { security, authSelectors } = this.props
let isAuthorized = authSelectors.isAuthorized(security)
if(isAuthorized === null) {
return null
}
let { isAuthorized } = this.props
return (
<button className={isAuthorized ? "authorization__btn locked" : "authorization__btn unlocked"} onClick={ this.onClick }>
@@ -30,10 +28,4 @@ export default class AuthorizeOperationBtn extends React.Component {
)
}
static propTypes = {
authSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
security: ImPropTypes.iterable.isRequired
}
}

View File

@@ -41,6 +41,13 @@ export default class Auths extends React.Component {
authActions.logout(auths)
}
close =(e) => {
e.preventDefault()
let { authActions } = this.props
authActions.showDefinitions(false)
}
render() {
let { definitions, getComponent, authSelectors, errSelectors } = this.props
const AuthItem = getComponent("AuthItem")
@@ -74,6 +81,7 @@ export default class Auths extends React.Component {
}).toArray()
}
<div className="auth-btn-wrapper">
<Button className="btn modal-btn auth btn-done" onClick={ this.close }>Done</Button>
{
nonOauthDefinitions.size === authorizedAuth.size ? <Button className="btn modal-btn auth" onClick={ this.logoutClick }>Logout</Button>
: <Button type="submit" className="btn modal-btn auth authorize">Authorize</Button>

View File

@@ -200,11 +200,11 @@ export default class Oauth2 extends React.Component {
<Row key={ name }>
<div className="checkbox">
<Input data-value={ name }
id={`${name}-checkbox-${this.state.name}`}
id={`${name}-${flow}-checkbox-${this.state.name}`}
disabled={ isAuthorized }
type="checkbox"
onChange={ this.onScopeChange }/>
<label htmlFor={`${name}-checkbox-${this.state.name}`}>
<label htmlFor={`${name}-${flow}-checkbox-${this.state.name}`}>
<span className="item"></span>
<div className="text">
<p className="name">{name}</p>

View File

@@ -27,6 +27,16 @@ export default class ContentType extends React.Component {
}
}
componentWillReceiveProps(nextProps) {
if(!nextProps.contentTypes || !nextProps.contentTypes.size) {
return
}
if(!nextProps.contentTypes.includes(nextProps.value)) {
nextProps.onChange(nextProps.contentTypes.first())
}
}
onChangeWrapper = e => this.props.onChange(e.target.value)
render() {
@@ -37,7 +47,7 @@ export default class ContentType extends React.Component {
return (
<div className={ "content-type-wrapper " + ( className || "" ) }>
<select className="content-type" value={value} onChange={this.onChangeWrapper} >
<select className="content-type" value={value || ""} onChange={this.onChangeWrapper} >
{ contentTypes.map( (val) => {
return <option key={ val } value={ val }>{ val }</option>
}).toArray()}

View File

@@ -1,6 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import Collapse from "react-collapse"
import { Collapse } from "react-collapse"
import { presets } from "react-motion"
import ObjectInspector from "react-object-inspector"
import Perf from "react-addons-perf"

View File

@@ -0,0 +1,20 @@
import React from "react"
import PropTypes from "prop-types"
export const DeepLink = ({ enabled, path, text }) => {
return (
<a className="nostyle"
onClick={enabled ? (e) => e.preventDefault() : null}
href={enabled ? `#/${path}` : null}>
<span>{text}</span>
</a>
)
}
DeepLink.propTypes = {
enabled: PropTypes.bool,
isShown: PropTypes.bool,
path: PropTypes.string,
text: PropTypes.string
}
export default DeepLink

View File

@@ -1,7 +1,7 @@
import React from "react"
import PropTypes from "prop-types"
import { List } from "immutable"
import Collapse from "react-collapse"
import { Collapse } from "react-collapse"
export default class Errors extends React.Component {
@@ -73,7 +73,7 @@ const ThrownErrorItem = ( { error, jumpToLine } ) => {
<span style={{ whiteSpace: "pre-line", "maxWidth": "100%" }}>
{ error.get("message") }
</span>
<div>
<div style={{ "text-decoration": "underline", "cursor": "pointer" }}>
{ errorLine && jumpToLine ? <a onClick={jumpToLine.bind(null, errorLine)}>Jump to line { errorLine }</a> : null }
</div>
</div>
@@ -113,7 +113,7 @@ const SpecErrorItem = ( { error, jumpToLine } ) => {
}
function toTitleCase(str) {
return str
return (str || "")
.split(" ")
.map(substr => substr[0].toUpperCase() + substr.slice(1))
.join(" ")

View File

@@ -8,7 +8,6 @@ export default class Execute extends Component {
specActions: PropTypes.object.isRequired,
operation: PropTypes.object.isRequired,
path: PropTypes.string.isRequired,
getComponent: PropTypes.func.isRequired,
method: PropTypes.string.isRequired,
onExecute: PropTypes.func
}

View File

@@ -2,20 +2,24 @@ import React from "react"
import PropTypes from "prop-types"
import Im from "immutable"
const propStyle = { color: "#999", fontStyle: "italic" }
export default class Headers extends React.Component {
static propTypes = {
headers: PropTypes.object.isRequired
headers: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired
};
render() {
let { headers } = this.props
let { headers, getComponent } = this.props
const Property = getComponent("Property")
if ( !headers || !headers.size )
return null
return (
return (
<div className="headers-wrapper">
<h4 className="headers__title">Headers:</h4>
<table className="headers">
@@ -32,10 +36,13 @@ export default class Headers extends React.Component {
if(!Im.Map.isMap(header)) {
return null
}
const type = header.getIn(["schema"]) ? header.getIn(["schema", "type"]) : header.getIn(["type"])
const schemaExample = header.getIn(["schema", "example"])
return (<tr key={ key }>
<td className="header-col">{ key }</td>
<td className="header-col">{ header.get( "description" ) }</td>
<td className="header-col">{ header.get( "type" ) }</td>
<td className="header-col">{ type } { schemaExample ? <Property propKey={ "Example" } propVal={ schemaExample } propStyle={ propStyle } /> : null }</td>
</tr>)
}).toArray()
}

View File

@@ -2,6 +2,7 @@ import React from "react"
import PropTypes from "prop-types"
import { fromJS } from "immutable"
import ImPropTypes from "react-immutable-proptypes"
import { sanitizeUrl } from "core/utils"
class Path extends React.Component {
@@ -35,9 +36,9 @@ class Contact extends React.Component {
return (
<div>
{ url && <div><a href={ url } target="_blank">{ name } - Website</a></div> }
{ url && <div><a href={ sanitizeUrl(url) } target="_blank">{ name } - Website</a></div> }
{ email &&
<a href={`mailto:${email}`}>
<a href={sanitizeUrl(`mailto:${email}`)}>
{ url ? `Send email to ${name}` : `Contact ${name}`}
</a>
}
@@ -59,7 +60,7 @@ class License extends React.Component {
return (
<div>
{
url ? <a target="_blank" href={ url }>{ name }</a>
url ? <a target="_blank" href={ sanitizeUrl(url) }>{ name }</a>
: <span>{ name }</span>
}
</div>
@@ -97,7 +98,7 @@ export default class Info extends React.Component {
{ version && <VersionStamp version={version}></VersionStamp> }
</h2>
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
{ url && <a target="_blank" href={ url }><span className="url"> { url } </span></a> }
{ url && <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a> }
</hgroup>
<div className="description">
@@ -106,14 +107,14 @@ export default class Info extends React.Component {
{
termsOfService && <div>
<a target="_blank" href={ termsOfService }>Terms of service</a>
<a target="_blank" href={ sanitizeUrl(termsOfService) }>Terms of service</a>
</div>
}
{ contact && contact.size ? <Contact data={ contact } /> : null }
{ license && license.size ? <License license={ license } /> : null }
{ externalDocsUrl ?
<a target="_blank" href={externalDocsUrl}>{externalDocsDescription || externalDocsUrl}</a>
<a target="_blank" href={sanitizeUrl(externalDocsUrl)}>{externalDocsDescription || externalDocsUrl}</a>
: null }
</div>

View File

@@ -1,6 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import OriCollapse from "react-collapse"
import { Collapse as OriCollapse } from "react-collapse"
function xclass(...args) {
return args.filter(a => !!a).join(" ").trim()
@@ -183,7 +183,7 @@ export class Select extends React.Component {
{ allowEmptyValue ? <option value="">--</option> : null }
{
allowedValues.map(function (item, key) {
return <option key={ key } value={ String(item) }>{ item }</option>
return <option key={ key } value={ String(item) }>{ String(item) }</option>
})
}
</select>

View File

@@ -61,7 +61,18 @@ export default class BaseLayout extends React.Component {
const isSpecEmpty = !specSelectors.specStr()
if(isSpecEmpty) {
return <h4>No spec provided.</h4>
let loadingMessage
if(isLoading) {
loadingMessage = <div className="loading"></div>
} else {
loadingMessage = <h4>No API definition provided.</h4>
}
return <div className="swagger-ui">
<div className="loading-container">
{loadingMessage}
</div>
</div>
}
return (
@@ -94,8 +105,9 @@ export default class BaseLayout extends React.Component {
) : null }
{ servers && servers.size ? (
<div className="server-container">
<div className="global-server-container">
<Col className="servers wrapper" mobile={12}>
<span className="servers-title">Server</span>
<Servers
servers={servers}
currentServer={oas3Selectors.selectedServer()}

View File

@@ -1,6 +1,7 @@
import React from "react"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import { Iterable } from "immutable"
const Headers = ( { headers } )=>{
return (
@@ -28,19 +29,29 @@ Duration.propTypes = {
export default class LiveResponse extends React.Component {
static propTypes = {
response: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
pathMethod: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
response: PropTypes.instanceOf(Iterable).isRequired,
path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
displayRequestDuration: PropTypes.bool.isRequired,
specSelectors: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired
}
shouldComponentUpdate(nextProps) {
// BUG: props.response is always coming back as a new Immutable instance
// same issue as responses.jsx (tryItOutResponse)
return this.props.response !== nextProps.response
|| this.props.path !== nextProps.path
|| this.props.method !== nextProps.method
|| this.props.displayRequestDuration !== nextProps.displayRequestDuration
}
render() {
const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, pathMethod } = this.props
const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, path, method } = this.props
const { showMutatedRequest } = getConfigs()
const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(pathMethod[0], pathMethod[1]) : specSelectors.requestFor(pathMethod[0], pathMethod[1])
const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(path, method) : specSelectors.requestFor(path, method)
const status = response.get("status")
const url = response.get("url")
const headers = response.get("headers").toJS()
@@ -118,7 +129,6 @@ export default class LiveResponse extends React.Component {
static propTypes = {
getComponent: PropTypes.func.isRequired,
request: ImPropTypes.map,
response: ImPropTypes.map
}
}

View File

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

View File

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

View File

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

View File

@@ -1,16 +1,19 @@
import React, { Component } from "react"
import React, { PureComponent } from "react"
import ImPropTypes from "react-immutable-proptypes"
import PropTypes from "prop-types"
export default class Model extends Component {
export default class Model extends PureComponent {
static propTypes = {
schema: PropTypes.object.isRequired,
schema: ImPropTypes.orderedMap.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
isRef: PropTypes.bool,
required: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number
depth: PropTypes.number,
specPath: PropTypes.array.isRequired,
}
getModelName =( ref )=> {
@@ -29,13 +32,13 @@ export default class Model extends Component {
}
render () {
let { getComponent, specSelectors, schema, required, name, isRef } = this.props
let { getComponent, getConfigs, specSelectors, schema, required, name, isRef, specPath } = this.props
const ObjectModel = getComponent("ObjectModel")
const ArrayModel = getComponent("ArrayModel")
const PrimitiveModel = getComponent("PrimitiveModel")
let type = "object"
let $$ref = schema && schema.get("$$ref")
// If we weren't passed a `name` but have a ref, grab the name from the ref
if ( !name && $$ref ) {
name = this.getModelName( $$ref )
@@ -44,15 +47,17 @@ export default class Model extends Component {
if ( !schema && $$ref ) {
schema = this.getRefSchema( name )
}
const deprecated = specSelectors.isOAS3() && schema.get("deprecated")
isRef = isRef !== undefined ? isRef : !!$$ref
type = schema && schema.get("type") || type
switch(type) {
case "object":
return <ObjectModel
className="object" { ...this.props }
specPath={specPath}
getConfigs={ getConfigs }
schema={ schema }
name={ name }
deprecated={deprecated}
@@ -60,6 +65,7 @@ export default class Model extends Component {
case "array":
return <ArrayModel
className="array" { ...this.props }
getConfigs={ getConfigs }
schema={ schema }
name={ name }
deprecated={deprecated}
@@ -72,6 +78,7 @@ export default class Model extends Component {
return <PrimitiveModel
{ ...this.props }
getComponent={ getComponent }
getConfigs={ getConfigs }
schema={ schema }
name={ name }
deprecated={deprecated}

View File

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

View File

@@ -9,17 +9,27 @@ export default class ObjectModel extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
expanded: PropTypes.bool,
onToggle: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
isRef: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number
depth: PropTypes.number,
specPath: PropTypes.object.isRequired
}
render(){
let { schema, name, isRef, getComponent, depth, expandDepth, ...otherProps } = this.props
let { specSelectors } = otherProps
let { isOAS3 } = specSelectors
let { schema, name, isRef, getComponent, getConfigs, depth, onToggle, expanded, specPath, ...otherProps } = this.props
let { specSelectors,expandDepth } = otherProps
const { isOAS3 } = specSelectors
if(!schema) {
return null
}
const { showExtensions } = getConfigs()
let description = schema.get("description")
let properties = schema.get("properties")
@@ -32,14 +42,13 @@ export default class ObjectModel extends Component {
const Model = getComponent("Model")
const ModelCollapse = getComponent("ModelCollapse")
const JumpToPathSection = ({ name }) => {
const path = isOAS3 && isOAS3() ? `components.schemas.${name}` : `definitions.${name}`
return <span className="model-jump-to-path"><JumpToPath path={path} /></span>
const JumpToPathSection = () => {
return <span className="model-jump-to-path"><JumpToPath specPath={specPath} /></span>
}
const collapsedContent = (<span>
<span>{ braceOpen }</span>...<span>{ braceClose }</span>
{
isRef ? <JumpToPathSection name={ name }/> : ""
isRef ? <JumpToPathSection /> : ""
}
</span>)
@@ -53,10 +62,16 @@ export default class ObjectModel extends Component {
</span>
return <span className="model">
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent={ collapsedContent }>
<ModelCollapse
modelName={name}
title={titleEl}
onToggle = {onToggle}
expanded={ expanded ? true : depth <= expandDepth }
collapsedContent={ collapsedContent }>
<span className="brace-open object">{ braceOpen }</span>
{
!isRef ? null : <JumpToPathSection name={ name }/>
!isRef ? null : <JumpToPathSection />
}
<span className="inner-object">
{
@@ -72,13 +87,14 @@ export default class ObjectModel extends Component {
{
!(properties && properties.size) ? null : properties.entrySeq().map(
([key, value]) => {
let isDeprecated = isOAS3() && value.get("deprecated")
let isRequired = List.isList(requiredProperties) && requiredProperties.contains(key)
let propertyStyle = { verticalAlign: "top", paddingRight: "0.2em" }
if ( isRequired ) {
propertyStyle.fontWeight = "bold"
}
return (<tr key={key}>
return (<tr key={key} className={isDeprecated && "deprecated"}>
<td style={ propertyStyle }>
{ key }{ isRequired && <span style={{ color: "red" }}>*</span> }
</td>
@@ -86,12 +102,38 @@ export default class ObjectModel extends Component {
<Model key={ `object-${name}-${key}_${value}` } { ...otherProps }
required={ isRequired }
getComponent={ getComponent }
specPath={[...specPath, "properties", key]}
getConfigs={ getConfigs }
schema={ value }
depth={ depth + 1 } />
</td>
</tr>)
}).toArray()
}
{
// empty row befor extensions...
!showExtensions ? null : <tr>&nbsp;</tr>
}
{
!showExtensions ? null :
schema.entrySeq().map(
([key, value]) => {
if(key.slice(0,2) !== "x-") {
return
}
const normalizedValue = !value ? null : value.toJS ? value.toJS() : value
return (<tr key={key} style={{ color: "#777" }}>
<td>
{ key }
</td>
<td style={{ verticalAlign: "top" }}>
{ JSON.stringify(normalizedValue) }
</td>
</tr>)
}).toArray()
}
{
!additionalProperties || !additionalProperties.size ? null
: <tr>
@@ -99,6 +141,8 @@ export default class ObjectModel extends Component {
<td>
<Model { ...otherProps } required={ false }
getComponent={ getComponent }
specPath={[...specPath, "additionalProperties"]}
getConfigs={ getConfigs }
schema={ additionalProperties }
depth={ depth + 1 } />
</td>
@@ -112,6 +156,8 @@ export default class ObjectModel extends Component {
{anyOf.map((schema, k) => {
return <div key={k}><Model { ...otherProps } required={ false }
getComponent={ getComponent }
specPath={[...specPath, "anyOf", k]}
getConfigs={ getConfigs }
schema={ schema }
depth={ depth + 1 } /></div>
})}
@@ -126,6 +172,8 @@ export default class ObjectModel extends Component {
{oneOf.map((schema, k) => {
return <div key={k}><Model { ...otherProps } required={ false }
getComponent={ getComponent }
specPath={[...specPath, "oneOf", k]}
getConfigs={ getConfigs }
schema={ schema }
depth={ depth + 1 } /></div>
})}
@@ -137,12 +185,15 @@ export default class ObjectModel extends Component {
: <tr>
<td>{ "not ->" }</td>
<td>
{not.map((schema, k) => {
return <div key={k}><Model { ...otherProps } required={ false }
getComponent={ getComponent }
schema={ schema }
depth={ depth + 1 } /></div>
})}
<div>
<Model { ...otherProps }
required={ false }
getComponent={ getComponent }
specPath={[...specPath, "not"]}
getConfigs={ getConfigs }
schema={ not }
depth={ depth + 1 } />
</div>
</td>
</tr>
}

View File

@@ -1,5 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import { sanitizeUrl } from "core/utils"
export default class OnlineValidatorBadge extends React.Component {
static propTypes = {
@@ -32,6 +33,8 @@ export default class OnlineValidatorBadge extends React.Component {
let { getConfigs } = this.props
let { spec } = getConfigs()
let sanitizedValidatorUrl = sanitizeUrl(this.state.validatorUrl)
if ( typeof spec === "object" && Object.keys(spec).length) return null
if (!this.state.url || !this.state.validatorUrl || this.state.url.indexOf("localhost") >= 0
@@ -40,8 +43,8 @@ export default class OnlineValidatorBadge extends React.Component {
}
return (<span style={{ float: "right"}}>
<a target="_blank" href={`${ this.state.validatorUrl }/debug?url=${ this.state.url }`}>
<ValidatorImage src={`${ this.state.validatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
<a target="_blank" href={`${ sanitizedValidatorUrl }/debug?url=${ this.state.url }`}>
<ValidatorImage src={`${ sanitizedValidatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
</a>
</span>)
}

View File

@@ -0,0 +1,17 @@
import React from "react"
import PropTypes from "prop-types"
export const OperationExtRow = ({ xKey, xVal }) => {
const xNormalizedValue = !xVal ? null : xVal.toJS ? xVal.toJS() : xVal
return (<tr>
<td>{ xKey }</td>
<td>{ JSON.stringify(xNormalizedValue) }</td>
</tr>)
}
OperationExtRow.propTypes = {
xKey: PropTypes.string,
xVal: PropTypes.any
}
export default OperationExtRow

View File

@@ -0,0 +1,35 @@
import React from "react"
import PropTypes from "prop-types"
export const OperationExt = ({ extensions, getComponent }) => {
let OperationExtRow = getComponent("OperationExtRow")
return (
<div className="opblock-section">
<div className="opblock-section-header">
<h4>Extensions</h4>
</div>
<div className="table-container">
<table>
<thead>
<tr>
<td className="col col_header">Field</td>
<td className="col col_header">Value</td>
</tr>
</thead>
<tbody>
{
extensions.entrySeq().map(([k, v]) => <OperationExtRow key={`${k}-${v}`} xKey={k} xVal={v} />)
}
</tbody>
</table>
</div>
</div>
)
}
OperationExt.propTypes = {
extensions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired
}
export default OperationExt

View File

@@ -1,138 +1,95 @@
import React, { PureComponent } from "react"
import PropTypes from "prop-types"
import { getList } from "core/utils"
import * as CustomPropTypes from "core/proptypes"
//import "less/opblock"
import { getExtensions, sanitizeUrl } from "core/utils"
import { Iterable } from "immutable"
export default class Operation extends PureComponent {
static propTypes = {
path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
operation: PropTypes.object.isRequired,
showSummary: PropTypes.bool,
specPath: PropTypes.array.isRequired,
operation: PropTypes.instanceOf(Iterable).isRequired,
response: PropTypes.instanceOf(Iterable),
request: PropTypes.instanceOf(Iterable),
isShownKey: CustomPropTypes.arrayOrString.isRequired,
jumpToKey: CustomPropTypes.arrayOrString.isRequired,
allowTryItOut: PropTypes.bool,
displayOperationId: PropTypes.bool,
displayRequestDuration: PropTypes.bool,
response: PropTypes.object,
request: PropTypes.object,
toggleShown: PropTypes.func.isRequired,
onTryoutClick: PropTypes.func.isRequired,
onCancelClick: PropTypes.func.isRequired,
onExecute: PropTypes.func.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
authActions: PropTypes.object,
authSelectors: PropTypes.object,
specActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
oas3Selectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired,
getConfigs: PropTypes.func.isRequired
fn: PropTypes.object.isRequired
}
static defaultProps = {
showSummary: true,
operation: null,
response: null,
allowTryItOut: true,
displayOperationId: false,
displayRequestDuration: false
}
constructor(props, context) {
super(props, context)
this.state = {
tryItOutEnabled: false
}
}
componentWillReceiveProps(nextProps) {
const defaultContentType = "application/json"
let { specActions, path, method, operation } = nextProps
let producesValue = operation.get("produces_value")
let produces = operation.get("produces")
let consumes = operation.get("consumes")
let consumesValue = operation.get("consumes_value")
if(nextProps.response !== this.props.response) {
this.setState({ executeInProgress: false })
}
if (producesValue === undefined) {
producesValue = produces && produces.size ? produces.first() : defaultContentType
specActions.changeProducesValue([path, method], producesValue)
}
if (consumesValue === undefined) {
consumesValue = consumes && consumes.size ? consumes.first() : defaultContentType
specActions.changeConsumesValue([path, method], consumesValue)
}
}
toggleShown =() => {
let { layoutActions, isShownKey } = this.props
layoutActions.show(isShownKey, !this.isShown())
}
isShown =() => {
let { layoutSelectors, isShownKey, getConfigs } = this.props
let { docExpansion } = getConfigs()
return layoutSelectors.isShown(isShownKey, docExpansion === "full" ) // Here is where we set the default
}
onTryoutClick =() => {
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
}
onCancelClick =() => {
let { specActions, path, method } = this.props
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
specActions.clearValidateParams([path, method])
}
onExecute = () => {
this.setState({ executeInProgress: true })
request: null,
specPath: []
}
render() {
let {
isShownKey,
jumpToKey,
path,
method,
operation,
showSummary,
specPath,
response,
request,
allowTryItOut,
displayOperationId,
displayRequestDuration,
toggleShown,
onTryoutClick,
onCancelClick,
onExecute,
fn,
getComponent,
getConfigs,
specActions,
specSelectors,
authActions,
authSelectors,
getConfigs,
oas3Actions
oas3Actions,
oas3Selectors
} = this.props
let operationProps = this.props.operation
let summary = operation.get("summary")
let description = operation.get("description")
let deprecated = operation.get("deprecated")
let externalDocs = operation.get("externalDocs")
let {
isShown,
isAuthorized,
path,
method,
op,
tag,
showSummary,
operationId,
allowTryItOut,
displayOperationId,
displayRequestDuration,
isDeepLinkingEnabled,
tryItOutEnabled,
executeInProgress
} = operationProps.toJS()
let {
summary,
description,
deprecated,
externalDocs,
schemes
} = op.operation
let operation = operationProps.getIn(["op", "operation"])
let security = operationProps.get("security")
let responses = operation.get("responses")
let security = operation.get("security") || specSelectors.security()
let produces = operation.get("produces")
let schemes = operation.get("schemes")
let parameters = getList(operation, ["parameters"])
let operationId = operation.get("__originalOperationId")
let operationScheme = specSelectors.operationScheme(path, method)
let isShownKey = ["operations", tag, operationId]
let extensions = getExtensions(operation)
const Responses = getComponent("responses")
const Parameters = getComponent( "parameters" )
@@ -143,33 +100,33 @@ export default class Operation extends PureComponent {
const Collapse = getComponent( "Collapse" )
const Markdown = getComponent( "Markdown" )
const Schemes = getComponent( "schemes" )
const OperationServers = getComponent( "OperationServers" )
const OperationExt = getComponent( "OperationExt" )
const DeepLink = getComponent( "DeepLink" )
const { deepLinking } = getConfigs()
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
const { showExtensions } = getConfigs()
// Merge in Live Response
if(responses && response && response.size > 0) {
let notDocumented = !responses.get(String(response.get("status")))
let notDocumented = !responses.get(String(response.get("status"))) && !responses.get("default")
response = response.set("notDocumented", notDocumented)
}
let { tryItOutEnabled } = this.state
let shown = this.isShown()
let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )
return (
<div className={deprecated ? "opblock opblock-deprecated" : shown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
<div className={`opblock-summary opblock-summary-${method}`} onClick={this.toggleShown} >
<div className={deprecated ? "opblock opblock-deprecated" : isShown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
<div className={`opblock-summary opblock-summary-${method}`} onClick={toggleShown} >
{/*TODO: convert this into a component, that can be wrapped
and pulled in with getComponent */}
<span className="opblock-summary-method">{method.toUpperCase()}</span>
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
<a
className="nostyle"
onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
href={isDeepLinkingEnabled ? `#/${isShownKey[1]}/${isShownKey[2]}` : null}>
<span>{path}</span>
</a>
<JumpToPath path={jumpToKey} />
<DeepLink
enabled={isDeepLinkingEnabled}
isShown={isShown}
path={`${isShownKey.join("/")}`}
text={path} />
<JumpToPath path={specPath} /> {/*TODO: use wrapComponents here, swagger-ui doesn't care about jumpToPath */}
</span>
{ !showSummary ? null :
@@ -182,13 +139,17 @@ export default class Operation extends PureComponent {
{
(!security || !security.count()) ? null :
<AuthorizeOperationBtn authActions={ authActions }
security={ security }
authSelectors={ authSelectors }/>
<AuthorizeOperationBtn
isAuthorized={ isAuthorized }
onClick={() => {
const applicableDefinitions = authSelectors.definitionsForRequirements(security)
authActions.showDefinitions(applicableDefinitions)
}}
/>
}
</div>
<Collapse isOpened={shown}>
<Collapse isOpened={isShown}>
<div className="opblock-body">
{ deprecated && <h4 className="opblock-title_normal"> Warning: Deprecated</h4>}
{ description &&
@@ -199,23 +160,25 @@ export default class Operation extends PureComponent {
</div>
}
{
externalDocs && externalDocs.get("url") ?
externalDocs && externalDocs.url ?
<div className="opblock-external-docs-wrapper">
<h4 className="opblock-title_normal">Find more details</h4>
<div className="opblock-external-docs">
<span className="opblock-external-docs__description">
<Markdown source={ externalDocs.get("description") } />
<Markdown source={ externalDocs.description } />
</span>
<a className="opblock-external-docs__link" href={ externalDocs.get("url") }>{ externalDocs.get("url") }</a>
<a target="_blank" className="opblock-external-docs__link" href={ sanitizeUrl(externalDocs.url) }>{ externalDocs.url }</a>
</div>
</div> : null
}
<Parameters
parameters={parameters}
specPath={[...specPath, "parameters"]}
operation={operation}
onChangeKey={onChangeKey}
onTryoutClick = { this.onTryoutClick }
onCancelClick = { this.onCancelClick }
onTryoutClick = { onTryoutClick }
onCancelClick = { onCancelClick }
tryItOutEnabled = { tryItOutEnabled }
allowTryItOut={allowTryItOut}
@@ -227,6 +190,21 @@ export default class Operation extends PureComponent {
getConfigs={ getConfigs }
/>
{ !tryItOutEnabled ? null :
<OperationServers
getComponent={getComponent}
path={path}
method={method}
operationServers={operation.get("servers")}
pathServers={specSelectors.paths().getIn([path, "servers"])}
getSelectedServer={oas3Selectors.selectedServer}
setSelectedServer={oas3Actions.setSelectedServer}
setServerVariableValue={oas3Actions.setServerVariableValue}
getServerVariable={oas3Selectors.serverVariableValue}
getEffectiveServerValue={oas3Selectors.serverEffectiveValue}
/>
}
{!tryItOutEnabled || !allowTryItOut ? null : schemes && schemes.size ? <div className="opblock-schemes">
<Schemes schemes={ schemes }
path={ path }
@@ -240,25 +218,23 @@ export default class Operation extends PureComponent {
{ !tryItOutEnabled || !allowTryItOut ? null :
<Execute
getComponent={getComponent}
operation={ operation }
specActions={ specActions }
specSelectors={ specSelectors }
path={ path }
method={ method }
onExecute={ this.onExecute } />
onExecute={ onExecute } />
}
{ (!tryItOutEnabled || !response || !allowTryItOut) ? null :
<Clear
onClick={ this.onClearClick }
specActions={ specActions }
path={ path }
method={ method }/>
}
</div>
{this.state.executeInProgress ? <div className="loading-container"><div className="loading"></div></div> : null}
{executeInProgress ? <div className="loading-container"><div className="loading"></div></div> : null}
{ !responses ? null :
<Responses
@@ -272,10 +248,16 @@ export default class Operation extends PureComponent {
specActions={ specActions }
produces={ produces }
producesValue={ operation.get("produces_value") }
pathMethod={ [path, method] }
specPath={[...specPath, "responses"]}
path={ path }
method={ method }
displayRequestDuration={ displayRequestDuration }
fn={fn} />
}
{ !showExtensions || !extensions.size ? null :
<OperationExt extensions={ extensions } getComponent={ getComponent } />
}
</div>
</Collapse>
</div>

View File

@@ -1,8 +1,13 @@
import React from "react"
import PropTypes from "prop-types"
import { helpers } from "swagger-client"
import { createDeepLinkPath } from "core/utils"
const { opId } = helpers
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 {
@@ -21,28 +26,21 @@ export default class Operations extends React.Component {
render() {
let {
specSelectors,
specActions,
oas3Actions,
getComponent,
layoutSelectors,
layoutActions,
authActions,
authSelectors,
getConfigs,
fn
getConfigs
} = this.props
let taggedOps = specSelectors.taggedOperations()
const Operation = getComponent("operation")
const OperationContainer = getComponent("OperationContainer", true)
const Collapse = getComponent("Collapse")
const Markdown = getComponent("Markdown")
const DeepLink = getComponent("DeepLink")
let showSummary = layoutSelectors.showSummary()
let {
docExpansion,
displayOperationId,
displayRequestDuration,
maxDisplayedTags,
deepLinking
} = getConfigs()
@@ -82,12 +80,11 @@ export default class Operations extends React.Component {
onClick={() => layoutActions.show(isShownKey, !showTag)}
className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }
id={isShownKey.join("-")}>
<a
className="nostyle"
onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
href= {isDeepLinkingEnabled ? `#/${tag}` : null}>
<span>{tag}</span>
</a>
<DeepLink
enabled={isDeepLinkingEnabled}
isShown={showTag}
path={tag}
text={tag} />
{ !tagDescription ? null :
<small>
<Markdown source={tagDescription} />
@@ -101,7 +98,7 @@ export default class Operations extends React.Component {
{ tagExternalDocsUrl ? ": " : null }
{ tagExternalDocsUrl ?
<a
href={tagExternalDocsUrl}
href={sanitizeUrl(tagExternalDocsUrl)}
onClick={(e) => e.stopPropagation()}
target={"_blank"}
>{tagExternalDocsUrl}</a> : null
@@ -120,47 +117,30 @@ export default class Operations extends React.Component {
<Collapse isOpened={showTag}>
{
operations.map( op => {
const path = op.get("path")
const method = op.get("method")
const specPath = ["paths", path, method]
const path = op.get("path", "")
const method = op.get("method", "")
const jumpToKey = `paths.${path}.${method}`
const operationId =
op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), path, method) || op.get("id")
const isShownKey = ["operations", createDeepLinkPath(tag), createDeepLinkPath(operationId)]
// 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
const allowTryItOut = specSelectors.allowTryItOutFor(op.get("path"), op.get("method"))
const response = specSelectors.responseFor(op.get("path"), op.get("method"))
const request = specSelectors.requestFor(op.get("path"), op.get("method"))
if(validMethods.indexOf(method) === -1) {
return null
}
return <Operation
{...op.toObject()}
isShownKey={isShownKey}
jumpToKey={jumpToKey}
showSummary={showSummary}
key={isShownKey}
response={ response }
request={ request }
allowTryItOut={allowTryItOut}
displayOperationId={displayOperationId}
displayRequestDuration={displayRequestDuration}
specActions={ specActions }
specSelectors={ specSelectors }
oas3Actions={oas3Actions}
layoutActions={ layoutActions }
layoutSelectors={ layoutSelectors }
authActions={ authActions }
authSelectors={ authSelectors }
getComponent={ getComponent }
fn={fn}
getConfigs={ getConfigs }
return <OperationContainer
key={`${path}-${method}`}
specPath={specPath}
op={op}
path={path}
method={method}
tag={tag}
/>
}).toArray()
}

View File

@@ -0,0 +1,12 @@
import React from "react"
import PropTypes from "prop-types"
export const ParameterExt = ({ xKey, xVal }) => {
return <div className="parameter__extension">{ xKey }: { String(xVal) }</div>
}
ParameterExt.propTypes = {
xKey: PropTypes.string,
xVal: PropTypes.any
}
export default ParameterExt

View File

@@ -1,6 +1,8 @@
import React, { Component } from "react"
import { Map } from "immutable"
import PropTypes from "prop-types"
import win from "core/window"
import { getExtensions } from "core/utils"
export default class ParameterRow extends Component {
static propTypes = {
@@ -12,7 +14,8 @@ export default class ParameterRow extends Component {
onChangeConsumes: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
getConfigs: PropTypes.func.isRequired
getConfigs: PropTypes.func.isRequired,
specPath: PropTypes.array.isRequired,
}
constructor(props, context) {
@@ -29,11 +32,21 @@ export default class ParameterRow extends Component {
componentWillReceiveProps(props) {
let { specSelectors, pathMethod, param } = props
let { isOAS3 } = specSelectors
let example = param.get("example")
let defaultValue = param.get("default")
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
let enumValue
if(isOAS3()) {
let schema = param.get("schema") || Map()
enumValue = schema.get("enum")
} else {
enumValue = parameter ? parameter.get("enum") : undefined
}
let paramValue = parameter ? parameter.get("value") : undefined
let enumValue = parameter ? parameter.get("enum") : undefined
let value
if ( paramValue !== undefined ) {
@@ -57,10 +70,12 @@ export default class ParameterRow extends Component {
}
render() {
let {param, onChange, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod} = this.props
let {param, onChange, getComponent, getConfigs, isExecute, fn, onChangeConsumes, specSelectors, pathMethod, specPath} = this.props
let { isOAS3 } = specSelectors
const { showExtensions } = getConfigs()
// const onChangeWrapper = (value) => onChange(param, value)
const JsonSchemaForm = getComponent("JsonSchemaForm")
const ParamBody = getComponent("ParamBody")
@@ -80,6 +95,7 @@ export default class ParameterRow extends Component {
const ModelExample = getComponent("modelExample")
const Markdown = getComponent("Markdown")
const ParameterExt = getComponent("ParameterExt")
let schema = param.get("schema")
let type = isOAS3 && isOAS3() ? param.getIn(["schema", "type"]) : param.get("type")
@@ -89,6 +105,7 @@ export default class ParameterRow extends Component {
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
let value = parameter ? parameter.get("value") : ""
let extensions = getExtensions(param)
return (
<tr>
@@ -102,6 +119,7 @@ export default class ParameterRow extends Component {
{ isOAS3 && isOAS3() && param.get("deprecated") ? "deprecated": null }
</div>
<div className="parameter__in">({ param.get("in") })</div>
{ !showExtensions || !extensions.size ? null : extensions.map((v, key) => <ParameterExt key={`${key}-${v}`} xKey={key} xVal={v} /> )}
</td>
<td className="col parameters-col_description">
@@ -121,6 +139,7 @@ export default class ParameterRow extends Component {
{
bodyParam && schema ? <ModelExample getComponent={ getComponent }
specPath={[...specPath, "schema"]}
getConfigs={ getConfigs }
isExecute={ isExecute }
specSelectors={ specSelectors }

View File

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

View File

@@ -1,5 +1,6 @@
import React, { Component } from "react"
import PropTypes from "prop-types"
import { getExtensions } from "core/utils"
const propStyle = { color: "#999", fontStyle: "italic" }
@@ -7,12 +8,15 @@ export default class Primitive extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
name: PropTypes.string,
depth: PropTypes.number
}
render(){
let { schema, getComponent, name, depth } = this.props
let { schema, getComponent, getConfigs, name, depth } = this.props
const { showExtensions } = getConfigs()
if(!schema || !schema.get) {
// don't render if schema isn't correctly formed
@@ -25,9 +29,13 @@ export default class Primitive extends Component {
let enumArray = schema.get("enum")
let title = schema.get("title") || name
let description = schema.get("description")
let properties = schema.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
let extensions = getExtensions(schema)
let properties = schema
.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
.filterNot( (v, key) => extensions.has(key) )
const Markdown = getComponent("Markdown")
const EnumModel = getComponent("EnumModel")
const Property = getComponent("Property")
return <span className="model">
<span className="prop">
@@ -35,9 +43,10 @@ export default class Primitive extends Component {
<span className="prop-type">{ type }</span>
{ format && <span className="prop-format">(${format})</span>}
{
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }>
<br />{ key }: { String(v) }</span>)
: null
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
}
{
showExtensions && extensions.size ? extensions.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
}
{
!description ? null :
@@ -56,4 +65,4 @@ export default class Primitive extends Component {
</span>
</span>
}
}
}

View File

@@ -0,0 +1,16 @@
import React from "react"
import PropTypes from "prop-types"
export const Property = ({ propKey, propVal, propStyle }) => {
return (
<span style={ propStyle }>
<br />{ propKey }: { String(propVal) }</span>
)
}
Property.propTypes = {
propKey: PropTypes.string,
propVal: PropTypes.any,
propStyle: PropTypes.object
}
export default Property

View File

@@ -1,6 +1,6 @@
import React from "react"
import PropTypes from "prop-types"
import { formatXml } from "core/utils"
import formatXml from "xml-but-prettier"
import lowerCase from "lodash/lowerCase"
export default class ResponseBody extends React.Component {
@@ -31,7 +31,10 @@ export default class ResponseBody extends React.Component {
// XML
} else if (/xml/i.test(contentType)) {
body = formatXml(content)
body = formatXml(content, {
textNodesOnSameLine: true,
indentor: " "
})
bodyEl = <HighlightCode value={ body } />
// HTML or Plain Text
@@ -54,9 +57,6 @@ export default class ResponseBody extends React.Component {
(headers["Content-Description"] && (/File Transfer/i).test(headers["Content-Description"])) ||
(headers["content-description"] && (/File Transfer/i).test(headers["content-description"]))) {
let contentLength = headers["content-length"] || headers["Content-Length"]
if ( !(+contentLength) ) return null
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
if (!isSafari && "Blob" in window) {

View File

@@ -1,7 +1,7 @@
import React from "react"
import PropTypes from "prop-types"
import cx from "classnames"
import { fromJS, Seq } from "immutable"
import { fromJS, Seq, Iterable } from "immutable"
import { getSampleSchema, fromJSOrdered } from "core/utils"
const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
@@ -42,11 +42,12 @@ export default class Response extends React.Component {
static propTypes = {
code: PropTypes.string.isRequired,
response: PropTypes.object,
response: PropTypes.instanceOf(Iterable),
className: PropTypes.string,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
specPath: PropTypes.array.isRequired,
fn: PropTypes.object.isRequired,
contentType: PropTypes.string,
controlsAcceptHeader: PropTypes.bool,
@@ -72,6 +73,7 @@ export default class Response extends React.Component {
code,
response,
className,
specPath,
fn,
getComponent,
getConfigs,
@@ -94,16 +96,19 @@ export default class Response extends React.Component {
const ContentType = getComponent("contentType")
var sampleResponse
var schema
var schema, specPathWithPossibleSchema
if(isOAS3()) {
let oas3SchemaForContentType = response.getIn(["content", this.state.responseContentType, "schema"])
const schemaPath = ["content", this.state.responseContentType, "schema"]
const oas3SchemaForContentType = response.getIn(schemaPath)
sampleResponse = oas3SchemaForContentType ? getSampleSchema(oas3SchemaForContentType.toJS(), this.state.responseContentType, {
includeReadOnly: true
}) : null
schema = oas3SchemaForContentType ? inferSchema(oas3SchemaForContentType.toJS()) : null
specPathWithPossibleSchema = oas3SchemaForContentType ? schemaPath : specPath
} else {
schema = inferSchema(response.toJS())
schema = inferSchema(response.toJS()) // TODO: don't convert back and forth. Lets just stick with immutable for inferSchema
specPathWithPossibleSchema = response.has("schema") ? [...specPath, "schema"] : specPath
sampleResponse = schema ? getSampleSchema(schema, contentType, {
includeReadOnly: true,
includeWriteOnly: true // writeOnly has no filtering effect in swagger 2.0
@@ -145,6 +150,7 @@ export default class Response extends React.Component {
{ example ? (
<ModelExample
specPath={specPathWithPossibleSchema}
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
@@ -153,7 +159,10 @@ export default class Response extends React.Component {
) : null}
{ headers ? (
<Headers headers={ headers }/>
<Headers
headers={ headers }
getComponent={ getComponent }
/>
) : null}

View File

@@ -1,41 +1,53 @@
import React from "react"
import PropTypes from "prop-types"
import { fromJS } from "immutable"
import { fromJS, Iterable } from "immutable"
import { defaultStatusCode, getAcceptControllingResponse } from "core/utils"
export default class Responses extends React.Component {
static propTypes = {
request: PropTypes.object,
tryItOutResponse: PropTypes.object,
responses: PropTypes.object.isRequired,
produces: PropTypes.object,
tryItOutResponse: PropTypes.instanceOf(Iterable),
responses: PropTypes.instanceOf(Iterable).isRequired,
produces: PropTypes.instanceOf(Iterable),
producesValue: PropTypes.any,
displayRequestDuration: PropTypes.bool.isRequired,
path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
displayRequestDuration: PropTypes.bool.isRequired,
specPath: PropTypes.array.isRequired,
fn: PropTypes.object.isRequired
}
static defaultProps = {
request: null,
tryItOutResponse: null,
produces: fromJS(["application/json"]),
displayRequestDuration: false
}
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
shouldComponentUpdate(nextProps) {
// BUG: props.tryItOutResponse is always coming back as a new Immutable instance
let render = this.props.tryItOutResponse !== nextProps.tryItOutResponse
|| this.props.responses !== nextProps.responses
|| this.props.produces !== nextProps.produces
|| this.props.producesValue !== nextProps.producesValue
|| this.props.displayRequestDuration !== nextProps.displayRequestDuration
|| this.props.path !== nextProps.path
|| this.props.method !== nextProps.method
return render
}
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue([this.props.path, this.props.method], val)
onResponseContentTypeChange = ({ controlsAcceptHeader, value }) => {
const { oas3Actions, pathMethod } = this.props
const { oas3Actions, path, method } = this.props
if(controlsAcceptHeader) {
oas3Actions.setResponseContentType({
value,
pathMethod
path,
method
})
}
}
@@ -43,14 +55,14 @@ export default class Responses extends React.Component {
render() {
let {
responses,
request,
tryItOutResponse,
getComponent,
getConfigs,
specSelectors,
fn,
producesValue,
displayRequestDuration
displayRequestDuration,
specPath,
} = this.props
let defaultCode = defaultStatusCode( responses )
@@ -81,12 +93,12 @@ export default class Responses extends React.Component {
{
!tryItOutResponse ? null
: <div>
<LiveResponse request={ request }
response={ tryItOutResponse }
<LiveResponse response={ tryItOutResponse }
getComponent={ getComponent }
getConfigs={ getConfigs }
specSelectors={ specSelectors }
pathMethod={ this.props.pathMethod }
path={ this.props.path }
method={ this.props.method }
displayRequestDuration={ displayRequestDuration } />
<h4>Responses</h4>
</div>
@@ -104,9 +116,11 @@ export default class Responses extends React.Component {
<tbody>
{
responses.entrySeq().map( ([code, response]) => {
let className = tryItOutResponse && tryItOutResponse.get("status") == code ? "response_current" : ""
return (
<Response key={ code }
specPath={[...specPath, code]}
isDefault={defaultCode === code}
fn={fn}
className={ className }