in with the new

This commit is contained in:
Ron
2017-03-17 21:17:53 -07:00
parent bd8344c808
commit f22a628934
157 changed files with 12952 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
import React, { PropTypes } from "react"
export default class App extends React.Component {
getLayout() {
let { getComponent, layoutSelectors } = this.props
const layoutName = layoutSelectors.current()
const Component = getComponent(layoutName, true)
return Component ? Component : ()=> <h1> No layout defined for "{layoutName}" </h1>
}
render() {
const Layout = this.getLayout()
return (
<Layout/>
)
}
}
App.propTypes = {
getComponent: PropTypes.func.isRequired,
layoutSelectors: PropTypes.object.isRequired,
}
App.defaultProps = {
}

View File

@@ -0,0 +1,82 @@
import React, { PropTypes } from "react"
export default class ApiKeyAuth extends React.Component {
static propTypes = {
authorized: PropTypes.object,
getComponent: PropTypes.func.isRequired,
errSelectors: PropTypes.object.isRequired,
schema: PropTypes.object.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func
}
constructor(props, context) {
super(props, context)
let { name, schema } = this.props
let value = this.getValue()
this.state = {
name: name,
schema: schema,
value: value
}
}
getValue () {
let { name, authorized } = this.props
return authorized && authorized.getIn([name, "value"])
}
onChange =(e) => {
let { onChange } = this.props
let value = e.target.value
let newState = Object.assign({}, this.state, { value: value })
this.setState(newState)
onChange(newState)
}
render() {
let { schema, getComponent, errSelectors, name } = this.props
const Input = getComponent("Input")
const Row = getComponent("Row")
const Col = getComponent("Col")
const AuthError = getComponent("authError")
const Markdown = getComponent( "Markdown" )
const JumpToPath = getComponent("JumpToPath", true)
let value = this.getValue()
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
return (
<div>
<h4>Api key authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4>
{ value && <h6>Authorized</h6>}
<Row>
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}}
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>
<Row>
<label>Value:</label>
<Col>
{
value || <Input type="text" onChange={ this.onChange }/>
}
</Col>
</Row>
{
errors.valueSeq().map( (error, key) => {
return <AuthError error={ error }
key={ key }/>
} )
}
</div>
)
}
}

View File

@@ -0,0 +1,59 @@
import React, { PropTypes } from "react"
export default class AuthorizationPopup extends React.Component {
close =() => {
let { authActions } = this.props
authActions.showDefinitions(false)
}
render() {
let { authSelectors, authActions, getComponent, errSelectors, specSelectors, fn: { AST } } = this.props
let definitions = authSelectors.shownDefinitions()
const Auths = getComponent("auths")
return (
<div className="dialog-ux">
<div className="backdrop-ux"></div>
<div className="modal-ux">
<div className="modal-dialog-ux">
<div className="modal-ux-inner">
<div className="modal-ux-header">
<h3>Available authorizations</h3>
<button type="button" className="close-modal" onClick={ this.close }>
<svg width="20" height="20">
<use xlinkHref="#close" />
</svg>
</button>
</div>
<div className="modal-ux-content">
{
definitions.valueSeq().map(( definition, key ) => {
return <Auths key={ key }
AST={AST}
definitions={ definition }
getComponent={ getComponent }
errSelectors={ errSelectors }
authSelectors={ authSelectors }
authActions={ authActions }
specSelectors={ specSelectors }/>
})
}
</div>
</div>
</div>
</div>
</div>
)
}
static propTypes = {
fn: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
authSelectors: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
errSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
}
}

View File

@@ -0,0 +1,42 @@
import React, { PropTypes } from "react"
export default class AuthorizeBtn extends React.Component {
static propTypes = {
className: PropTypes.string
}
onClick =() => {
let { authActions, authSelectors, errActions} = this.props
let definitions = authSelectors.definitionsToAuthorize()
authActions.showDefinitions(definitions)
}
render() {
let { authSelectors, getComponent } = this.props
//must be moved out of button component
const AuthorizationPopup = getComponent("authorizationPopup", true)
let showPopup = !!authSelectors.shownDefinitions()
let isAuthorized = !!authSelectors.authorized().size
return (
<div className="auth-wrapper">
<button className={isAuthorized ? "btn authorize locked" : "btn authorize unlocked"} onClick={ this.onClick }>
<span>Authorize</span>
<svg width="20" height="20">
<use xlinkHref={ isAuthorized ? "#locked" : "#unlocked" } />
</svg>
</button>
{ showPopup && <AuthorizationPopup /> }
</div>
)
}
static propTypes = {
getComponent: PropTypes.func.isRequired,
authSelectors: PropTypes.object.isRequired,
errActions: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
}
}

View File

@@ -0,0 +1,34 @@
import React, { PropTypes } from "react"
import ImPropTypes from "react-immutable-proptypes"
export default class AuthorizeOperationBtn extends React.Component {
onClick =(e) => {
e.stopPropagation()
let { security, authActions, authSelectors } = this.props
let definitions = authSelectors.getDefinitionsByNames(security)
authActions.showDefinitions(definitions)
}
render() {
let { security, authSelectors } = this.props
let isAuthorized = authSelectors.isAuthorized(security)
return (
<button className={isAuthorized ? "authorization__btn locked" : "authorization__btn unlocked"} onClick={ this.onClick }>
<svg width="20" height="20">
<use xlinkHref={ isAuthorized ? "#locked" : "#unlocked" } />
</svg>
</button>
)
}
static propTypes = {
authSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
security: ImPropTypes.iterable.isRequired
}
}

View File

@@ -0,0 +1,138 @@
import React, { PropTypes } from "react"
import ImPropTypes from "react-immutable-proptypes"
export default class Auths extends React.Component {
static propTypes = {
definitions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
authSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired
}
constructor(props, context) {
super(props, context)
this.state = {}
}
onAuthChange =(auth) => {
let { name } = auth
this.setState({ [name]: auth })
}
submitAuth =(e) => {
e.preventDefault()
let { authActions } = this.props
authActions.authorize(this.state)
}
logoutClick =(e) => {
e.preventDefault()
let { authActions, definitions } = this.props
let auths = definitions.map( (val, key) => {
return key
}).toArray()
authActions.logout(auths)
}
render() {
let { definitions, getComponent, authSelectors, errSelectors, specSelectors } = this.props
const ApiKeyAuth = getComponent("apiKeyAuth")
const BasicAuth = getComponent("basicAuth")
const Oauth2 = getComponent("oauth2", true)
const Button = getComponent("Button")
const JumpToPath = getComponent("JumpToPath", true)
let specStr = specSelectors.specStr()
let authorized = authSelectors.authorized()
let authorizedAuth = definitions.filter( (definition, key) => {
return !!authorized.get(key)
})
let nonOauthDefinitions = definitions.filter( schema => schema.get("type") !== "oauth2")
let oauthDefinitions = definitions.filter( schema => schema.get("type") === "oauth2")
return (
<div className="auth-container">
{
!!nonOauthDefinitions.size && <form onSubmit={ this.submitAuth }>
{
nonOauthDefinitions.map( (schema, name) => {
let type = schema.get("type")
let authEl
switch(type) {
case "apiKey": authEl = <ApiKeyAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ this.onAuthChange } />
break
case "basic": authEl = <BasicAuth key={ name }
schema={ schema }
name={ name }
errSelectors={ errSelectors }
authorized={ authorized }
getComponent={ getComponent }
onChange={ this.onAuthChange } />
break
default: authEl = <div key={ name }>Unknown security definition type { type }</div>
}
return (<div key={`${name}-jump`}>
{ authEl }
</div>)
}).toArray()
}
<div className="auth-btn-wrapper">
{
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>
}
</div>
</form>
}
{
oauthDefinitions && oauthDefinitions.size ? <div>
<div className="scope-def">
<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.</p>
<p>API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>
</div>
{
definitions.filter( schema => schema.get("type") === "oauth2")
.map( (schema, name) =>{
return (<div key={ name }>
<Oauth2 authorized={ authorized }
schema={ schema }
name={ name } />
</div>)
}
).toArray()
}
</div> : null
}
</div>
)
}
static propTypes = {
errSelectors: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
authSelectors: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
definitions: ImPropTypes.iterable.isRequired
}
}

View File

@@ -0,0 +1,97 @@
import React, { PropTypes } from "react"
import ImPropTypes from "react-immutable-proptypes"
export default class BasicAuth extends React.Component {
static propTypes = {
authorized: PropTypes.object,
getComponent: PropTypes.func.isRequired,
schema: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired
}
constructor(props, context) {
super(props, context)
let { schema, name } = this.props
let value = this.getValue()
let username = value.username
this.state = {
name: name,
schema: schema,
value: !username ? {} : {
username: username
}
}
}
getValue () {
let { authorized, name } = this.props
return authorized && authorized.getIn([name, "value"]) || {}
}
onChange =(e) => {
let { onChange } = this.props
let { value, name } = e.target
let newValue = this.state.value
newValue[name] = value
this.setState({ value: newValue })
onChange(this.state)
}
render() {
let { schema, getComponent, name, errSelectors } = this.props
const Input = getComponent("Input")
const Row = getComponent("Row")
const Col = getComponent("Col")
const AuthError = getComponent("authError")
const JumpToPath = getComponent("JumpToPath", true)
const Markdown = getComponent( "Markdown" )
let username = this.getValue().username
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
return (
<div>
<h4>Basic authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4>
{ username && <h6>Authorized</h6> }
<Row>
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}}
source={ schema.get("description") } />
</Row>
<Row>
<Col tablet={2} desktop={2}>username:</Col>
<Col tablet={10} desktop={10}>
{
username || <Input type="text" required="required" name="username" onChange={ this.onChange }/>
}
</Col>
</Row>
{
!username && <Row>
<Col tablet={2} desktop={2}>password:</Col>
<Col tablet={10} desktop={10}><Input required="required" autoComplete="new-password" name="password" type="password" onChange={ this.onChange }/></Col>
</Row>
}
{
errors.valueSeq().map( (error, key) => {
return <AuthError error={ error }
key={ key }/>
} )
}
</div>
)
}
static propTypes = {
name: PropTypes.string.isRequired,
errSelectors: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
onChange: PropTypes.func,
schema: ImPropTypes.map,
authorized: ImPropTypes.map
}
}

