in with the new
This commit is contained in:
27
src/core/components/app.jsx
Normal file
27
src/core/components/app.jsx
Normal 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 = {
|
||||
}
|
||||
82
src/core/components/auth/api-key-auth.jsx
Normal file
82
src/core/components/auth/api-key-auth.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
59
src/core/components/auth/authorization-popup.jsx
Normal file
59
src/core/components/auth/authorization-popup.jsx
Normal 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,
|
||||
}
|
||||
}
|
||||
42
src/core/components/auth/authorize-btn.jsx
Normal file
42
src/core/components/auth/authorize-btn.jsx
Normal 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,
|
||||
}
|
||||
}
|
||||
34
src/core/components/auth/authorize-operation-btn.jsx
Normal file
34
src/core/components/auth/authorize-operation-btn.jsx
Normal 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
|
||||
}
|
||||
}
|
||||
138
src/core/components/auth/auths.jsx
Normal file
138
src/core/components/auth/auths.jsx
Normal 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
|
||||
}
|
||||
}
|
||||
97
src/core/components/auth/basic-auth.jsx
Normal file
97
src/core/components/auth/basic-auth.jsx
Normal 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
|
||||
}
|
||||
}
|
||||
23
src/core/components/auth/error.jsx
Normal file
23
src/core/components/auth/error.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
218
src/core/components/auth/oauth2.jsx
Normal file
218
src/core/components/auth/oauth2.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
24
src/core/components/clear.jsx
Normal file
24
src/core/components/clear.jsx
Normal 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,
|
||||
}
|
||||
}
|
||||
45
src/core/components/content-type.jsx
Normal file
45
src/core/components/content-type.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
28
src/core/components/curl.jsx
Normal file
28
src/core/components/curl.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
51
src/core/components/debug.jsx
Normal file
51
src/core/components/debug.jsx
Normal 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
|
||||
}
|
||||
|
||||
113
src/core/components/errors.jsx
Normal file
113
src/core/components/errors.jsx
Normal 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
|
||||
}
|
||||
41
src/core/components/execute.jsx
Normal file
41
src/core/components/execute.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
9
src/core/components/footer.jsx
Normal file
9
src/core/components/footer.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from "react"
|
||||
|
||||
export default class Footer extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div className="footer"></div>
|
||||
)
|
||||
}
|
||||
}
|
||||
46
src/core/components/headers.jsx
Normal file
46
src/core/components/headers.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
24
src/core/components/highlight-code.jsx
Normal file
24
src/core/components/highlight-code.jsx
Normal 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>
|
||||
}
|
||||
}
|
||||
128
src/core/components/info.jsx
Normal file
128
src/core/components/info.jsx
Normal 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
|
||||
}
|
||||
252
src/core/components/layout-utils.jsx
Normal file
252
src/core/components/layout-utils.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
72
src/core/components/layouts/xpane.jsx
Normal file
72
src/core/components/layouts/xpane.jsx
Normal 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
|
||||
}
|
||||
|
||||
|
||||
91
src/core/components/live-response.jsx
Normal file
91
src/core/components/live-response.jsx
Normal 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
|
||||
}
|
||||
}
|
||||
58
src/core/components/model-example.jsx
Normal file
58
src/core/components/model-example.jsx
Normal 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>
|
||||
}
|
||||
|
||||
}
|
||||
300
src/core/components/model.jsx
Normal file
300
src/core/components/model.jsx
Normal 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/> {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>)
|
||||
}
|
||||
}
|
||||
42
src/core/components/models.jsx
Normal file
42
src/core/components/models.jsx
Normal 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>
|
||||
}
|
||||
}
|
||||
40
src/core/components/online-validator-badge.jsx
Normal file
40
src/core/components/online-validator-badge.jsx
Normal 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>)
|
||||
}
|
||||
}
|
||||
258
src/core/components/operation.jsx
Normal file
258
src/core/components/operation.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
133
src/core/components/operations.jsx
Normal file
133
src/core/components/operations.jsx
Normal 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
|
||||
}
|
||||
119
src/core/components/overview.jsx
Normal file
119
src/core/components/overview.jsx
Normal 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
|
||||
}
|
||||
141
src/core/components/param-body.jsx
Normal file
141
src/core/components/param-body.jsx
Normal 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>
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
119
src/core/components/parameter-row.jsx
Normal file
119
src/core/components/parameter-row.jsx
Normal 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"}}> *</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>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
109
src/core/components/parameters.jsx
Normal file
109
src/core/components/parameters.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
95
src/core/components/response-body.jsx
Normal file
95
src/core/components/response-body.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
91
src/core/components/response.jsx
Normal file
91
src/core/components/response.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
93
src/core/components/responses.jsx
Normal file
93
src/core/components/responses.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
45
src/core/components/schemes.jsx
Normal file
45
src/core/components/schemes.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
0
src/core/components/system-wrapper.jsx
Normal file
0
src/core/components/system-wrapper.jsx
Normal file
27
src/core/components/try-it-out-button.jsx
Normal file
27
src/core/components/try-it-out-button.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user