View File

@@ -0,0 +1,23 @@
import React, { PropTypes } from "react"
export default class AuthError extends React.Component {
static propTypes = {
error: PropTypes.object.isRequired
}
render() {
let { error } = this.props
let level = error.get("level")
let message = error.get("message")
let source = error.get("source")
return (
<div className="errors" style={{ backgroundColor: "#ffeeee", color: "red", margin: "1em" }}>
<b style={{ textTransform: "capitalize", marginRight: "1em"}} >{ source } { level }</b>
<span>{ message }</span>
</div>
)
}
}

View File

@@ -0,0 +1,218 @@
import React, { PropTypes } from "react"
import oauth2Authorize from "core/oauth2-authorize"
const IMPLICIT = "implicit"
const ACCESS_CODE = "accessCode"
const PASSWORD = "password"
const APPLICATION = "application"
export default class Oauth2 extends React.Component {
static propTypes = {
name: PropTypes.string,
authorized: PropTypes.object,
configs: PropTypes.object,
getComponent: PropTypes.func.isRequired,
schema: PropTypes.object.isRequired,
authSelectors: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
errSelectors: PropTypes.object.isRequired,
errActions: PropTypes.object.isRequired
}
constructor(props, context) {
super(props, context)
let { name, schema, authorized } = this.props
let auth = authorized && authorized.get(name)
let username = auth && auth.get("username") || ""
let clientId = auth && auth.get("clientId") || ""
let clientSecret = auth && auth.get("clientSecret") || ""
let passwordType = auth && auth.get("passwordType") || "none"
this.state = {
name: name,
schema: schema,
scopes: [],
clientId: clientId,
clientSecret: clientSecret,
username: username,
password: "",
passwordType: passwordType
}
}
authorize =() => {
let { authActions, errActions, getConfigs } = this.props
let configs = getConfigs()
errActions.clear({authId: name,type: "auth", source: "auth"})
oauth2Authorize(this.state, authActions, errActions, configs)
}
onScopeChange =(e) => {
let { target } = e
let { checked } = target
let scope = target.dataset.value
if ( checked && this.state.scopes.indexOf(scope) === -1 ) {
let newScopes = this.state.scopes.concat([scope])
this.setState({ scopes: newScopes })
} else if ( !checked && this.state.scopes.indexOf(scope) > -1) {
this.setState({ scopes: this.state.scopes.filter((val) => val !== scope) })
}
}
onInputChange =(e) => {
let { target : { dataset : { name }, value } } = e
let state = {
[name]: value
}
this.setState(state)
}
logout =(e) => {
e.preventDefault()
let { authActions, errActions, name } = this.props
errActions.clear({authId: name, type: "auth", source: "auth"})
authActions.logout([ name ])
}
render() {
let { schema, getComponent, authSelectors, errSelectors, name } = this.props
const Input = getComponent("Input")
const Row = getComponent("Row")
const Col = getComponent("Col")
const Button = getComponent("Button")
const AuthError = getComponent("authError")
const JumpToPath = getComponent("JumpToPath", true)
const Markdown = getComponent( "Markdown" )
let flow = schema.get("flow")
let scopes = schema.get("allowedScopes") || schema.get("scopes")
let authorizedAuth = authSelectors.authorized().get(name)
let isAuthorized = !!authorizedAuth
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
let isValid = !errors.filter( err => err.get("source") === "validation").size
return (
<div>
<h4>OAuth2.0 <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}}
source={ schema.get("description") } />
{ isAuthorized && <h6>Authorized</h6> }
{ ( flow === IMPLICIT || flow === ACCESS_CODE ) && <p>Authorization URL: <code>{ schema.get("authorizationUrl") }</code></p> }
{ ( flow === PASSWORD || flow === ACCESS_CODE || flow === APPLICATION ) && <p>Token URL:<code> { schema.get("tokenUrl") }</code></p> }
<p className="flow">Flow: <code>{ schema.get("flow") }</code></p>
{
flow === PASSWORD && ( !isAuthorized || isAuthorized && this.state.username) && <Row>
<Col tablet={2} desktop={2}>username:</Col>
<Col tablet={10} desktop={10}>
{
isAuthorized ? <span>{ this.state.username }</span>
: <input type="text" data-name="username" onChange={ this.onInputChange }/>
}
</Col>
</Row>
}
{
flow === PASSWORD && !isAuthorized && <Row>
<Col tablet={2} desktop={2}>password:</Col>
<Col tablet={10} desktop={10}>
<input type="password" data-name="password" onChange={ this.onInputChange }/>
</Col>
</Row>
}
{
flow === PASSWORD && <Row>
<Col tablet={2} desktop={2}>type:</Col>
<Col tablet={10} desktop={10}>
{
isAuthorized ? <span>{ this.state.passwordType }</span>
: <select data-name="passwordType" onChange={ this.onInputChange }>
<option value="none">None or other</option>
<option value="basic">Basic auth</option>
<option value="request">Request body</option>
</select>
}
</Col>
</Row>
}
{
( flow === IMPLICIT || flow === ACCESS_CODE || ( flow === PASSWORD && this.state.passwordType!== "none") ) &&
( !isAuthorized || isAuthorized && this.state.clientId) && <Row>
<label htmlFor="client_id">client_id:</label>
<Col tablet={10} desktop={10}>
{
isAuthorized ? <span>{ this.state.clientId }</span>
: <input id="client_id" type="text" required={ flow === PASSWORD } data-name="clientId"
onChange={ this.onInputChange }/>
}
</Col>
</Row>
}
{
( flow === ACCESS_CODE || ( flow === PASSWORD && this.state.passwordType!== "none") ) && <Row>
<label htmlFor="client_secret">client_secret:</label>
<Col tablet={10} desktop={10}>
{
isAuthorized ? <span>{ this.state.clientSecret }</span>
: <input id="client_secret" type="text" data-name="clientSecret"
onChange={ this.onInputChange }/>
}
</Col>
</Row>
}
{
!isAuthorized && flow !== PASSWORD && scopes && scopes.size ? <div className="scopes">
<h2>Scopes:</h2>
{ scopes.map((description, name) => {
return (
<Row key={ name }>
<div className="checkbox">
<Input data-value={ name }
id={`${name}-checkbox`}
disabled={ isAuthorized }
type="checkbox"
onChange={ this.onScopeChange }/>
<label htmlFor={`${name}-checkbox`}>
<span className="item"></span>
<div className="text">
<p className="name">{name}</p>
<p className="description">{description}</p>
</div>
</label>
</div>
</Row>
)
}).toArray()
}
</div> : null
}
{
errors.valueSeq().map( (error, key) => {
return <AuthError error={ error }
key={ key }/>
} )
}
<div className="auth-btn-wrapper">
{ isValid && flow !== APPLICATION &&
( isAuthorized ? <Button className="btn modal-btn auth authorize" onClick={ this.logout }>Logout</Button>
: <Button className="btn modal-btn auth authorize" onClick={ this.authorize }>Authorize</Button>
)
}
</div>
</div>
)
}
}

View File

@@ -0,0 +1,24 @@
import React, { Component, PropTypes } from "react"
export default class Clear extends Component {
onClick =() => {
let { specActions, path, method } = this.props
specActions.clearResponse( path, method )
specActions.clearRequest( path, method )
}
render(){
return (
<button className="btn btn-clear opblock-control__btn" onClick={ this.onClick }>
Clear
</button>
)
}
static propTypes = {
specActions: PropTypes.object.isRequired,
path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
}
}

View File

@@ -0,0 +1,45 @@
import React, { PropTypes } from "react"
import ImPropTypes from "react-immutable-proptypes"
import { fromJS } from 'immutable'
const noop = ()=>{}
export default class ContentType extends React.Component {
static propTypes = {
contentTypes: PropTypes.oneOfType([ImPropTypes.list, ImPropTypes.set]),
value: PropTypes.string,
onChange: PropTypes.func,
className: PropTypes.string
}
static defaultProps = {
onChange: noop,
value: null,
contentTypes: fromJS(["application/json"]),
}
componentDidMount() {
// Needed to populate the form, initially
this.props.onChange(this.props.contentTypes.first())
}
onChangeWrapper = e => this.props.onChange(e.target.value)
render() {
let { contentTypes, className, value } = this.props
if ( !contentTypes || !contentTypes.size )
return null
return (
<div className={ "content-type-wrapper " + ( className || "" ) }>
<select className="content-type" value={value} onChange={this.onChangeWrapper} >
{ contentTypes.map( (val) => {
return <option key={ val } value={ val }>{ val }</option>
}).toArray()}
</select>
</div>
)
}
}

View File

@@ -0,0 +1,28 @@
import React, { PropTypes } from "react"
import curlify from "core/curlify"
export default class Curl extends React.Component {
static propTypes = {
request: PropTypes.object.isRequired
}
handleFocus(e) {
e.target.select()
document.execCommand("copy")
}
render() {
let { request } = this.props
let curl = curlify(request)
return (
<div>
<h4>Curl</h4>
<div className="copy-paste">
<textarea onFocus={this.handleFocus} className="curl" style={{ whiteSpace: "normal" }} defaultValue={curl}></textarea>
</div>
</div>
)
}
}

View File

@@ -0,0 +1,51 @@
import React, { PropTypes } from "react"
import Collapse from "react-collapse"
import { presets } from "react-motion"
import ObjectInspector from "react-object-inspector"
import Perf from "react-addons-perf"
export default class Debug extends React.Component {
constructor() {
super()
this.state = {
jsonDumpOpen: false
}
this.toggleJsonDump = (e) => {
e.preventDefault()
this.setState({jsonDumpOpen: !this.state.jsonDumpOpen})
}
window.Perf = Perf
}
plusOrMinus(bool) {
return bool ? "-" : "+"
}
render() {
let { getState } = this.props
window.props = this.props
return (
<div className="info">
<h3><a onClick={this.toggleJsonDump}> {this.plusOrMinus(this.state.jsonDumpOpen)} App </a></h3>
<Collapse isOpened={this.state.jsonDumpOpen} springConfig={presets.noWobble}>
<ObjectInspector data={getState().toJS() || {}} name="state" initialExpandedPaths={["state"]}/>
</Collapse>
</div>
)
}
}
Debug.propTypes = {
getState: PropTypes.func.isRequired
}

View File

@@ -0,0 +1,113 @@
import React, { PropTypes } from "react"
import Im, { List } from "immutable"
import Collapse from "react-collapse"
import sortBy from "lodash/sortBy"
export default class Errors extends React.Component {
static propTypes = {
jumpToLine: PropTypes.func,
errSelectors: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired
}
render() {
let { jumpToLine, errSelectors, layoutSelectors, layoutActions } = this.props
let errors = errSelectors.allErrors()
// all thrown errors, plus error-level everything else
let allErrorsToDisplay = errors.filter(err => err.get("type") === "thrown" ? true :err.get("level") === "error")
if(!allErrorsToDisplay || allErrorsToDisplay.count() < 1) {
return null
}
let isVisible = layoutSelectors.isShown(["errorPane"], true)
let toggleVisibility = () => layoutActions.show(["errorPane"], !isVisible)
let sortedJSErrors = allErrorsToDisplay.sortBy(err => err.get("line"))
return (
<pre className="errors-wrapper">
<hgroup className="error">
<h4 className="errors__title">Errors</h4>
<button className="btn errors__clear-btn" onClick={ toggleVisibility }>{ isVisible ? "Hide" : "Show" }</button>
</hgroup>
<Collapse isOpened={ isVisible } animated >
<div className="errors">
{ sortedJSErrors.map((err, i) => {
if(err.get("type") === "thrown") {
return <ThrownErrorItem key={ i } error={ err.get("error") || err } jumpToLine={jumpToLine} />
}
if(err.get("type") === "spec") {
return <SpecErrorItem key={ i } error={ err } jumpToLine={jumpToLine} />
}
}) }
</div>
</Collapse>
</pre>
)
}
}
const ThrownErrorItem = ( { error, jumpToLine } ) => {
if(!error) {
return null
}
let errorLine = error.get("line")
return (
<div className="error-wrapper">
{ !error ? null :
<div>
<h4>{ (error.get("source") && error.get("level")) ?
toTitleCase(error.get("source")) + " " + error.get("level") : "" }
{ error.get("path") ? <small> at {error.get("path")}</small>: null }</h4>
<span style={{ whiteSpace: "pre-line", "maxWidth": "100%" }}>
{ error.get("message") }
</span>
<div>
{ errorLine ? <a onClick={jumpToLine.bind(null, errorLine)}>Jump to line { errorLine }</a> : null }
</div>
</div>
}
</div>
)
}
const SpecErrorItem = ( { error, jumpToLine } ) => {
return (
<div className="error-wrapper">
{ !error ? null :
<div>
<h4>{ toTitleCase(error.get("source")) + " " + error.get("level") }{ error.get("path") ? <small> at {List.isList(error.get("path")) ? error.get("path").join(".") : error.get("path")}</small>: null }</h4>
<span style={{ whiteSpace: "pre-line"}}>{ error.get("message") }</span>
<div>
{ jumpToLine ? (
<a onClick={jumpToLine.bind(null, error.get("line"))}>Jump to line { error.get("line") }</a>
) : null }
</div>
</div>
}
</div>
)
}
function toTitleCase(str) {
return str
.split(" ")
.map(substr => substr[0].toUpperCase() + substr.slice(1))
.join(" ")
}
ThrownErrorItem.propTypes = {
error: PropTypes.object.isRequired,
jumpToLine: PropTypes.func
}
SpecErrorItem.propTypes = {
error: PropTypes.object.isRequired,
jumpToLine: PropTypes.func
}

View File

@@ -0,0 +1,41 @@
import React, { Component, PropTypes } from "react"
import { fromJS } from "immutable"
export default class Execute extends Component {
static propTypes = {
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
operation: PropTypes.object.isRequired,
path: PropTypes.string.isRequired,
getComponent: PropTypes.func.isRequired,
method: PropTypes.string.isRequired,
onExecute: PropTypes.func
}
onClick=()=>{
let { specSelectors, specActions, operation, path, method } = this.props
specActions.validateParams( [path, method] )
if ( specSelectors.validateBeforeExecute([path, method]) ) {
if(this.props.onExecute) {
this.props.onExecute()
}
specActions.execute( { operation, path, method } )
}
}
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue([this.props.path, this.props.method], val)
render(){
let { getComponent, operation, specActions, path, method } = this.props
const ContentType = getComponent( "contentType" )
return (
<button className="btn execute opblock-control__btn" onClick={ this.onClick }>
Execute
</button>
)
}
}

View File

@@ -0,0 +1,9 @@
import React from "react"
export default class Footer extends React.Component {
render() {
return (
<div className="footer"></div>
)
}
}

View File

@@ -0,0 +1,46 @@
import React, { PropTypes } from "react"
import Im from "immutable"
export default class Headers extends React.Component {
static propTypes = {
headers: PropTypes.object.isRequired
};
render() {
let { headers } = this.props
if ( !headers || !headers.size )
return null
return (
<div className="headers-wrapper">
<h4 className="headers__title">Headers:</h4>
<table className="headers">
<thead>
<tr className="header-row">
<th className="header-col">Name</th>
<th className="header-col">Description</th>
<th className="header-col">Type</th>
</tr>
</thead>
<tbody>
{
headers.entrySeq().map( ([ key, header ]) => {
if(!Im.Map.isMap(header)) {
return null
}
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>
</tr>)
}).toArray()
}
</tbody>
</table>
</div>
)
}
}

View File

@@ -0,0 +1,24 @@
import React, { Component, PropTypes } from "react"
import { highlight } from "core/utils"
export default class HighlightCode extends Component {
static propTypes = {
value: PropTypes.string.isRequired,
className: PropTypes.string
}
componentDidMount() {
highlight(this.refs.el)
}
componentDidUpdate() {
highlight(this.refs.el)
}
render () {
let { value, className } = this.props
className = className || ""
return <pre ref="el" className={className + " microlight"}>{ value }</pre>
}
}

View File

@@ -0,0 +1,128 @@
import React, { PropTypes } from "react"
import { fromJS } from "immutable"
import ImPropTypes from "react-immutable-proptypes"
class Path extends React.Component {
static propTypes = {
host: PropTypes.string,
basePath: PropTypes.string
}
render() {
let { host, basePath } = this.props
return (
<pre className="base-url">
[ Base url: {host}{basePath}]
</pre>
)
}
}
class Contact extends React.Component {
static propTypes = {
data: PropTypes.object
}
render(){
let { data } = this.props
let name = data.get("name") || "the developer"
let url = data.get("url")
let email = data.get("email")
return (
<div>
{ url && <div><a href={ url } target="_blank">{ name } - Website</a></div> }
{ email &&
<a href={`mailto:${email}`}>
{ url ? `Send email to ${name}` : `Contact ${name}`}
</a>
}
</div>
)
}
}
class License extends React.Component {
static propTypes = {
license: PropTypes.object
}
render(){
let { license } = this.props
let name = license.get("name") || "License"
let url = license.get("url")
return (
<div>
{
url ? <a href={ url }>{ name }</a>
: <span>{ name }</span>
}
</div>
)
}
}
export default class Info extends React.Component {
static propTypes = {
info: PropTypes.object,
url: PropTypes.string,
host: PropTypes.string,
basePath: PropTypes.string,
externalDocs: ImPropTypes.map,
getComponent: PropTypes.func.isRequired,
}
render() {
let { info, url, host, basePath, getComponent, externalDocs } = this.props
let version = info.get("version")
let description = info.get("description")
let title = info.get("title")
let termsOfService = info.get("termsOfService")
let contact = info.get("contact")
let license = info.get("license")
const { url:externalDocsUrl, description:externalDocsDescription } = (externalDocs || fromJS({})).toJS()
const Markdown = getComponent("Markdown")
return (
<div className="info">
<hgroup className="main">
<h2 className="title" >{ title }
{ version && <small><pre className="version"> { version } </pre></small> }
</h2>
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
{ url && <a href={ url }><span className="url"> { url } </span></a> }
</hgroup>
<div className="description">
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}} source={ description } />
</div>
{
termsOfService && <div>
<a href={ 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>
: null }
</div>
)
}
}
Info.propTypes = {
title: PropTypes.any,
description: PropTypes.any,
version: PropTypes.any,
url: PropTypes.string
}

View File

@@ -0,0 +1,252 @@
import React, { PropTypes } from "react"
import OriCollapse from "react-collapse"
import _Markdown from "react-remarkable"
const noop = () => {}
function xclass(...args) {
return args.filter(a => !!a).join(" ").trim()
}
export const Markdown = _Markdown
export class Container extends React.Component {
render() {
let { fullscreen, full, ...rest } = this.props
// Normal element
if(fullscreen)
return <section {...rest}/>
let containerClass = "container" + (full ? "-full" : "")
return (
<section {...rest} className={xclass(rest.className, containerClass)}/>
)
}
}
Container.propTypes = {
fullscreen: PropTypes.bool,
full: PropTypes.bool,
className: PropTypes.string
}
const DEVICES = {
"mobile": "",
"tablet": "-tablet",
"desktop": "-desktop",
"large": "-hd"
}
export class Col extends React.Component {
render() {
const {
hide,
keepContents,
mobile, /* we don't want these in the final component, since React now complains. So we extract them */
tablet,
desktop,
large,
...rest
} = this.props
if(hide && !keepContents)
return <span/>
let classesAr = []
for (let device in DEVICES) {
let deviceClass = DEVICES[device]
if(device in this.props) {
let val = this.props[device]
if(val < 1) {
classesAr.push("none" + deviceClass)
continue
}
classesAr.push("block" + deviceClass)
classesAr.push("col-" + val + deviceClass)
}
}
let classes = xclass(rest.className, "clear", ...classesAr)
return (
<section {...rest} style={{display: hide ? "none": null}} className={classes}/>
)
}
}
Col.propTypes = {
hide: PropTypes.bool,
keepContents: PropTypes.bool,
mobile: PropTypes.number,
tablet: PropTypes.number,
desktop: PropTypes.number,
large: PropTypes.number,
className: PropTypes.string
}
export class Row extends React.Component {
render() {
return <div {...this.props} className={xclass(this.props.className, "wrapper")} />
}
}
Row.propTypes = {
className: PropTypes.string
}
export class Button extends React.Component {
static propTypes = {
className: PropTypes.string
}
static defaultProps = {
className: ""
}
render() {
return <button {...this.props} className={xclass(this.props.className, "button")} />
}
}
export const TextArea = (props) => <textarea {...props} />
export const Input = (props) => <input {...props} />
export class Select extends React.Component {
static propTypes = {
allowedValues: PropTypes.object,
value: PropTypes.any,
onChange: PropTypes.func,
multiple: PropTypes.bool,
allowEmptyValue: PropTypes.bool
}
static defaultProps = {
multiple: false,
allowEmptyValue: true
}
constructor(props, context) {
super(props, context)
let value
if (props.value !== undefined) {
value = props.value
} else {
value = props.multiple ? [""] : ""
}
this.state = { value: value }
}
onChange = (e) => {
let { onChange, multiple } = this.props
let options = [].slice.call(e.target.options)
let value
if (multiple) {
value = options.filter(function (option) {
return option.selected
})
.map(function (option){
return option.value
})
} else {
value = e.target.value
}
this.setState({value: value})
onChange && onChange(value)
}
render(){
let { allowedValues, multiple, allowEmptyValue } = this.props
let value = this.state.value.toJS ? this.state.value.toJS() : this.state.value
return (
<select multiple={ multiple } value={ value } onChange={ this.onChange } >
{ allowEmptyValue ? <option value="">--</option> : null }
{
allowedValues.map(function (item, key) {
return <option key={ key } value={ String(item) }>{ item }</option>
})
}
</select>
)
}
}
export class Link extends React.Component {
render() {
return <a {...this.props} className={xclass(this.props.className, "link")}/>
}
}
Link.propTypes = {
className: PropTypes.string
}
const NoMargin = ({children}) => <div style={{height: "auto", border: "none", margin: 0, padding: 0}}> {children} </div>
NoMargin.propTypes = {
children: PropTypes.node
}
export class Collapse extends React.Component {
static propTypes = {
isOpened: PropTypes.bool,
children: PropTypes.node.isRequired,
animated: PropTypes.bool
}
static defaultProps = {
isOpened: false,
animated: false
}
renderNotAnimated() {
if(!this.props.isOpened)
return <noscript/>
return (
<NoMargin>
{this.props.children}
</NoMargin>
)
}
render() {
let { animated, isOpened, children } = this.props
if(!animated)
return this.renderNotAnimated()
children = isOpened ? children : null
return (
<OriCollapse isOpened={isOpened}>
<NoMargin>
{children}
</NoMargin>
</OriCollapse>
)
}
}

View File

@@ -0,0 +1,72 @@
import React, { PropTypes } from "react"
export default class XPane extends React.Component {
render() {
let { getComponent, specSelectors, specActions, layoutSelectors, layoutActions } = this.props
let info = specSelectors.info()
let url = specSelectors.url()
let showEditor = layoutSelectors.isShown("editor")
let Info = getComponent("info")
let Operations = getComponent("operations", true)
let Overview = getComponent("overview", true)
let Editor = getComponent("editor", true)
let Footer = getComponent("footer", true)
let Header = getComponent("header", true)
let Container = getComponent("Container")
let Row = getComponent("Row")
let Col = getComponent("Col")
let Button = getComponent("Button")
let showEditorAction = ()=> layoutActions.show("editor", !showEditor)
return (
<Container fullscreen>
<Header/>
{
info && info.size ? <Info version={info.get("version")}
description={info.get("description")}
title={info.get("title")}
url={url}/>
: null
}
<Button onClick={showEditorAction}>{showEditor ? "Hide" : "Show"} Editor</Button>
<Button onClick={specActions.formatIntoYaml}>Format contents</Button>
<Row>
<Col desktop={3} >
<Overview/>
</Col>
<Col hide={!showEditor} keepContents={true} desktop={5} >
<Editor/>
</Col>
<Col desktop={showEditor ? 4 : 9} >
<Operations/>
</Col>
</Row>
<Footer></Footer>
</Container>
)
}
}
XPane.propTypes = {
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired
}

View File

@@ -0,0 +1,91 @@
import React, { PropTypes } from "react"
import ImPropTypes from "react-immutable-proptypes"
const Headers = ( { headers } )=>{
return (
<div>
<h5>Response headers</h5>
<pre>{headers}</pre>
</div>)
}
Headers.propTypes = {
headers: PropTypes.array.isRequired
}
export default class LiveResponse extends React.Component {
static propTypes = {
response: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired
}
render() {
let { request, response, getComponent } = this.props
const Curl = getComponent("curl")
let body = response.get("text")
let status = response.get("status")
let url = response.get("url")
let originalHeaders = response.get("headers")
let headers = originalHeaders && originalHeaders.toJS()
let headersKeys = Object.keys(headers)
let returnObject = headersKeys.map(key => {
return <span className="headerline" key={key}> {key}: {headers[key]} </span>
})
let notDocumented = response.get("notDocumented")
let ResponseBody = getComponent("responseBody")
let contentType = headers && headers["content-type"]
let isError = response.get("error")
return (
<div>
{ request && <Curl request={ request }/> }
<h4>Server response</h4>
<table className="responses-table">
<thead>
<tr className="responses-header">
<td className="col col_header response-col_status">Code</td>
<td className="col col_header response-col_description">Details</td>
</tr>
</thead>
<tbody>
<tr className="response">
<td className="col response-col_status">
{ status }
{
!notDocumented ? null :
<div className="response-undocumented">
<i> Undocumented </i>
</div>
}
</td>
<td className="col response-col_description">
{
!isError ? null : <span>
{`${response.get("name")}: ${response.get("message")}`}
</span>
}
{
!body || isError ? null
: <ResponseBody content={ body }
contentType={ contentType }
url={ url }
headers={ headers }
getComponent={ getComponent }/>
}
{
!headers ? null : <Headers headers={ returnObject }/>
}
</td>
</tr>
</tbody>
</table>
</div>
)
}
static propTypes = {
getComponent: PropTypes.func.isRequired,
request: ImPropTypes.map,
response: ImPropTypes.map
}
}

View File

@@ -0,0 +1,58 @@
import React, { PropTypes } from "react"
export default class ModelExample extends React.Component {
static propTypes = {
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
schema: PropTypes.object.isRequired,
example: PropTypes.any.isRequired,
isExecute: PropTypes.bool
}
constructor(props, context) {
super(props, context)
this.state = {
activeTab: "example"
}
}
activeTab =( e ) => {
let { target : { dataset : { name } } } = e
this.setState({
activeTab: name
})
}
render() {
let { getComponent, specSelectors, schema, example, isExecute } = this.props
const Model = getComponent("model")
return <div>
<ul className="tab">
<li className={ "tabitem" + ( isExecute || this.state.activeTab === "example" ? " active" : "") }>
<a className="tablinks" data-name="example" onClick={ this.activeTab }>Example Value</a>
</li>
<li className={ "tabitem" + ( !isExecute && this.state.activeTab === "model" ? " active" : "") }>
<a className={ "tablinks" + ( isExecute ? " inactive" : "" )} data-name="model" onClick={ this.activeTab }>Model</a>
</li>
</ul>
<div>
{
(isExecute || this.state.activeTab === "example") && example
}
{
!isExecute && this.state.activeTab === "model" && <Model schema={ schema }
getComponent={ getComponent }
specSelectors={ specSelectors }
expandDepth={ 1 } />
}
</div>
</div>
}
}

View File

@@ -0,0 +1,300 @@
import React, { Component, PropTypes } from "react"
import ImPropTypes from "react-immutable-proptypes"
import isObject from "lodash/isObject"
import { List } from "immutable"
const braceOpen = "{"
const braceClose = "}"
const EnumModel = ({ value }) => {
let collapsedContent = <span>Array [ { value.count() } ]</span>
return <span className="prop-enum">
Enum:<br />
<Collapse collapsedContent={ collapsedContent }>
[ { value.join(", ") } ]
</Collapse>
</span>
}
EnumModel.propTypes = {
value: ImPropTypes.iterable
}
class ObjectModel extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
isRef: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number
}
render(){
let { schema, name, isRef, getComponent, depth, ...props } = this.props
let { expandDepth } = this.props
const JumpToPath = getComponent("JumpToPath", true)
let description = schema.get("description")
let properties = schema.get("properties")
let additionalProperties = schema.get("additionalProperties")
let title = schema.get("title") || name
let required = schema.get("required")
const JumpToPathSection = ({ name }) => <span className="model-jump-to-path"><JumpToPath path={`definitions.${name}`} /></span>
let collapsedContent = (<span>
<span>{ braceOpen }</span>...<span>{ braceClose }</span>
{
isRef ? <JumpToPathSection name={ name }/> : ""
}
</span>)
return <span className="model">
{
title && <span className="model-title">
{ isRef && schema.get("$$ref") && <span className="model-hint">{ schema.get("$$ref") }</span> }
<span className="model-title__text">{ title }</span>
</span>
}
<Collapse collapsed={ depth > expandDepth } collapsedContent={ collapsedContent }>
<span className="brace-open object">{ braceOpen }</span>
{
!isRef ? null : <JumpToPathSection name={ name }/>
}
<span className="inner-object">
{
<table className="model" style={{ marginLeft: "2em" }}><tbody>
{
!description ? null : <tr style={{ color: "#999", fontStyle: "italic" }}>
<td>description:</td>
<td>{ description }</td>
</tr>
}
{
!(properties && properties.size) ? null : properties.entrySeq().map(
([key, value]) => {
let isRequired = List.isList(required) && required.contains(key)
let propertyStyle = { verticalAlign: "top", paddingRight: "0.2em" }
if ( isRequired ) {
propertyStyle.fontWeight = "bold"
}
return (<tr key={key}>
<td style={ propertyStyle }>{ key }:</td>
<td style={{ verticalAlign: "top" }}>
<Model key={ `object-${name}-${key}_${value}` } { ...props }
required={ isRequired }
getComponent={ getComponent }
schema={ value }
depth={ depth + 1 } />
</td>
</tr>)
}).toArray()
}
{
!additionalProperties || !additionalProperties.size ? null
: <tr>
<td>{ "< * >:" }</td>
<td>
<Model { ...props } required={ false }
getComponent={ getComponent }
schema={ additionalProperties }
depth={ depth + 1 } />
</td>
</tr>
}
</tbody></table>
}
</span>
<span className="brace-close">{ braceClose }</span>
</Collapse>
</span>
}
}
class Primitive extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
required: PropTypes.bool
}
render(){
let { schema, required } = this.props
if(!schema || !schema.get) {
// don't render if schema isn't correctly formed
return <div></div>
}
let type = schema.get("type")
let format = schema.get("format")
let xml = schema.get("xml")
let enumArray = schema.get("enum")
let description = schema.get("description")
let properties = schema.filter( ( v, key) => ["enum", "type", "format", "$$ref"].indexOf(key) === -1 )
let style = required ? { fontWeight: "bold" } : {}
let propStyle = { color: "#999", fontStyle: "italic" }
return <span className="prop">
<span className="prop-type" style={ style }>{ type }</span> { required && <span style={{ color: "red" }}>*</span>}
{ format && <span className="prop-format">(${format})</span>}
{
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }>
<br />{ key !== "description" && key + ": " }{ String(v) }</span>)
: null
}
{
xml && xml.size ? (<span><br /><span style={ propStyle }>xml:</span>
{
xml.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }><br/>&nbsp;&nbsp;&nbsp;{key}: { String(v) }</span>).toArray()
}
</span>): null
}
{
enumArray && <EnumModel value={ enumArray } />
}
</span>
}
}
class ArrayModel extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
required: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number
}
render(){
let { required, schema, depth, expandDepth } = this.props
let items = schema.get("items")
return <span>
<Collapse collapsed={ depth > expandDepth } collapsedContent="[...]">
[
<span><Model { ...this.props } schema={ items } required={ false }/></span>
]
</Collapse>
{ required && <span style={{ color: "red" }}>*</span>}
</span>
}
}
class Model extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
name: PropTypes.string,
isRef: PropTypes.bool,
required: PropTypes.bool,
expandDepth: PropTypes.number,
depth: PropTypes.number
}
getModelName =( ref )=> {
if ( ref.indexOf("#/definitions/") !== -1 ) {
return ref.replace(/^.*#\/definitions\//, "")
}
}
getRefSchema =( model )=> {
let { specSelectors } = this.props
return specSelectors.findDefinition(model)
}
render () {
let { schema, required, name, isRef } = this.props
let $$ref = schema && schema.get("$$ref")
let modelName = $$ref && this.getModelName( $$ref )
let modelSchema, type
if ( schema && (schema.get("type") || schema.get("properties")) ) {
modelSchema = schema
} else if ( $$ref ) {
modelSchema = this.getRefSchema( modelName )
}
type = modelSchema && modelSchema.get("type")
if ( !type && modelSchema && modelSchema.get("properties") ) {
type = "object"
}
switch(type) {
case "object":
return <ObjectModel className="object" { ...this.props } schema={ modelSchema }
name={ modelName || name }
isRef={ isRef!== undefined ? isRef : !!$$ref }/>
case "array":
return <ArrayModel className="array" { ...this.props } schema={ modelSchema } required={ required } />
case "string":
case "number":
case "integer":
case "boolean":
default:
return <Primitive schema={ modelSchema } required={ required }/>
}
}
}
export default class ModelComponent extends Component {
static propTypes = {
schema: PropTypes.object.isRequired,
name: PropTypes.string,
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
expandDepth: PropTypes.number
}
render(){
let { name, schema } = this.props
let title = schema.get("title") || name
return <div className="model-box">
<Model { ...this.props } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
</div>
}
}
class Collapse extends Component {
static propTypes = {
collapsedContent: PropTypes.any,
collapsed: PropTypes.bool,
children: PropTypes.any
}
static defaultProps = {
collapsedContent: "{...}",
collapsed: true,
}
constructor(props, context) {
super(props, context)
let { collapsed, collapsedContent } = this.props
this.state = {
collapsed: collapsed !== undefined ? collapsed : Collapse.defaultProps.collapsed,
collapsedContent: collapsedContent || Collapse.defaultProps.collapsedContent
}
}
toggleCollapsed=()=>{
this.setState({
collapsed: !this.state.collapsed
})
}
render () {
return (<span>
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
<span className={ "model-toggle" + ( this.state.collapsed ? " collapsed" : "" ) }></span>
</span>
{ this.state.collapsed ? this.state.collapsedContent : this.props.children }
</span>)
}
}

View File

@@ -0,0 +1,42 @@
import React, { Component, PropTypes } from "react"
export default class Models extends Component {
static propTypes = {
getComponent: PropTypes.func,
specSelectors: PropTypes.object
}
render(){
let { specSelectors, getComponent, layoutSelectors, layoutActions } = this.props
let definitions = specSelectors.definitions()
let showModels = layoutSelectors.isShown('models', true)
const Model = getComponent("model")
const Collapse = getComponent("Collapse")
if (!definitions.size) return null
return <section className={ showModels ? "models is-open" : "models"}>
<h4 onClick={() => layoutActions.show('models', !showModels)}>
<span>Models</span>
<svg width="20" height="20">
<use xlinkHref="#large-arrow" />
</svg>
</h4>
<Collapse isOpened={showModels} animated>
{
definitions.entrySeq().map( ( [ name, model ])=>{
return <div className="model-container" key={ `models-section-${name}` }>
<Model name={ name }
schema={ model }
isRef={ true }
getComponent={ getComponent }
specSelectors={ specSelectors }/>
</div>
}).toArray()
}
</Collapse>
</section>
}
}

View File

@@ -0,0 +1,40 @@
import React from "react"
export default class OnlineValidatorBadge extends React.Component {
constructor(props, context) {
super(props, context)
let { specSelectors, getConfigs } = props
let { validatorUrl } = getConfigs()
this.state = {
url: specSelectors.url(),
validatorUrl: validatorUrl
}
}
componentWillReceiveProps(nextProps) {
let { specSelectors, getConfigs } = nextProps
let { validatorUrl } = getConfigs()
this.setState({
url: specSelectors.url(),
validatorUrl: validatorUrl
})
}
render() {
let { getConfigs } = this.props
let { spec } = getConfigs()
if ( typeof spec === "object" && Object.keys(spec).length) return null
if (!this.state.url) {
return null
}
return (<span style={{ float: "right"}}>
<a target="_blank" href={`${ this.state.validatorUrl }/debug?url=${ this.state.url }`}>
<img alt="Online validator badge" src={`${ this.state.validatorUrl }?url=${ this.state.url }`} />
</a>
</span>)
}
}

View File

@@ -0,0 +1,258 @@
import React, { PropTypes } from "react"
import { Map, fromJS } from "immutable"
import shallowCompare from "react-addons-shallow-compare"
import { getList } from "core/utils"
import * as CustomPropTypes from "core/proptypes"
//import "less/opblock"
export default class Operation extends React.Component {
static propTypes = {
path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
operation: PropTypes.object.isRequired,
showSummary: PropTypes.bool,
isShownKey: CustomPropTypes.arrayOrString.isRequired,
jumpToKey: CustomPropTypes.arrayOrString.isRequired,
allowTryItOut: PropTypes.bool,
response: PropTypes.object,
request: PropTypes.object,
getComponent: PropTypes.func.isRequired,
authActions: PropTypes.object,
authSelectors: PropTypes.object,
specActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired
}
static defaultProps = {
showSummary: true,
response: null,
allowTryItOut: true,
}
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)
}
}
shouldComponentUpdate(props, state) {
return shallowCompare(this, props, state)
}
toggleShown =() => {
let { layoutActions, isShownKey } = this.props
layoutActions.show(isShownKey, !this.isShown())
}
isShown =() => {
let { layoutSelectors, isShownKey } = this.props
return layoutSelectors.isShown(isShownKey, false ) // 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 })
}
render() {
let {
isShownKey,
jumpToKey,
path,
method,
operation,
showSummary,
response,
request,
allowTryItOut,
fn,
getComponent,
specActions,
specSelectors,
authActions,
authSelectors,
layoutSelectors,
layoutActions,
} = this.props
let summary = operation.get("summary")
let description = operation.get("description")
let deprecated = operation.get("deprecated")
let externalDocs = operation.get("externalDocs")
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"])
const Responses = getComponent("responses")
const Parameters = getComponent( "parameters" )
const Execute = getComponent( "execute" )
const Clear = getComponent( "clear" )
const AuthorizeOperationBtn = getComponent( "authorizeOperationBtn" )
const JumpToPath = getComponent("JumpToPath", true)
const Collapse = getComponent( "Collapse" )
const Markdown = getComponent( "Markdown" )
const Schemes = getComponent( "schemes" )
// Merge in Live Response
if(response && response.size > 0) {
let notDocumented = !responses.get(String(response.get("status")))
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} >
<div className={`opblock-summary opblock-summary-${method}`} onClick={this.toggleShown} >
<span className="opblock-summary-method">{method.toUpperCase()}</span>
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
<span>{path}</span>
<JumpToPath path={jumpToKey} />
</span>
{ !showSummary ? null :
<div className="opblock-summary-description">
{ summary }
</div>
}
{
(!security || !security.count()) ? null :
<AuthorizeOperationBtn authActions={ authActions }
security={ security }
authSelectors={ authSelectors }/>
}
</div>
<Collapse isOpened={shown} animated>
<div className="opblock-body">
{ deprecated && <h4 className="opblock-title_normal"> Warning: Deprecated</h4>}
{ description &&
<div className="opblock-description-wrapper">
<div className="opblock-description">
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}} source={ description } />
</div>
</div>
}
{
externalDocs && externalDocs.get("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">{ externalDocs.get("description") }</span>
<a className="opblock-external-docs__link" href={ externalDocs.get("url") }>{ externalDocs.get("url") }</a>
</div>
</div> : null
}
<Parameters
parameters={parameters}
onChangeKey={onChangeKey}
onTryoutClick = { this.onTryoutClick }
onCancelClick = { this.onCancelClick }
tryItOutEnabled = { tryItOutEnabled }
allowTryItOut={allowTryItOut}
fn={fn}
getComponent={ getComponent }
specActions={ specActions }
specSelectors={ specSelectors }
pathMethod={ [path, method] }
/>
{!tryItOutEnabled || !allowTryItOut ? null : schemes && schemes.size ? <Schemes schemes={ schemes }
path={ path }
method={ method }
specActions={ specActions }/>
: null
}
<div className={(!tryItOutEnabled || !response || !allowTryItOut) ? "execute-wrapper" : "btn-group"}>
{ !tryItOutEnabled || !allowTryItOut ? null :
<Execute
getComponent={getComponent}
operation={ operation }
specActions={ specActions }
specSelectors={ specSelectors }
path={ path }
method={ method }
onExecute={ this.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}
{ !responses ? null :
<Responses
responses={ responses }
request={ request }
tryItOutResponse={ response }
getComponent={ getComponent }
specSelectors={ specSelectors }
specActions={ specActions }
produces={ produces }
producesValue={ operation.get("produces_value") }
pathMethod={ [path, method] }
fn={fn} />
}
</div>
</Collapse>
</div>
)
}
}

View File

@@ -0,0 +1,133 @@
import React, { PropTypes } from "react"
import {presets} from "react-motion"
export default class Operations extends React.Component {
static propTypes = {
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
layoutSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
authActions: PropTypes.object.isRequired,
authSelectors: PropTypes.object.isRequired,
};
static defaultProps = {
};
render() {
let {
specSelectors,
specActions,
getComponent,
layoutSelectors,
layoutActions,
authActions,
authSelectors,
fn
} = this.props
let taggedOps = specSelectors.taggedOperations()
const Operation = getComponent("operation")
const Collapse = getComponent("Collapse")
const Schemes = getComponent("schemes")
let showSummary = layoutSelectors.showSummary()
return (
<div>
{
taggedOps.map( (tagObj, tag) => {
let operations = tagObj.get("operations")
let tagDescription = tagObj.getIn(["tagDetails", "description"], null)
let isShownKey = ["operations-tag", tag]
let showTag = layoutSelectors.isShown(isShownKey, true)
return (
<div className={showTag ? "opblock-tag-section is-open" : "opblock-tag-section"} key={"operation-" + tag}>
<h4 className={!tagDescription ? "opblock-tag no-desc" : "opblock-tag" }>
<span onClick={() => layoutActions.show(isShownKey, !showTag)}>{tag}</span>
{ !tagDescription ? null :
<small onClick={() => layoutActions.show(isShownKey, !showTag)} >
{ tagDescription }
</small>
}
<button className="expand-methods" title="Expand all methods">
<svg className="expand" width="20" height="20">
<use xlinkHref="#expand" />
</svg>
</button>
<button className="expand-operation" title="Expand operation" onClick={() => layoutActions.show(isShownKey, !showTag)}>
<svg className="arrow" width="20" height="20">
<use xlinkHref={showTag ? "#large-arrow-down" : "#large-arrow"} />
</svg>
</button>
</h4>
<Collapse isOpened={showTag}>
{
operations.map( op => {
const isShownKey = ["operations", op.get("id"), tag]
const path = op.get("path", "")
const method = op.get("method", "")
const jumpToKey = `paths.${path}.${method}`
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"))
return <Operation
{...op.toObject()}
isShownKey={isShownKey}
jumpToKey={jumpToKey}
showSummary={showSummary}
key={isShownKey}
response={ response }
request={ request }
allowTryItOut={allowTryItOut}
specActions={ specActions }
specSelectors={ specSelectors }
layoutActions={ layoutActions }
layoutSelectors={ layoutSelectors }
authActions={ authActions }
authSelectors={ authSelectors }
getComponent={ getComponent }
fn={fn}
/>
}).toArray()
}
</Collapse>
</div>
)
}).toArray()
}
{ taggedOps.size < 1 ? <h3> No operations defined in spec! </h3> : null }
</div>
)
}
}
Operations.propTypes = {
layoutActions: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
fn: PropTypes.object.isRequired
}

View File

@@ -0,0 +1,119 @@
import React, { PropTypes } from "react"
import { Link } from "core/components/layout-utils"
export default class Overview extends React.Component {
constructor(...args) {
super(...args)
this.setTagShown = this._setTagShown.bind(this)
}
_setTagShown(showTagId, shown) {
this.props.layoutActions.show(showTagId, shown)
}
showOp(key, shown) {
let { layoutActions } = this.props
layoutActions.show(key, shown)
}
render() {
let { specSelectors, layoutSelectors, layoutActions, getComponent } = this.props
let taggedOps = specSelectors.taggedOperations()
const Collapse = getComponent("Collapse")
return (
<div>
<h4 className="overview-title">Overview</h4>
{
taggedOps.map( (tagObj, tag) => {
let operations = tagObj.get("operations")
let tagDetails = tagObj.get("tagDetails")
let showTagId = ["overview-tags", tag]
let showTag = layoutSelectors.isShown(showTagId, true)
let toggleShow = ()=> layoutActions.show(showTagId, !showTag)
return (
<div key={"overview-"+tag}>
<h4 onClick={toggleShow} className="link overview-tag"> {showTag ? "-" : "+"}{tag}</h4>
<Collapse isOpened={showTag} animated>
{
operations.map( op => {
let { path, method, operation, id } = op.toObject() // toObject is shallow
let showOpIdPrefix = "operations"
let showOpId = id
let shown = layoutSelectors.isShown([showOpIdPrefix, showOpId])
return <OperationLink key={id}
path={path}
method={method}
id={path + "-" + method}
shown={shown}
showOpId={showOpId}
showOpIdPrefix={showOpIdPrefix}
href={`#operation-${showOpId}`}
onClick={layoutActions.show} />
}).toArray()
}
</Collapse>
</div>
)
}).toArray()
}
{ taggedOps.size < 1 && <h3> No operations defined in spec! </h3> }
</div>
)
}
}
Overview.propTypes = {
layoutSelectors: PropTypes.object.isRequired,
specSelectors: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired
}
export class OperationLink extends React.Component {
constructor(props) {
super(props)
this.onClick = this._onClick.bind(this)
}
_onClick() {
let { showOpId, showOpIdPrefix, onClick, shown } = this.props
onClick([showOpIdPrefix, showOpId], !shown)
}
render() {
let { id, method, shown, href } = this.props
return (
<Link href={ href } style={{fontWeight: shown ? "bold" : "normal"}} onClick={this.onClick} className="block opblock-link">
<div>
<small className={`bold-label-${method}`}>{method.toUpperCase()}</small>
<span className="bold-label" >{id}</span>
</div>
</Link>
)
}
}
OperationLink.propTypes = {
href: PropTypes.string,
onClick: PropTypes.func,
id: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
shown: PropTypes.bool.isRequired,
showOpId: PropTypes.string.isRequired,
showOpIdPrefix: PropTypes.string.isRequired
}

View File

@@ -0,0 +1,141 @@
import React, { Component, PropTypes } from "react"
import shallowCompare from "react-addons-shallow-compare"
import { Set, fromJS, List } from "immutable"
import { getSampleSchema } from "core/utils"
const NOOP = Function.prototype
export default class ParamBody extends Component {
static propTypes = {
param: PropTypes.object,
onChange: PropTypes.func,
onChangeConsumes: PropTypes.func,
consumes: PropTypes.object,
consumesValue: PropTypes.string,
fn: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
isExecute: PropTypes.bool,
specSelectors: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired
};
static defaultProp = {
consumes: fromJS(["application/json"]),
param: fromJS({}),
onChange: NOOP,
onChangeConsumes: NOOP,
};
constructor(props, context) {
super(props, context)
this.state = {
isEditBox: false,
value: ""
}
}
componentDidMount() {
this.updateValues.call(this, this.props)
}
shouldComponentUpdate(props, state) {
return shallowCompare(this, props, state)
}
componentWillReceiveProps(nextProps) {
this.updateValues.call(this, nextProps)
}
updateValues = (props) => {
let { specSelectors, pathMethod, param, isExecute, consumesValue="", onChangeConsumes } = props
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name")) : {}
let isXml = /xml/i.test(consumesValue)
let paramValue = isXml ? parameter.get("value_xml") : parameter.get("value")
if ( paramValue ) {
this.setState({ value: paramValue })
this.onChange(paramValue, {isXml: isXml, isEditBox: isExecute})
} else {
if (isXml) {
this.onChange(this.sample("xml"), {isXml: isXml, isEditBox: isExecute})
} else {
this.onChange(this.sample(), {isEditBox: isExecute})
}
}
}
sample = (xml) => {
let { param, fn:{inferSchema} } = this.props
let schema = inferSchema(param.toJS())
return getSampleSchema(schema, xml)
}
onChange = (value, { isEditBox, isXml }) => {
this.setState({value, isEditBox})
this._onChange(value, isXml)
}
_onChange = (val, isXml) => { (this.props.onChange || NOOP)(this.props.param, val, isXml) }
handleOnChange = e => {
let {consumesValue} = this.props
this.onChange(e.target.value.trim(), {isXml: /xml/i.test(consumesValue)})
}
toggleIsEditBox = () => this.setState( state => ({isEditBox: !state.isEditBox}))
render() {
let {
onChangeConsumes,
param,
isExecute,
specSelectors,
pathMethod,
getComponent,
} = this.props
const Button = getComponent("Button")
const TextArea = getComponent("TextArea")
const HighlightCode = getComponent("highlightCode")
const ContentType = getComponent("contentType")
// for domains where specSelectors not passed
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name")) : param
let errors = parameter.get("errors", List())
let consumesValue = specSelectors.contentTypeValues(pathMethod).get("requestContentType")
let consumes = this.props.consumes && this.props.consumes.size ? this.props.consumes : ParamBody.defaultProp.consumes
let { value, isEditBox } = this.state
return (
<div className="body-param">
{
isEditBox && isExecute
? <TextArea className={ "body-param__text" + ( errors.count() ? " invalid" : "")} value={value} onChange={ this.handleOnChange }/>
: (value && <HighlightCode className="body-param__example"
value={ value }/>)
}
<div className="body-param-options">
{
!isExecute ? null
: <div className="body-param-edit">
<Button className={isEditBox ? "btn cancel body-param__example-edit" : "btn edit body-param__example-edit"}
onClick={this.toggleIsEditBox}>{ isEditBox ? "Cancel" : "Edit"}
</Button>
</div>
}
<label htmlFor="">
<span>Parameter content type</span>
<ContentType value={ consumesValue } contentTypes={ consumes } onChange={onChangeConsumes} className="body-param-content-type" />
</label>
</div>
</div>
)
}
}

View File

@@ -0,0 +1,119 @@
import React, { Component, PropTypes } from "react"
import win from "core/window"
export default class ParameterRow extends Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
param: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
fn: PropTypes.object.isRequired,
isExecute: PropTypes.bool,
onChangeConsumes: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired
}
constructor(props, context) {
super(props, context)
let { specSelectors, pathMethod, param } = props
let defaultValue = param.get("default")
let parameter = specSelectors.getParameter(pathMethod, param.get("name"))
let value = parameter ? parameter.get("value") : ""
if ( defaultValue !== undefined && value === undefined ) {
this.onChangeWrapper(defaultValue)
}
}
componentWillReceiveProps(props) {
let { specSelectors, pathMethod, param } = props
let defaultValue = param.get("default")
let parameter = specSelectors.getParameter(pathMethod, param.get("name"))
let value = parameter ? parameter.get("value") : ""
if ( defaultValue !== undefined && value === undefined ) {
this.onChangeWrapper(defaultValue)
}
}
onChangeWrapper = (value) => {
let { onChange, param } = this.props
return onChange(param, value)
}
render() {
let {param, onChange, getComponent, isExecute, fn, onChangeConsumes, specSelectors, pathMethod} = this.props
// const onChangeWrapper = (value) => onChange(param, value)
const JsonSchemaForm = getComponent("JsonSchemaForm")
const ParamBody = getComponent("ParamBody")
let inType = param.get("in")
let bodyParam = inType !== "body" ? null
: <ParamBody getComponent={getComponent}
fn={fn}
param={param}
consumes={ specSelectors.operationConsumes(pathMethod) }
consumesValue={ specSelectors.contentTypeValues(pathMethod).get("requestContentType") }
onChange={onChange}
onChangeConsumes={onChangeConsumes}
isExecute={ isExecute }
specSelectors={ specSelectors }
pathMethod={ pathMethod }
/>
const ModelExample = getComponent("modelExample")
const Markdown = getComponent("Markdown")
let schema = param.get("schema")
let isFormData = inType === "formData"
let isFormDataSupported = "FormData" in win
let required = param.get("required")
let itemType = param.getIn(["items", "type"])
let parameter = specSelectors.getParameter(pathMethod, param.get("name"))
let value = parameter ? parameter.get("value") : ""
return (
<tr>
<td className="col parameters-col_name">
<div className={required ? "parameter__name required" : "parameter__name"}>
{ param.get("name") }
{ !required ? null : <span style={{color: "red"}}>&nbsp;*</span> }
</div>
<div className="parаmeter__type">{ param.get("type") } { itemType && `[${itemType}]` }</div>
<div className="parameter__in">({ param.get("in") })</div>
</td>
<td className="col parameters-col_description">
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}}
source={ param.get("description") }/>
{(isFormData && !isFormDataSupported) && <div>Error: your browser does not support FormData</div>}
{ bodyParam || !isExecute ? null
: <JsonSchemaForm fn={fn}
getComponent={getComponent}
value={ value }
required={ required }
description={param.get("description") ? `${param.get("name")} - ${param.get("description")}` : `${param.get("name")}`}
onChange={ this.onChangeWrapper }
schema={ param }/>
}
{
bodyParam && schema ? <ModelExample getComponent={ getComponent }
isExecute={ isExecute }
specSelectors={ specSelectors }
schema={ schema }
example={ bodyParam }/>
: null
}
</td>
</tr>
)
}
}

View File

@@ -0,0 +1,109 @@
import React, { Component, PropTypes } from "react"
import ImPropTypes from "react-immutable-proptypes"
import Im, { fromJS } from "immutable"
// More readable, just iterate over maps, only
const eachMap = (iterable, fn) => iterable.valueSeq().filter(Im.Map.isMap).map(fn)
export default class Parameters extends Component {
static propTypes = {
parameters: ImPropTypes.list.isRequired,
specActions: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired,
tryItOutEnabled: PropTypes.bool,
allowTryItOut: PropTypes.bool,
onTryoutClick: PropTypes.func,
onCancelClick: PropTypes.func,
onChangeKey: PropTypes.array,
pathMethod: PropTypes.array.isRequired
}
static defaultProps = {
onTryoutClick: Function.prototype,
onCancelClick: Function.prototype,
tryItOutEnabled: false,
allowTryItOut: true,
onChangeKey: [],
}
onChange = ( param, value, isXml ) => {
let {
specActions: { changeParam },
onChangeKey,
} = this.props
changeParam( onChangeKey, param.get("name"), value, isXml)
}
onChangeConsumesWrapper = ( val ) => {
let {
specActions: { changeConsumesValue },
onChangeKey
} = this.props
changeConsumesValue(onChangeKey, val)
}
render(){
let {
onTryoutClick,
onCancelClick,
parameters,
allowTryItOut,
tryItOutEnabled,
fn,
getComponent,
specSelectors,
pathMethod
} = this.props
const ParameterRow = getComponent("parameterRow")
const TryItOutButton = getComponent("TryItOutButton")
const isExecute = tryItOutEnabled && allowTryItOut
return (
<div className="opblock-section">
<div className="opblock-section-header">
<h4 className="opblock-title">Parameters</h4>
{ allowTryItOut ? (
<TryItOutButton enabled={ tryItOutEnabled } onCancelClick={ onCancelClick } onTryoutClick={ onTryoutClick } />
) : null }
</div>
{ !parameters.count() ? <div className="opblock-description-wrapper"><p>No parameters</p></div> :
<div className="table-container">
<table className="parameters">
<thead>
<tr>
<th className="col col_header parameters-col_name">Name</th>
<th className="col col_header parameters-col_description">Description</th>
</tr>
</thead>
<tbody>
{
eachMap(parameters, (parameter, k) => (
<ParameterRow fn={ fn }
getComponent={ getComponent }
param={ parameter }
key={ parameter.get( "name" ) }
onChange={ this.onChange }
onChangeConsumes={this.onChangeConsumesWrapper}
specSelectors={ specSelectors }
pathMethod={ pathMethod }
isExecute={ isExecute }/>
)).toArray()
}
</tbody>
</table>
</div>
}
</div>
)
}
}

View File

@@ -0,0 +1,95 @@
import React, { PropTypes } from "react"
import { formatXml } from "core/utils"
import lowerCase from "lodash/lowerCase"
export default class ResponseBody extends React.Component {
static propTypes = {
content: PropTypes.any.isRequired,
contentType: PropTypes.string.isRequired,
getComponent: PropTypes.func.isRequired,
headers: PropTypes.object,
url: PropTypes.string
}
render() {
let { content, contentType, url, headers={}, getComponent } = this.props
const HighlightCode = getComponent("highlightCode")
let body, bodyEl
url = url || ""
// JSON
if (/json/i.test(contentType)) {
try {
body = JSON.stringify(JSON.parse(content), null, " ")
} catch (error) {
body = "can't parse JSON. Raw result:\n\n" + content
}
bodyEl = <HighlightCode value={ body } />
// XML
} else if (/xml/i.test(contentType)) {
body = formatXml(content)
bodyEl = <HighlightCode value={ body } />
// HTML or Plain Text
} else if (lowerCase(contentType) === "text/html" || /text\/plain/.test(contentType)) {
bodyEl = <HighlightCode value={ content } />
// Image
} else if (/^image\//i.test(contentType)) {
bodyEl = <img src={ url } />
// Audio
} else if (/^audio\//i.test(contentType)) {
bodyEl = <pre><audio controls><source src={ url } type={ contentType } /></audio></pre>
// Download
} else if (
/^application\/octet-stream/i.test(contentType) ||
headers["Content-Disposition"] && (/attachment/i).test(headers["Content-Disposition"]) ||
headers["content-disposition"] && (/attachment/i).test(headers["content-disposition"]) ||
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) {
let type = contentType || "text/html"
let blob = (content instanceof Blob) ? content : new Blob([content], {type: type})
let href = window.URL.createObjectURL(blob)
let fileName = url.substr(url.lastIndexOf("/") + 1)
let download = [type, fileName, href].join(":")
// Use filename from response header
let disposition = headers["content-disposition"] || headers["Content-Disposition"]
if (typeof disposition !== "undefined") {
let responseFilename = /filename=([^;]*);?/i.exec(disposition)
if (responseFilename !== null && responseFilename.length > 1) {
download = responseFilename[1]
}
}
bodyEl = <div><a href={ href } download={ download }>{ "Download file" }</a></div>
} else {
bodyEl = <pre>Download headers detected but your browser does not support downloading binary via XHR (Blob).</pre>
}
// Anything else (CORS)
} else if (typeof content === "string") {
bodyEl = <HighlightCode value={ content } />
} else {
bodyEl = <div>Unknown response type</div>
}
return ( !bodyEl ? null : <div>
<h5>Response body</h5>
{ bodyEl }
</div>
)
}
}

View File

@@ -0,0 +1,91 @@
import React, { PropTypes } from "react"
import { fromJS } from 'immutable'
import { getSampleSchema } from "core/utils"
const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
if ( examples && examples.size ) {
return examples.entrySeq().map( ([ key, example ]) => {
return (<div key={ key }>
<h5>{ key }</h5>
<HighlightCode className="example" value={ example } />
</div>)
}).toArray()
}
if ( sampleResponse ) { return <div>
<HighlightCode className="example" value={ sampleResponse } />
</div>
}
return null
}
export default class Response extends React.Component {
static propTypes = {
code: PropTypes.string.isRequired,
response: PropTypes.object,
className: PropTypes.string,
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired,
contentType: PropTypes.string
}
static defaultProps = {
response: fromJS({}),
};
render() {
let {
code,
response,
className,
fn,
getComponent,
specSelectors,
contentType
} = this.props
let { inferSchema } = fn
let schema = inferSchema(response.toJS())
let headers = response.get("headers")
let examples = response.get("examples")
const Headers = getComponent("headers")
const HighlightCode = getComponent("highlightCode")
const ModelExample = getComponent("modelExample")
const Markdown = getComponent( "Markdown" )
let sampleResponse = schema ? getSampleSchema(schema, contentType, { includeReadOnly: true }) : null
let example = getExampleComponent( sampleResponse, examples, HighlightCode )
return (
<tr className={ "response " + ( className || "") }>
<td className="col response-col_status">
{ code }
</td>
<td className="col response-col_description">
<div className="response-col_description__inner">
<Markdown options={{html: true, typographer: true, linkify: true, linkTarget: "_blank"}} source={ response.get( "description" ) } />
</div>
{ example ? (
<ModelExample
getComponent={ getComponent }
specSelectors={ specSelectors }
schema={ fromJS(schema) }
example={ example }/>
) : null}
{ headers ? (
<Headers headers={ headers }/>
) : null}
</td>
</tr>
)
}
}

View File

@@ -0,0 +1,93 @@
import React, { PropTypes } from "react"
import { fromJS } from "immutable"
import { defaultStatusCode } 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,
producesValue: PropTypes.any,
getComponent: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
fn: PropTypes.object.isRequired
}
static defaultProps = {
request: null,
tryItOutResponse: null,
produces: fromJS(["application/json"])
}
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
render() {
let { responses, request, tryItOutResponse, getComponent, specSelectors, fn, producesValue } = this.props
let defaultCode = defaultStatusCode( responses )
const ContentType = getComponent( "contentType" )
const LiveResponse = getComponent( "liveResponse" )
const Response = getComponent( "response" )
let produces = this.props.produces && this.props.produces.size ? this.props.produces : Responses.defaultProps.produces
return (
<div className="responses-wrapper">
<div className="opblock-section-header">
<h4>Responses</h4>
<label>
<span>Response content type</span>
<ContentType value={producesValue}
onChange={this.onChangeProducesWrapper}
contentTypes={produces}
className="execute-content-type"/>
</label>
</div>
<div className="responses-inner">
{
!tryItOutResponse ? null
: <div>
<LiveResponse request={ request }
response={ tryItOutResponse }
getComponent={ getComponent } />
<h4>Responses</h4>
</div>
}
<table className="responses-table">
<thead>
<tr className="responses-header">
<td className="col col_header response-col_status">Code</td>
<td className="col col_header response-col_description">Description</td>
</tr>
</thead>
<tbody>
{
responses.entrySeq().map( ([code, response]) => {
let className = tryItOutResponse && tryItOutResponse.get("status") == code ? "response_current" : ""
return (
<Response key={ code }
isDefault={defaultCode === code}
fn={fn}
className={ className }
code={ code }
response={ response }
specSelectors={ specSelectors }
contentType={ producesValue }
getComponent={ getComponent }/>
)
}).toArray()
}
</tbody>
</table>
</div>
</div>
)
}
}

View File

@@ -0,0 +1,45 @@
import React, { PropTypes } from "react"
export default class Schemes extends React.Component {
static propTypes = {
specActions: PropTypes.object.isRequired,
schemes: PropTypes.object.isRequired,
path: PropTypes.string,
method: PropTypes.string
}
componentWillMount() {
let { schemes } = this.props
//fire 'change' event to set default 'value' of select
this.setScheme(schemes.first())
}
onChange =( e ) => {
let { path, method, specActions } = this.props
this.setScheme( e.target.value )
}
setScheme =( value ) => {
let { path, method, specActions } = this.props
specActions.setScheme( value, path, method )
}
render() {
let { schemes } = this.props
return (
<label htmlFor="schemes">
<span>Schemes</span>
<select onChange={ this.onChange }>
{ schemes.valueSeq().map(
( scheme ) => <option value={ scheme } key={ scheme }>{ scheme }</option>
).toArray()}
</select>
</label>
)
}
}

View File

View File

@@ -0,0 +1,27 @@
import React, { PropTypes } from "react"
export default class TryItOutButton extends React.Component {
static propTypes = {
onTryoutClick: PropTypes.func,
enabled: PropTypes.bool, // Try it out is enabled, ie: the user has access to the form
};
static defaultProps = {
onTryoutClick: Function.prototype,
enabled: false,
};
render() {
const { onTryoutClick, onCancelClick, enabled } = this.props
return (
<div className="try-out">
{
enabled ? <button className="btn try-out__btn cancel" onClick={ onTryoutClick }>Cancel</button>
: <button className="btn try-out__btn" onClick={ onCancelClick }>Try it out </button>
}
</div>
)
}
}