Merge branch 'master' of github.com:swagger-api/swagger-ui into ft/docs
This commit is contained in:
@@ -7,6 +7,7 @@ export default class ArrayModel extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
required: PropTypes.bool,
|
||||
@@ -15,7 +16,7 @@ export default class ArrayModel extends Component {
|
||||
}
|
||||
|
||||
render(){
|
||||
let { getComponent, schema, depth, expandDepth, name } = this.props
|
||||
let { getComponent, getConfigs, schema, depth, expandDepth, name } = this.props
|
||||
let description = schema.get("description")
|
||||
let items = schema.get("items")
|
||||
let title = schema.get("title") || name
|
||||
@@ -24,30 +25,29 @@ export default class ArrayModel extends Component {
|
||||
const Markdown = getComponent("Markdown")
|
||||
const ModelCollapse = getComponent("ModelCollapse")
|
||||
const Model = getComponent("Model")
|
||||
const Property = getComponent("Property")
|
||||
|
||||
const titleEl = title &&
|
||||
<span className="model-title">
|
||||
<span className="model-title__text">{ title }</span>
|
||||
</span>
|
||||
|
||||
/*
|
||||
/*
|
||||
Note: we set `name={null}` in <Model> below because we don't want
|
||||
the name of the current Model passed (and displayed) as the name of the array element Model
|
||||
*/
|
||||
*/
|
||||
|
||||
return <span className="model">
|
||||
<ModelCollapse title={titleEl} collapsed={ depth > expandDepth } collapsedContent="[...]">
|
||||
[
|
||||
{
|
||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }>
|
||||
<br />{ key }: { String(v) }</span>)
|
||||
: null
|
||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
|
||||
}
|
||||
{
|
||||
!description ? null :
|
||||
<Markdown source={ description } />
|
||||
}
|
||||
<span><Model { ...this.props } name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
|
||||
<span><Model { ...this.props } getConfigs={ getConfigs } name={null} schema={ items } required={ false } depth={ depth + 1 } /></span>
|
||||
]
|
||||
</ModelCollapse>
|
||||
</span>
|
||||
|
||||
@@ -51,7 +51,11 @@ export default class ApiKeyAuth extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4>Api key authorization<JumpToPath path={[ "securityDefinitions", name ]} /></h4>
|
||||
<h4>
|
||||
<code>{ name || schema.get("name") }</code>
|
||||
(apiKey)
|
||||
<JumpToPath path={[ "securityDefinitions", name ]} />
|
||||
</h4>
|
||||
{ value && <h6>Authorized</h6>}
|
||||
<Row>
|
||||
<Markdown source={ schema.get("description") } />
|
||||
|
||||
62
src/core/components/auth/auth-item.jsx
Normal file
62
src/core/components/auth/auth-item.jsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
export default class Auths extends React.Component {
|
||||
static propTypes = {
|
||||
schema: ImPropTypes.orderedMap.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onAuthChange: PropTypes.func.isRequired,
|
||||
authorized: ImPropTypes.orderedMap.isRequired
|
||||
}
|
||||
|
||||
render() {
|
||||
let {
|
||||
schema,
|
||||
name,
|
||||
getComponent,
|
||||
onAuthChange,
|
||||
authorized,
|
||||
errSelectors
|
||||
} = this.props
|
||||
const ApiKeyAuth = getComponent("apiKeyAuth")
|
||||
const BasicAuth = getComponent("basicAuth")
|
||||
|
||||
let authEl
|
||||
|
||||
const type = schema.get("type")
|
||||
|
||||
switch(type) {
|
||||
case "apiKey": authEl = <ApiKeyAuth key={ name }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
errSelectors={ errSelectors }
|
||||
authorized={ authorized }
|
||||
getComponent={ getComponent }
|
||||
onChange={ onAuthChange } />
|
||||
break
|
||||
case "basic": authEl = <BasicAuth key={ name }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
errSelectors={ errSelectors }
|
||||
authorized={ authorized }
|
||||
getComponent={ getComponent }
|
||||
onChange={ onAuthChange } />
|
||||
break
|
||||
default: authEl = <div key={ name }>Unknown security definition type { type }</div>
|
||||
}
|
||||
|
||||
return (<div key={`${name}-jump`}>
|
||||
{ authEl }
|
||||
</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
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,23 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
export default class AuthorizeOperationBtn extends React.Component {
|
||||
static propTypes = {
|
||||
isAuthorized: PropTypes.bool.isRequired,
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
|
||||
onClick =(e) => {
|
||||
e.stopPropagation()
|
||||
let { onClick } = this.props
|
||||
|
||||
let { security, authActions, authSelectors } = this.props
|
||||
let definitions = authSelectors.getDefinitionsByNames(security)
|
||||
|
||||
authActions.showDefinitions(definitions)
|
||||
if(onClick) {
|
||||
onClick()
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let { security, authSelectors } = this.props
|
||||
|
||||
let isAuthorized = authSelectors.isAuthorized(security)
|
||||
|
||||
if(isAuthorized === null) {
|
||||
return null
|
||||
}
|
||||
let { isAuthorized } = this.props
|
||||
|
||||
return (
|
||||
<button className={isAuthorized ? "authorization__btn locked" : "authorization__btn unlocked"} onClick={ this.onClick }>
|
||||
@@ -30,10 +28,4 @@ export default class AuthorizeOperationBtn extends React.Component {
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
authSelectors: PropTypes.object.isRequired,
|
||||
authActions: PropTypes.object.isRequired,
|
||||
security: ImPropTypes.iterable.isRequired
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ export default class Auths extends React.Component {
|
||||
e.preventDefault()
|
||||
|
||||
let { authActions } = this.props
|
||||
|
||||
authActions.authorize(this.state)
|
||||
}
|
||||
|
||||
@@ -42,10 +41,16 @@ export default class Auths extends React.Component {
|
||||
authActions.logout(auths)
|
||||
}
|
||||
|
||||
close =(e) => {
|
||||
e.preventDefault()
|
||||
let { authActions } = this.props
|
||||
|
||||
authActions.showDefinitions(false)
|
||||
}
|
||||
|
||||
render() {
|
||||
let { definitions, getComponent, authSelectors, errSelectors } = this.props
|
||||
const ApiKeyAuth = getComponent("apiKeyAuth")
|
||||
const BasicAuth = getComponent("basicAuth")
|
||||
const AuthItem = getComponent("AuthItem")
|
||||
const Oauth2 = getComponent("oauth2", true)
|
||||
const Button = getComponent("Button")
|
||||
|
||||
@@ -64,36 +69,19 @@ export default class Auths extends React.Component {
|
||||
!!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>)
|
||||
|
||||
return <AuthItem
|
||||
key={name}
|
||||
schema={schema}
|
||||
name={name}
|
||||
getComponent={getComponent}
|
||||
onAuthChange={this.onAuthChange}
|
||||
authorized={authorized}
|
||||
errSelectors={errSelectors}
|
||||
/>
|
||||
}).toArray()
|
||||
}
|
||||
<div className="auth-btn-wrapper">
|
||||
<Button className="btn modal-btn auth btn-done" onClick={ this.close }>Done</Button>
|
||||
{
|
||||
nonOauthDefinitions.size === authorizedAuth.size ? <Button className="btn modal-btn auth" onClick={ this.logoutClick }>Logout</Button>
|
||||
: <Button type="submit" className="btn modal-btn auth authorize">Authorize</Button>
|
||||
|
||||
@@ -2,11 +2,6 @@ import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
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,
|
||||
@@ -16,6 +11,7 @@ export default class Oauth2 extends React.Component {
|
||||
authSelectors: PropTypes.object.isRequired,
|
||||
authActions: PropTypes.object.isRequired,
|
||||
errSelectors: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
errActions: PropTypes.object.isRequired,
|
||||
getConfigs: PropTypes.any
|
||||
}
|
||||
@@ -83,7 +79,9 @@ export default class Oauth2 extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
let { schema, getComponent, authSelectors, errSelectors, name } = this.props
|
||||
let {
|
||||
schema, getComponent, authSelectors, errSelectors, name, specSelectors
|
||||
} = this.props
|
||||
const Input = getComponent("Input")
|
||||
const Row = getComponent("Row")
|
||||
const Col = getComponent("Col")
|
||||
@@ -92,6 +90,14 @@ export default class Oauth2 extends React.Component {
|
||||
const JumpToPath = getComponent("JumpToPath", true)
|
||||
const Markdown = getComponent( "Markdown" )
|
||||
|
||||
const { isOAS3 } = specSelectors
|
||||
|
||||
// Auth type consts
|
||||
const IMPLICIT = "implicit"
|
||||
const PASSWORD = "password"
|
||||
const ACCESS_CODE = isOAS3() ? "authorizationCode" : "accessCode"
|
||||
const APPLICATION = isOAS3() ? "clientCredentials" : "application"
|
||||
|
||||
let flow = schema.get("flow")
|
||||
let scopes = schema.get("allowedScopes") || schema.get("scopes")
|
||||
let authorizedAuth = authSelectors.authorized().get(name)
|
||||
@@ -102,7 +108,7 @@ export default class Oauth2 extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h4>OAuth2.0 <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
|
||||
<h4>{name} (OAuth2, { schema.get("flow") }) <JumpToPath path={[ "securityDefinitions", name ]} /></h4>
|
||||
{ !this.state.appName ? null : <h5>Application: { this.state.appName } </h5> }
|
||||
{ description && <Markdown source={ schema.get("description") } /> }
|
||||
|
||||
@@ -194,11 +200,11 @@ export default class Oauth2 extends React.Component {
|
||||
<Row key={ name }>
|
||||
<div className="checkbox">
|
||||
<Input data-value={ name }
|
||||
id={`${name}-checkbox-${this.state.name}`}
|
||||
id={`${name}-${flow}-checkbox-${this.state.name}`}
|
||||
disabled={ isAuthorized }
|
||||
type="checkbox"
|
||||
onChange={ this.onScopeChange }/>
|
||||
<label htmlFor={`${name}-checkbox-${this.state.name}`}>
|
||||
<label htmlFor={`${name}-${flow}-checkbox-${this.state.name}`}>
|
||||
<span className="item"></span>
|
||||
<div className="text">
|
||||
<p className="name">{name}</p>
|
||||
|
||||
@@ -27,6 +27,16 @@ export default class ContentType extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if(!nextProps.contentTypes || !nextProps.contentTypes.size) {
|
||||
return
|
||||
}
|
||||
|
||||
if(!nextProps.contentTypes.includes(nextProps.value)) {
|
||||
nextProps.onChange(nextProps.contentTypes.first())
|
||||
}
|
||||
}
|
||||
|
||||
onChangeWrapper = e => this.props.onChange(e.target.value)
|
||||
|
||||
render() {
|
||||
@@ -37,7 +47,7 @@ export default class ContentType extends React.Component {
|
||||
|
||||
return (
|
||||
<div className={ "content-type-wrapper " + ( className || "" ) }>
|
||||
<select className="content-type" value={value} onChange={this.onChangeWrapper} >
|
||||
<select className="content-type" value={value || ""} onChange={this.onChangeWrapper} >
|
||||
{ contentTypes.map( (val) => {
|
||||
return <option key={ val } value={ val }>{ val }</option>
|
||||
}).toArray()}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import Collapse from "react-collapse"
|
||||
import { Collapse } from "react-collapse"
|
||||
import { presets } from "react-motion"
|
||||
import ObjectInspector from "react-object-inspector"
|
||||
import Perf from "react-addons-perf"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List } from "immutable"
|
||||
import Collapse from "react-collapse"
|
||||
import { Collapse } from "react-collapse"
|
||||
|
||||
export default class Errors extends React.Component {
|
||||
|
||||
@@ -113,7 +113,7 @@ const SpecErrorItem = ( { error, jumpToLine } ) => {
|
||||
}
|
||||
|
||||
function toTitleCase(str) {
|
||||
return str
|
||||
return (str || "")
|
||||
.split(" ")
|
||||
.map(substr => substr[0].toUpperCase() + substr.slice(1))
|
||||
.join(" ")
|
||||
|
||||
@@ -8,7 +8,6 @@ export default class Execute extends Component {
|
||||
specActions: PropTypes.object.isRequired,
|
||||
operation: PropTypes.object.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
onExecute: PropTypes.func
|
||||
}
|
||||
|
||||
@@ -2,20 +2,24 @@ import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import Im from "immutable"
|
||||
|
||||
const propStyle = { color: "#999", fontStyle: "italic" }
|
||||
|
||||
export default class Headers extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
headers: PropTypes.object.isRequired
|
||||
headers: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
let { headers } = this.props
|
||||
let { headers, getComponent } = this.props
|
||||
const Property = getComponent("Property")
|
||||
|
||||
if ( !headers || !headers.size )
|
||||
return null
|
||||
|
||||
return (
|
||||
return (
|
||||
<div className="headers-wrapper">
|
||||
<h4 className="headers__title">Headers:</h4>
|
||||
<table className="headers">
|
||||
@@ -32,10 +36,13 @@ export default class Headers extends React.Component {
|
||||
if(!Im.Map.isMap(header)) {
|
||||
return null
|
||||
}
|
||||
const type = header.getIn(["schema"]) ? header.getIn(["schema", "type"]) : header.getIn(["type"])
|
||||
const schemaExample = header.getIn(["schema", "example"])
|
||||
|
||||
return (<tr key={ key }>
|
||||
<td className="header-col">{ key }</td>
|
||||
<td className="header-col">{ header.get( "description" ) }</td>
|
||||
<td className="header-col">{ header.get( "type" ) }</td>
|
||||
<td className="header-col">{ type } { schemaExample ? <Property propKey={ "Example" } propVal={ schemaExample } propStyle={ propStyle } /> : null }</td>
|
||||
</tr>)
|
||||
}).toArray()
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { fromJS } from "immutable"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { sanitizeUrl } from "core/utils"
|
||||
|
||||
|
||||
class Path extends React.Component {
|
||||
@@ -35,9 +36,9 @@ class Contact extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ url && <div><a href={ url } target="_blank">{ name } - Website</a></div> }
|
||||
{ url && <div><a href={ sanitizeUrl(url) } target="_blank">{ name } - Website</a></div> }
|
||||
{ email &&
|
||||
<a href={`mailto:${email}`}>
|
||||
<a href={sanitizeUrl(`mailto:${email}`)}>
|
||||
{ url ? `Send email to ${name}` : `Contact ${name}`}
|
||||
</a>
|
||||
}
|
||||
@@ -59,7 +60,7 @@ class License extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
url ? <a target="_blank" href={ url }>{ name }</a>
|
||||
url ? <a target="_blank" href={ sanitizeUrl(url) }>{ name }</a>
|
||||
: <span>{ name }</span>
|
||||
}
|
||||
</div>
|
||||
@@ -97,7 +98,7 @@ export default class Info extends React.Component {
|
||||
{ version && <VersionStamp version={version}></VersionStamp> }
|
||||
</h2>
|
||||
{ host || basePath ? <Path host={ host } basePath={ basePath } /> : null }
|
||||
{ url && <a target="_blank" href={ url }><span className="url"> { url } </span></a> }
|
||||
{ url && <a target="_blank" href={ sanitizeUrl(url) }><span className="url"> { url } </span></a> }
|
||||
</hgroup>
|
||||
|
||||
<div className="description">
|
||||
@@ -106,14 +107,14 @@ export default class Info extends React.Component {
|
||||
|
||||
{
|
||||
termsOfService && <div>
|
||||
<a target="_blank" href={ termsOfService }>Terms of service</a>
|
||||
<a target="_blank" href={ sanitizeUrl(termsOfService) }>Terms of service</a>
|
||||
</div>
|
||||
}
|
||||
|
||||
{ contact && contact.size ? <Contact data={ contact } /> : null }
|
||||
{ license && license.size ? <License license={ license } /> : null }
|
||||
{ externalDocsUrl ?
|
||||
<a target="_blank" href={externalDocsUrl}>{externalDocsDescription || externalDocsUrl}</a>
|
||||
<a target="_blank" href={sanitizeUrl(externalDocsUrl)}>{externalDocsDescription || externalDocsUrl}</a>
|
||||
: null }
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import OriCollapse from "react-collapse"
|
||||
import { Collapse as OriCollapse } from "react-collapse"
|
||||
|
||||
function xclass(...args) {
|
||||
return args.filter(a => !!a).join(" ").trim()
|
||||
@@ -183,7 +183,7 @@ export class Select extends React.Component {
|
||||
{ allowEmptyValue ? <option value="">--</option> : null }
|
||||
{
|
||||
allowedValues.map(function (item, key) {
|
||||
return <option key={ key } value={ String(item) }>{ item }</option>
|
||||
return <option key={ key } value={ String(item) }>{ String(item) }</option>
|
||||
})
|
||||
}
|
||||
</select>
|
||||
|
||||
@@ -94,8 +94,9 @@ export default class BaseLayout extends React.Component {
|
||||
) : null }
|
||||
|
||||
{ servers && servers.size ? (
|
||||
<div className="server-container">
|
||||
<div className="global-server-container">
|
||||
<Col className="servers wrapper" mobile={12}>
|
||||
<span className="servers-title">Server</span>
|
||||
<Servers
|
||||
servers={servers}
|
||||
currentServer={oas3Selectors.selectedServer()}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { Iterable } from "immutable"
|
||||
|
||||
const Headers = ( { headers } )=>{
|
||||
return (
|
||||
@@ -28,19 +29,29 @@ Duration.propTypes = {
|
||||
|
||||
export default class LiveResponse extends React.Component {
|
||||
static propTypes = {
|
||||
response: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
pathMethod: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
response: PropTypes.instanceOf(Iterable).isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
displayRequestDuration: PropTypes.bool.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
// BUG: props.response is always coming back as a new Immutable instance
|
||||
// same issue as responses.jsx (tryItOutResponse)
|
||||
return this.props.response !== nextProps.response
|
||||
|| this.props.path !== nextProps.path
|
||||
|| this.props.method !== nextProps.method
|
||||
|| this.props.displayRequestDuration !== nextProps.displayRequestDuration
|
||||
}
|
||||
|
||||
render() {
|
||||
const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, pathMethod } = this.props
|
||||
const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, path, method } = this.props
|
||||
const { showMutatedRequest } = getConfigs()
|
||||
|
||||
const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(pathMethod[0], pathMethod[1]) : specSelectors.requestFor(pathMethod[0], pathMethod[1])
|
||||
const curlRequest = showMutatedRequest ? specSelectors.mutatedRequestFor(path, method) : specSelectors.requestFor(path, method)
|
||||
const status = response.get("status")
|
||||
const url = response.get("url")
|
||||
const headers = response.get("headers").toJS()
|
||||
@@ -118,7 +129,6 @@ export default class LiveResponse extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
request: ImPropTypes.map,
|
||||
response: ImPropTypes.map
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ export default class ModelExample extends React.Component {
|
||||
{
|
||||
!isExecute && this.state.activeTab === "model" && <ModelWrapper schema={ schema }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
specSelectors={ specSelectors }
|
||||
expandDepth={ defaultModelExpandDepth } />
|
||||
|
||||
|
||||
@@ -6,18 +6,17 @@ export default class ModelComponent extends Component {
|
||||
schema: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
expandDepth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { getComponent } = this.props
|
||||
let { getComponent, getConfigs } = this.props
|
||||
const Model = getComponent("Model")
|
||||
|
||||
return <div className="model-box">
|
||||
<Model { ...this.props } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
|
||||
<Model { ...this.props } getConfigs={ getConfigs } depth={ 1 } expandDepth={ this.props.expandDepth || 0 }/>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { Component } from "react"
|
||||
import React, { PureComponent } from "react"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export default class Model extends Component {
|
||||
export default class Model extends PureComponent {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
schema: ImPropTypes.orderedMap.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
isRef: PropTypes.bool,
|
||||
@@ -29,13 +31,13 @@ export default class Model extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
let { getComponent, specSelectors, schema, required, name, isRef } = this.props
|
||||
let { getComponent, getConfigs, specSelectors, schema, required, name, isRef } = this.props
|
||||
const ObjectModel = getComponent("ObjectModel")
|
||||
const ArrayModel = getComponent("ArrayModel")
|
||||
const PrimitiveModel = getComponent("PrimitiveModel")
|
||||
let type = "object"
|
||||
let $$ref = schema && schema.get("$$ref")
|
||||
|
||||
|
||||
// If we weren't passed a `name` but have a ref, grab the name from the ref
|
||||
if ( !name && $$ref ) {
|
||||
name = this.getModelName( $$ref )
|
||||
@@ -44,15 +46,16 @@ export default class Model extends Component {
|
||||
if ( !schema && $$ref ) {
|
||||
schema = this.getRefSchema( name )
|
||||
}
|
||||
|
||||
|
||||
const deprecated = specSelectors.isOAS3() && schema.get("deprecated")
|
||||
isRef = isRef !== undefined ? isRef : !!$$ref
|
||||
type = schema && schema.get("type") || type
|
||||
|
||||
|
||||
switch(type) {
|
||||
case "object":
|
||||
return <ObjectModel
|
||||
className="object" { ...this.props }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
deprecated={deprecated}
|
||||
@@ -60,6 +63,7 @@ export default class Model extends Component {
|
||||
case "array":
|
||||
return <ArrayModel
|
||||
className="array" { ...this.props }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
deprecated={deprecated}
|
||||
@@ -72,6 +76,7 @@ export default class Model extends Component {
|
||||
return <PrimitiveModel
|
||||
{ ...this.props }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
deprecated={deprecated}
|
||||
|
||||
@@ -36,6 +36,7 @@ export default class Models extends Component {
|
||||
expandDepth={ defaultModelExpandDepth }
|
||||
schema={ model }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
specSelectors={ specSelectors }/>
|
||||
</div>
|
||||
}).toArray()
|
||||
|
||||
@@ -9,6 +9,7 @@ export default class ObjectModel extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
isRef: PropTypes.bool,
|
||||
@@ -17,10 +18,16 @@ export default class ObjectModel extends Component {
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema, name, isRef, getComponent, depth, expandDepth, ...otherProps } = this.props
|
||||
let { schema, name, isRef, getComponent, getConfigs, depth, expandDepth, ...otherProps } = this.props
|
||||
let { specSelectors } = otherProps
|
||||
let { isOAS3 } = specSelectors
|
||||
|
||||
if(!schema) {
|
||||
return null
|
||||
}
|
||||
|
||||
const { showExtensions } = getConfigs()
|
||||
|
||||
let description = schema.get("description")
|
||||
let properties = schema.get("properties")
|
||||
let additionalProperties = schema.get("additionalProperties")
|
||||
@@ -72,13 +79,14 @@ export default class ObjectModel extends Component {
|
||||
{
|
||||
!(properties && properties.size) ? null : properties.entrySeq().map(
|
||||
([key, value]) => {
|
||||
let isDeprecated = isOAS3() && value.get("deprecated")
|
||||
let isRequired = List.isList(requiredProperties) && requiredProperties.contains(key)
|
||||
let propertyStyle = { verticalAlign: "top", paddingRight: "0.2em" }
|
||||
if ( isRequired ) {
|
||||
propertyStyle.fontWeight = "bold"
|
||||
}
|
||||
|
||||
return (<tr key={key}>
|
||||
return (<tr key={key} className={isDeprecated && "deprecated"}>
|
||||
<td style={ propertyStyle }>
|
||||
{ key }{ isRequired && <span style={{ color: "red" }}>*</span> }
|
||||
</td>
|
||||
@@ -86,12 +94,37 @@ export default class ObjectModel extends Component {
|
||||
<Model key={ `object-${name}-${key}_${value}` } { ...otherProps }
|
||||
required={ isRequired }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ value }
|
||||
depth={ depth + 1 } />
|
||||
</td>
|
||||
</tr>)
|
||||
}).toArray()
|
||||
}
|
||||
{
|
||||
// empty row befor extensions...
|
||||
!showExtensions ? null : <tr> </tr>
|
||||
}
|
||||
{
|
||||
!showExtensions ? null :
|
||||
schema.entrySeq().map(
|
||||
([key, value]) => {
|
||||
if(key.slice(0,2) !== "x-") {
|
||||
return
|
||||
}
|
||||
|
||||
const normalizedValue = !value ? null : value.toJS ? value.toJS() : value
|
||||
|
||||
return (<tr key={key} style={{ color: "#777" }}>
|
||||
<td>
|
||||
{ key }
|
||||
</td>
|
||||
<td style={{ verticalAlign: "top" }}>
|
||||
{ JSON.stringify(normalizedValue) }
|
||||
</td>
|
||||
</tr>)
|
||||
}).toArray()
|
||||
}
|
||||
{
|
||||
!additionalProperties || !additionalProperties.size ? null
|
||||
: <tr>
|
||||
@@ -99,6 +132,7 @@ export default class ObjectModel extends Component {
|
||||
<td>
|
||||
<Model { ...otherProps } required={ false }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ additionalProperties }
|
||||
depth={ depth + 1 } />
|
||||
</td>
|
||||
@@ -112,6 +146,7 @@ export default class ObjectModel extends Component {
|
||||
{anyOf.map((schema, k) => {
|
||||
return <div key={k}><Model { ...otherProps } required={ false }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ schema }
|
||||
depth={ depth + 1 } /></div>
|
||||
})}
|
||||
@@ -126,6 +161,7 @@ export default class ObjectModel extends Component {
|
||||
{oneOf.map((schema, k) => {
|
||||
return <div key={k}><Model { ...otherProps } required={ false }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ schema }
|
||||
depth={ depth + 1 } /></div>
|
||||
})}
|
||||
@@ -137,12 +173,14 @@ export default class ObjectModel extends Component {
|
||||
: <tr>
|
||||
<td>{ "not ->" }</td>
|
||||
<td>
|
||||
{not.map((schema, k) => {
|
||||
return <div key={k}><Model { ...otherProps } required={ false }
|
||||
getComponent={ getComponent }
|
||||
schema={ schema }
|
||||
depth={ depth + 1 } /></div>
|
||||
})}
|
||||
<div>
|
||||
<Model { ...otherProps }
|
||||
required={ false }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
schema={ not }
|
||||
depth={ depth + 1 } />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { sanitizeUrl } from "core/utils"
|
||||
|
||||
export default class OnlineValidatorBadge extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -32,6 +33,8 @@ export default class OnlineValidatorBadge extends React.Component {
|
||||
let { getConfigs } = this.props
|
||||
let { spec } = getConfigs()
|
||||
|
||||
let sanitizedValidatorUrl = sanitizeUrl(this.state.validatorUrl)
|
||||
|
||||
if ( typeof spec === "object" && Object.keys(spec).length) return null
|
||||
|
||||
if (!this.state.url || !this.state.validatorUrl || this.state.url.indexOf("localhost") >= 0
|
||||
@@ -40,8 +43,8 @@ export default class OnlineValidatorBadge extends React.Component {
|
||||
}
|
||||
|
||||
return (<span style={{ float: "right"}}>
|
||||
<a target="_blank" href={`${ this.state.validatorUrl }/debug?url=${ this.state.url }`}>
|
||||
<ValidatorImage src={`${ this.state.validatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
|
||||
<a target="_blank" href={`${ sanitizedValidatorUrl }/debug?url=${ this.state.url }`}>
|
||||
<ValidatorImage src={`${ sanitizedValidatorUrl }?url=${ this.state.url }`} alt="Online validator badge"/>
|
||||
</a>
|
||||
</span>)
|
||||
}
|
||||
|
||||
17
src/core/components/operation-extension-row.jsx
Normal file
17
src/core/components/operation-extension-row.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export const OperationExtRow = ({ xKey, xVal }) => {
|
||||
const xNormalizedValue = !xVal ? null : xVal.toJS ? xVal.toJS() : xVal
|
||||
|
||||
return (<tr>
|
||||
<td>{ xKey }</td>
|
||||
<td>{ JSON.stringify(xNormalizedValue) }</td>
|
||||
</tr>)
|
||||
}
|
||||
OperationExtRow.propTypes = {
|
||||
xKey: PropTypes.string,
|
||||
xVal: PropTypes.any
|
||||
}
|
||||
|
||||
export default OperationExtRow
|
||||
35
src/core/components/operation-extensions.jsx
Normal file
35
src/core/components/operation-extensions.jsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export const OperationExt = ({ extensions, getComponent }) => {
|
||||
let OperationExtRow = getComponent("OperationExtRow")
|
||||
return (
|
||||
<div className="opblock-section">
|
||||
<div className="opblock-section-header">
|
||||
<h4>Extensions</h4>
|
||||
</div>
|
||||
<div className="table-container">
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<td className="col col_header">Field</td>
|
||||
<td className="col col_header">Value</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
extensions.entrySeq().map(([k, v]) => <OperationExtRow key={`${k}-${v}`} xKey={k} xVal={v} />)
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
OperationExt.propTypes = {
|
||||
extensions: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
export default OperationExt
|
||||
@@ -1,138 +1,93 @@
|
||||
import React, { PureComponent } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { getList } from "core/utils"
|
||||
import * as CustomPropTypes from "core/proptypes"
|
||||
|
||||
//import "less/opblock"
|
||||
import { getExtensions, sanitizeUrl } from "core/utils"
|
||||
import { Iterable } from "immutable"
|
||||
|
||||
export default class Operation extends PureComponent {
|
||||
static propTypes = {
|
||||
path: PropTypes.string.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
operation: PropTypes.object.isRequired,
|
||||
showSummary: PropTypes.bool,
|
||||
operation: PropTypes.instanceOf(Iterable).isRequired,
|
||||
response: PropTypes.instanceOf(Iterable),
|
||||
request: PropTypes.instanceOf(Iterable),
|
||||
|
||||
isShownKey: CustomPropTypes.arrayOrString.isRequired,
|
||||
jumpToKey: CustomPropTypes.arrayOrString.isRequired,
|
||||
|
||||
allowTryItOut: PropTypes.bool,
|
||||
|
||||
displayOperationId: PropTypes.bool,
|
||||
displayRequestDuration: PropTypes.bool,
|
||||
|
||||
response: PropTypes.object,
|
||||
request: PropTypes.object,
|
||||
toggleShown: PropTypes.func.isRequired,
|
||||
onTryoutClick: PropTypes.func.isRequired,
|
||||
onCancelClick: PropTypes.func.isRequired,
|
||||
onExecute: PropTypes.func.isRequired,
|
||||
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
authActions: PropTypes.object,
|
||||
authSelectors: PropTypes.object,
|
||||
specActions: PropTypes.object.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
oas3Selectors: PropTypes.object.isRequired,
|
||||
layoutActions: PropTypes.object.isRequired,
|
||||
layoutSelectors: PropTypes.object.isRequired,
|
||||
fn: PropTypes.object.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired
|
||||
fn: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
showSummary: true,
|
||||
operation: null,
|
||||
response: null,
|
||||
allowTryItOut: true,
|
||||
displayOperationId: false,
|
||||
displayRequestDuration: false
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = {
|
||||
tryItOutEnabled: false
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const defaultContentType = "application/json"
|
||||
let { specActions, path, method, operation } = nextProps
|
||||
let producesValue = operation.get("produces_value")
|
||||
let produces = operation.get("produces")
|
||||
let consumes = operation.get("consumes")
|
||||
let consumesValue = operation.get("consumes_value")
|
||||
|
||||
if(nextProps.response !== this.props.response) {
|
||||
this.setState({ executeInProgress: false })
|
||||
}
|
||||
|
||||
if (producesValue === undefined) {
|
||||
producesValue = produces && produces.size ? produces.first() : defaultContentType
|
||||
specActions.changeProducesValue([path, method], producesValue)
|
||||
}
|
||||
|
||||
if (consumesValue === undefined) {
|
||||
consumesValue = consumes && consumes.size ? consumes.first() : defaultContentType
|
||||
specActions.changeConsumesValue([path, method], consumesValue)
|
||||
}
|
||||
}
|
||||
|
||||
toggleShown =() => {
|
||||
let { layoutActions, isShownKey } = this.props
|
||||
layoutActions.show(isShownKey, !this.isShown())
|
||||
}
|
||||
|
||||
isShown =() => {
|
||||
let { layoutSelectors, isShownKey, getConfigs } = this.props
|
||||
let { docExpansion } = getConfigs()
|
||||
|
||||
return layoutSelectors.isShown(isShownKey, docExpansion === "full" ) // Here is where we set the default
|
||||
}
|
||||
|
||||
onTryoutClick =() => {
|
||||
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
|
||||
}
|
||||
|
||||
onCancelClick =() => {
|
||||
let { specActions, path, method } = this.props
|
||||
this.setState({tryItOutEnabled: !this.state.tryItOutEnabled})
|
||||
specActions.clearValidateParams([path, method])
|
||||
}
|
||||
|
||||
onExecute = () => {
|
||||
this.setState({ executeInProgress: true })
|
||||
request: null
|
||||
}
|
||||
|
||||
render() {
|
||||
let {
|
||||
isShownKey,
|
||||
jumpToKey,
|
||||
path,
|
||||
method,
|
||||
operation,
|
||||
showSummary,
|
||||
response,
|
||||
request,
|
||||
allowTryItOut,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
toggleShown,
|
||||
onTryoutClick,
|
||||
onCancelClick,
|
||||
onExecute,
|
||||
fn,
|
||||
getComponent,
|
||||
getConfigs,
|
||||
specActions,
|
||||
specSelectors,
|
||||
authActions,
|
||||
authSelectors,
|
||||
getConfigs,
|
||||
oas3Actions
|
||||
oas3Actions,
|
||||
oas3Selectors
|
||||
} = this.props
|
||||
let operationProps = this.props.operation
|
||||
|
||||
let summary = operation.get("summary")
|
||||
let description = operation.get("description")
|
||||
let deprecated = operation.get("deprecated")
|
||||
let externalDocs = operation.get("externalDocs")
|
||||
let {
|
||||
isShown,
|
||||
isAuthorized,
|
||||
jumpToKey,
|
||||
path,
|
||||
method,
|
||||
op,
|
||||
tag,
|
||||
showSummary,
|
||||
operationId,
|
||||
allowTryItOut,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
isDeepLinkingEnabled,
|
||||
tryItOutEnabled,
|
||||
executeInProgress
|
||||
} = operationProps.toJS()
|
||||
|
||||
let {
|
||||
summary,
|
||||
description,
|
||||
deprecated,
|
||||
externalDocs,
|
||||
schemes
|
||||
} = op.operation
|
||||
|
||||
let operation = operationProps.getIn(["op", "operation"])
|
||||
let security = operationProps.get("security")
|
||||
let responses = operation.get("responses")
|
||||
let security = operation.get("security") || specSelectors.security()
|
||||
let produces = operation.get("produces")
|
||||
let schemes = operation.get("schemes")
|
||||
let parameters = getList(operation, ["parameters"])
|
||||
let operationId = operation.get("__originalOperationId")
|
||||
let operationScheme = specSelectors.operationScheme(path, method)
|
||||
let isShownKey = ["operations", tag, operationId]
|
||||
let extensions = getExtensions(operation)
|
||||
|
||||
const Responses = getComponent("responses")
|
||||
const Parameters = getComponent( "parameters" )
|
||||
@@ -143,10 +98,10 @@ export default class Operation extends PureComponent {
|
||||
const Collapse = getComponent( "Collapse" )
|
||||
const Markdown = getComponent( "Markdown" )
|
||||
const Schemes = getComponent( "schemes" )
|
||||
const OperationServers = getComponent( "OperationServers" )
|
||||
const OperationExt = getComponent( "OperationExt" )
|
||||
|
||||
const { deepLinking } = getConfigs()
|
||||
|
||||
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||
const { showExtensions } = getConfigs()
|
||||
|
||||
// Merge in Live Response
|
||||
if(responses && response && response.size > 0) {
|
||||
@@ -154,19 +109,17 @@ export default class Operation extends PureComponent {
|
||||
response = response.set("notDocumented", notDocumented)
|
||||
}
|
||||
|
||||
let { tryItOutEnabled } = this.state
|
||||
let shown = this.isShown()
|
||||
let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )
|
||||
|
||||
return (
|
||||
<div className={deprecated ? "opblock opblock-deprecated" : shown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
|
||||
<div className={`opblock-summary opblock-summary-${method}`} onClick={this.toggleShown} >
|
||||
<div className={deprecated ? "opblock opblock-deprecated" : isShown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={isShownKey.join("-")} >
|
||||
<div className={`opblock-summary opblock-summary-${method}`} onClick={toggleShown} >
|
||||
<span className="opblock-summary-method">{method.toUpperCase()}</span>
|
||||
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
|
||||
<a
|
||||
className="nostyle"
|
||||
onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
|
||||
href={isDeepLinkingEnabled ? `#/${isShownKey[1]}/${isShownKey[2]}` : null}>
|
||||
href={isDeepLinkingEnabled ? `#/${isShownKey.join("/")}` : null}>
|
||||
<span>{path}</span>
|
||||
</a>
|
||||
<JumpToPath path={jumpToKey} />
|
||||
@@ -182,13 +135,17 @@ export default class Operation extends PureComponent {
|
||||
|
||||
{
|
||||
(!security || !security.count()) ? null :
|
||||
<AuthorizeOperationBtn authActions={ authActions }
|
||||
security={ security }
|
||||
authSelectors={ authSelectors }/>
|
||||
<AuthorizeOperationBtn
|
||||
isAuthorized={ isAuthorized }
|
||||
onClick={() => {
|
||||
const applicableDefinitions = authSelectors.definitionsForRequirements(security)
|
||||
authActions.showDefinitions(applicableDefinitions)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
|
||||
<Collapse isOpened={shown}>
|
||||
<Collapse isOpened={isShown}>
|
||||
<div className="opblock-body">
|
||||
{ deprecated && <h4 className="opblock-title_normal"> Warning: Deprecated</h4>}
|
||||
{ description &&
|
||||
@@ -199,23 +156,24 @@ export default class Operation extends PureComponent {
|
||||
</div>
|
||||
}
|
||||
{
|
||||
externalDocs && externalDocs.get("url") ?
|
||||
externalDocs && externalDocs.url ?
|
||||
<div className="opblock-external-docs-wrapper">
|
||||
<h4 className="opblock-title_normal">Find more details</h4>
|
||||
<div className="opblock-external-docs">
|
||||
<span className="opblock-external-docs__description">
|
||||
<Markdown source={ externalDocs.get("description") } />
|
||||
<Markdown source={ externalDocs.description } />
|
||||
</span>
|
||||
<a className="opblock-external-docs__link" href={ externalDocs.get("url") }>{ externalDocs.get("url") }</a>
|
||||
<a target="_blank" className="opblock-external-docs__link" href={ sanitizeUrl(externalDocs.url) }>{ externalDocs.url }</a>
|
||||
</div>
|
||||
</div> : null
|
||||
}
|
||||
|
||||
<Parameters
|
||||
parameters={parameters}
|
||||
operation={operation}
|
||||
onChangeKey={onChangeKey}
|
||||
onTryoutClick = { this.onTryoutClick }
|
||||
onCancelClick = { this.onCancelClick }
|
||||
onTryoutClick = { onTryoutClick }
|
||||
onCancelClick = { onCancelClick }
|
||||
tryItOutEnabled = { tryItOutEnabled }
|
||||
allowTryItOut={allowTryItOut}
|
||||
|
||||
@@ -227,6 +185,21 @@ export default class Operation extends PureComponent {
|
||||
getConfigs={ getConfigs }
|
||||
/>
|
||||
|
||||
{ !tryItOutEnabled ? null :
|
||||
<OperationServers
|
||||
getComponent={getComponent}
|
||||
path={path}
|
||||
method={method}
|
||||
operationServers={operation.get("servers")}
|
||||
pathServers={specSelectors.paths().getIn([path, "servers"])}
|
||||
getSelectedServer={oas3Selectors.selectedServer}
|
||||
setSelectedServer={oas3Actions.setSelectedServer}
|
||||
setServerVariableValue={oas3Actions.setServerVariableValue}
|
||||
getServerVariable={oas3Selectors.serverVariableValue}
|
||||
getEffectiveServerValue={oas3Selectors.serverEffectiveValue}
|
||||
/>
|
||||
}
|
||||
|
||||
{!tryItOutEnabled || !allowTryItOut ? null : schemes && schemes.size ? <div className="opblock-schemes">
|
||||
<Schemes schemes={ schemes }
|
||||
path={ path }
|
||||
@@ -240,25 +213,23 @@ export default class Operation extends PureComponent {
|
||||
{ !tryItOutEnabled || !allowTryItOut ? null :
|
||||
|
||||
<Execute
|
||||
getComponent={getComponent}
|
||||
operation={ operation }
|
||||
specActions={ specActions }
|
||||
specSelectors={ specSelectors }
|
||||
path={ path }
|
||||
method={ method }
|
||||
onExecute={ this.onExecute } />
|
||||
onExecute={ onExecute } />
|
||||
}
|
||||
|
||||
{ (!tryItOutEnabled || !response || !allowTryItOut) ? null :
|
||||
<Clear
|
||||
onClick={ this.onClearClick }
|
||||
specActions={ specActions }
|
||||
path={ path }
|
||||
method={ method }/>
|
||||
}
|
||||
</div>
|
||||
|
||||
{this.state.executeInProgress ? <div className="loading-container"><div className="loading"></div></div> : null}
|
||||
{executeInProgress ? <div className="loading-container"><div className="loading"></div></div> : null}
|
||||
|
||||
{ !responses ? null :
|
||||
<Responses
|
||||
@@ -272,10 +243,15 @@ export default class Operation extends PureComponent {
|
||||
specActions={ specActions }
|
||||
produces={ produces }
|
||||
producesValue={ operation.get("produces_value") }
|
||||
pathMethod={ [path, method] }
|
||||
path={ path }
|
||||
method={ method }
|
||||
displayRequestDuration={ displayRequestDuration }
|
||||
fn={fn} />
|
||||
}
|
||||
|
||||
{ !showExtensions || !extensions.size ? null :
|
||||
<OperationExt extensions={ extensions } getComponent={ getComponent } />
|
||||
}
|
||||
</div>
|
||||
</Collapse>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { helpers } from "swagger-client"
|
||||
import { createDeepLinkPath } from "core/utils"
|
||||
const { opId } = helpers
|
||||
import { createDeepLinkPath, sanitizeUrl } from "core/utils"
|
||||
|
||||
export default class Operations extends React.Component {
|
||||
|
||||
@@ -21,28 +19,20 @@ export default class Operations extends React.Component {
|
||||
render() {
|
||||
let {
|
||||
specSelectors,
|
||||
specActions,
|
||||
oas3Actions,
|
||||
getComponent,
|
||||
layoutSelectors,
|
||||
layoutActions,
|
||||
authActions,
|
||||
authSelectors,
|
||||
getConfigs,
|
||||
fn
|
||||
getConfigs
|
||||
} = this.props
|
||||
|
||||
let taggedOps = specSelectors.taggedOperations()
|
||||
|
||||
const Operation = getComponent("operation")
|
||||
const OperationContainer = getComponent("OperationContainer", true)
|
||||
const Collapse = getComponent("Collapse")
|
||||
const Markdown = getComponent("Markdown")
|
||||
|
||||
let showSummary = layoutSelectors.showSummary()
|
||||
let {
|
||||
docExpansion,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
maxDisplayedTags,
|
||||
deepLinking
|
||||
} = getConfigs()
|
||||
@@ -101,7 +91,7 @@ export default class Operations extends React.Component {
|
||||
{ tagExternalDocsUrl ? ": " : null }
|
||||
{ tagExternalDocsUrl ?
|
||||
<a
|
||||
href={tagExternalDocsUrl}
|
||||
href={sanitizeUrl(tagExternalDocsUrl)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
target={"_blank"}
|
||||
>{tagExternalDocsUrl}</a> : null
|
||||
@@ -120,47 +110,15 @@ export default class Operations extends React.Component {
|
||||
<Collapse isOpened={showTag}>
|
||||
{
|
||||
operations.map( op => {
|
||||
const path = op.get("path")
|
||||
const method = op.get("method")
|
||||
|
||||
const path = op.get("path", "")
|
||||
const method = op.get("method", "")
|
||||
const jumpToKey = `paths.${path}.${method}`
|
||||
|
||||
const operationId =
|
||||
op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), path, method) || op.get("id")
|
||||
const isShownKey = ["operations", createDeepLinkPath(tag), createDeepLinkPath(operationId)]
|
||||
|
||||
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}
|
||||
|
||||
displayOperationId={displayOperationId}
|
||||
displayRequestDuration={displayRequestDuration}
|
||||
|
||||
specActions={ specActions }
|
||||
specSelectors={ specSelectors }
|
||||
|
||||
oas3Actions={oas3Actions}
|
||||
|
||||
layoutActions={ layoutActions }
|
||||
layoutSelectors={ layoutSelectors }
|
||||
|
||||
authActions={ authActions }
|
||||
authSelectors={ authSelectors }
|
||||
|
||||
getComponent={ getComponent }
|
||||
fn={fn}
|
||||
getConfigs={ getConfigs }
|
||||
return <OperationContainer
|
||||
key={`${path}-${method}`}
|
||||
op={op}
|
||||
path={path}
|
||||
method={method}
|
||||
tag={tag}
|
||||
/>
|
||||
}).toArray()
|
||||
}
|
||||
|
||||
12
src/core/components/parameter-extension.jsx
Normal file
12
src/core/components/parameter-extension.jsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export const ParameterExt = ({ xKey, xVal }) => {
|
||||
return <div className="parameter__extension">{ xKey }: { String(xVal) }</div>
|
||||
}
|
||||
ParameterExt.propTypes = {
|
||||
xKey: PropTypes.string,
|
||||
xVal: PropTypes.any
|
||||
}
|
||||
|
||||
export default ParameterExt
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { Component } from "react"
|
||||
import { Map } from "immutable"
|
||||
import PropTypes from "prop-types"
|
||||
import win from "core/window"
|
||||
import { getExtensions } from "core/utils"
|
||||
|
||||
export default class ParameterRow extends Component {
|
||||
static propTypes = {
|
||||
@@ -29,11 +31,21 @@ export default class ParameterRow extends Component {
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
let { specSelectors, pathMethod, param } = props
|
||||
let { isOAS3 } = specSelectors
|
||||
|
||||
let example = param.get("example")
|
||||
let defaultValue = param.get("default")
|
||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
||||
let enumValue
|
||||
|
||||
if(isOAS3()) {
|
||||
let schema = param.get("schema") || Map()
|
||||
enumValue = schema.get("enum")
|
||||
} else {
|
||||
enumValue = parameter ? parameter.get("enum") : undefined
|
||||
}
|
||||
let paramValue = parameter ? parameter.get("value") : undefined
|
||||
let enumValue = parameter ? parameter.get("enum") : undefined
|
||||
|
||||
let value
|
||||
|
||||
if ( paramValue !== undefined ) {
|
||||
@@ -61,6 +73,8 @@ export default class ParameterRow extends Component {
|
||||
|
||||
let { isOAS3 } = specSelectors
|
||||
|
||||
const { showExtensions } = getConfigs()
|
||||
|
||||
// const onChangeWrapper = (value) => onChange(param, value)
|
||||
const JsonSchemaForm = getComponent("JsonSchemaForm")
|
||||
const ParamBody = getComponent("ParamBody")
|
||||
@@ -80,6 +94,7 @@ export default class ParameterRow extends Component {
|
||||
|
||||
const ModelExample = getComponent("modelExample")
|
||||
const Markdown = getComponent("Markdown")
|
||||
const ParameterExt = getComponent("ParameterExt")
|
||||
|
||||
let schema = param.get("schema")
|
||||
let type = isOAS3 && isOAS3() ? param.getIn(["schema", "type"]) : param.get("type")
|
||||
@@ -89,6 +104,7 @@ export default class ParameterRow extends Component {
|
||||
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
|
||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
||||
let value = parameter ? parameter.get("value") : ""
|
||||
let extensions = getExtensions(param)
|
||||
|
||||
return (
|
||||
<tr>
|
||||
@@ -102,6 +118,7 @@ export default class ParameterRow extends Component {
|
||||
{ isOAS3 && isOAS3() && param.get("deprecated") ? "deprecated": null }
|
||||
</div>
|
||||
<div className="parameter__in">({ param.get("in") })</div>
|
||||
{ !showExtensions || !extensions.size ? null : extensions.map((v, key) => <ParameterExt key={`${key}-${v}`} xKey={key} xVal={v} /> )}
|
||||
</td>
|
||||
|
||||
<td className="col parameters-col_description">
|
||||
@@ -115,7 +132,7 @@ export default class ParameterRow extends Component {
|
||||
required={ required }
|
||||
description={param.get("description") ? `${param.get("name")} - ${param.get("description")}` : `${param.get("name")}`}
|
||||
onChange={ this.onChangeWrapper }
|
||||
schema={ param }/>
|
||||
schema={ isOAS3 && isOAS3() ? param.get("schema") : param }/>
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React, { Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { getExtensions } from "core/utils"
|
||||
|
||||
const propStyle = { color: "#999", fontStyle: "italic" }
|
||||
|
||||
@@ -7,12 +8,15 @@ export default class Primitive extends Component {
|
||||
static propTypes = {
|
||||
schema: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
name: PropTypes.string,
|
||||
depth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema, getComponent, name, depth } = this.props
|
||||
let { schema, getComponent, getConfigs, name, depth } = this.props
|
||||
|
||||
const { showExtensions } = getConfigs()
|
||||
|
||||
if(!schema || !schema.get) {
|
||||
// don't render if schema isn't correctly formed
|
||||
@@ -25,9 +29,13 @@ export default class Primitive extends Component {
|
||||
let enumArray = schema.get("enum")
|
||||
let title = schema.get("title") || name
|
||||
let description = schema.get("description")
|
||||
let properties = schema.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
|
||||
let extensions = getExtensions(schema)
|
||||
let properties = schema
|
||||
.filter( ( v, key) => ["enum", "type", "format", "description", "$$ref"].indexOf(key) === -1 )
|
||||
.filterNot( (v, key) => extensions.has(key) )
|
||||
const Markdown = getComponent("Markdown")
|
||||
const EnumModel = getComponent("EnumModel")
|
||||
const Property = getComponent("Property")
|
||||
|
||||
return <span className="model">
|
||||
<span className="prop">
|
||||
@@ -35,9 +43,10 @@ export default class Primitive extends Component {
|
||||
<span className="prop-type">{ type }</span>
|
||||
{ format && <span className="prop-format">(${format})</span>}
|
||||
{
|
||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <span key={`${key}-${v}`} style={ propStyle }>
|
||||
<br />{ key }: { String(v) }</span>)
|
||||
: null
|
||||
properties.size ? properties.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
|
||||
}
|
||||
{
|
||||
showExtensions && extensions.size ? extensions.entrySeq().map( ( [ key, v ] ) => <Property key={`${key}-${v}`} propKey={ key } propVal={ v } propStyle={ propStyle } />) : null
|
||||
}
|
||||
{
|
||||
!description ? null :
|
||||
@@ -56,4 +65,4 @@ export default class Primitive extends Component {
|
||||
</span>
|
||||
</span>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
src/core/components/property.jsx
Normal file
16
src/core/components/property.jsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export const Property = ({ propKey, propVal, propStyle }) => {
|
||||
return (
|
||||
<span style={ propStyle }>
|
||||
<br />{ propKey }: { String(propVal) }</span>
|
||||
)
|
||||
}
|
||||
Property.propTypes = {
|
||||
propKey: PropTypes.string,
|
||||
propVal: PropTypes.any,
|
||||
propStyle: PropTypes.object
|
||||
}
|
||||
|
||||
export default Property
|
||||
@@ -29,7 +29,11 @@ Markdown.propTypes = {
|
||||
export default Markdown
|
||||
|
||||
const sanitizeOptions = {
|
||||
allowedTags: sanitize.defaults.allowedTags.concat([ "img" ]),
|
||||
allowedTags: sanitize.defaults.allowedTags.concat([ "h1", "h2", "img" ]),
|
||||
allowedAttributes: {
|
||||
...sanitize.defaults.allowedAttributes,
|
||||
"img": sanitize.defaults.allowedAttributes.img.concat(["title"])
|
||||
},
|
||||
textFilter: function(text) {
|
||||
return text.replace(/"/g, "\"")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { formatXml } from "core/utils"
|
||||
import formatXml from "xml-but-prettier"
|
||||
import lowerCase from "lodash/lowerCase"
|
||||
|
||||
export default class ResponseBody extends React.Component {
|
||||
@@ -31,7 +31,10 @@ export default class ResponseBody extends React.Component {
|
||||
|
||||
// XML
|
||||
} else if (/xml/i.test(contentType)) {
|
||||
body = formatXml(content)
|
||||
body = formatXml(content, {
|
||||
textNodesOnSameLine: true,
|
||||
indentor: " "
|
||||
})
|
||||
bodyEl = <HighlightCode value={ body } />
|
||||
|
||||
// HTML or Plain Text
|
||||
@@ -54,9 +57,6 @@ export default class ResponseBody extends React.Component {
|
||||
(headers["Content-Description"] && (/File Transfer/i).test(headers["Content-Description"])) ||
|
||||
(headers["content-description"] && (/File Transfer/i).test(headers["content-description"]))) {
|
||||
|
||||
let contentLength = headers["content-length"] || headers["Content-Length"]
|
||||
if ( !(+contentLength) ) return null
|
||||
|
||||
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent)
|
||||
|
||||
if (!isSafari && "Blob" in window) {
|
||||
@@ -83,8 +83,12 @@ export default class ResponseBody extends React.Component {
|
||||
// Anything else (CORS)
|
||||
} else if (typeof content === "string") {
|
||||
bodyEl = <HighlightCode value={ content } />
|
||||
} else {
|
||||
} else if ( content.size > 0 ) {
|
||||
// We don't know the contentType, but there was some content returned
|
||||
bodyEl = <div>Unknown response type</div>
|
||||
} else {
|
||||
// We don't know the contentType and there was no content returned
|
||||
bodyEl = null
|
||||
}
|
||||
|
||||
return ( !bodyEl ? null : <div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import cx from "classnames"
|
||||
import { fromJS, Seq } from "immutable"
|
||||
import { fromJS, Seq, Iterable } from "immutable"
|
||||
import { getSampleSchema, fromJSOrdered } from "core/utils"
|
||||
|
||||
const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
|
||||
@@ -42,7 +42,7 @@ export default class Response extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
code: PropTypes.string.isRequired,
|
||||
response: PropTypes.object,
|
||||
response: PropTypes.instanceOf(Iterable),
|
||||
className: PropTypes.string,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
@@ -153,7 +153,10 @@ export default class Response extends React.Component {
|
||||
) : null}
|
||||
|
||||
{ headers ? (
|
||||
<Headers headers={ headers }/>
|
||||
<Headers
|
||||
headers={ headers }
|
||||
getComponent={ getComponent }
|
||||
/>
|
||||
) : null}
|
||||
|
||||
|
||||
|
||||
@@ -1,41 +1,52 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { fromJS } from "immutable"
|
||||
import { fromJS, Iterable } from "immutable"
|
||||
import { defaultStatusCode, getAcceptControllingResponse } from "core/utils"
|
||||
|
||||
export default class Responses extends React.Component {
|
||||
|
||||
static propTypes = {
|
||||
request: PropTypes.object,
|
||||
tryItOutResponse: PropTypes.object,
|
||||
responses: PropTypes.object.isRequired,
|
||||
produces: PropTypes.object,
|
||||
tryItOutResponse: PropTypes.instanceOf(Iterable),
|
||||
responses: PropTypes.instanceOf(Iterable).isRequired,
|
||||
produces: PropTypes.instanceOf(Iterable),
|
||||
producesValue: PropTypes.any,
|
||||
displayRequestDuration: PropTypes.bool.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
specActions: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
pathMethod: PropTypes.array.isRequired,
|
||||
displayRequestDuration: PropTypes.bool.isRequired,
|
||||
fn: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
request: null,
|
||||
tryItOutResponse: null,
|
||||
produces: fromJS(["application/json"]),
|
||||
displayRequestDuration: false
|
||||
}
|
||||
|
||||
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue(this.props.pathMethod, val)
|
||||
shouldComponentUpdate(nextProps) {
|
||||
// BUG: props.tryItOutResponse is always coming back as a new Immutable instance
|
||||
let render = this.props.tryItOutResponse !== nextProps.tryItOutResponse
|
||||
|| this.props.responses !== nextProps.responses
|
||||
|| this.props.produces !== nextProps.produces
|
||||
|| this.props.producesValue !== nextProps.producesValue
|
||||
|| this.props.displayRequestDuration !== nextProps.displayRequestDuration
|
||||
|| this.props.path !== nextProps.path
|
||||
|| this.props.method !== nextProps.method
|
||||
return render
|
||||
}
|
||||
|
||||
onChangeProducesWrapper = ( val ) => this.props.specActions.changeProducesValue([this.props.path, this.props.method], val)
|
||||
|
||||
onResponseContentTypeChange = ({ controlsAcceptHeader, value }) => {
|
||||
const { oas3Actions, pathMethod } = this.props
|
||||
const { oas3Actions, path, method } = this.props
|
||||
if(controlsAcceptHeader) {
|
||||
oas3Actions.setResponseContentType({
|
||||
value,
|
||||
pathMethod
|
||||
path,
|
||||
method
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -43,7 +54,6 @@ export default class Responses extends React.Component {
|
||||
render() {
|
||||
let {
|
||||
responses,
|
||||
request,
|
||||
tryItOutResponse,
|
||||
getComponent,
|
||||
getConfigs,
|
||||
@@ -81,12 +91,12 @@ export default class Responses extends React.Component {
|
||||
{
|
||||
!tryItOutResponse ? null
|
||||
: <div>
|
||||
<LiveResponse request={ request }
|
||||
response={ tryItOutResponse }
|
||||
<LiveResponse response={ tryItOutResponse }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
specSelectors={ specSelectors }
|
||||
pathMethod={ this.props.pathMethod }
|
||||
path={ this.props.path }
|
||||
method={ this.props.method }
|
||||
displayRequestDuration={ displayRequestDuration } />
|
||||
<h4>Responses</h4>
|
||||
</div>
|
||||
|
||||
206
src/core/containers/OperationContainer.jsx
Normal file
206
src/core/containers/OperationContainer.jsx
Normal file
@@ -0,0 +1,206 @@
|
||||
import React, { PureComponent } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { helpers } from "swagger-client"
|
||||
import { Iterable, fromJS } from "immutable"
|
||||
|
||||
const { opId } = helpers
|
||||
|
||||
export default class OperationContainer extends PureComponent {
|
||||
constructor(props, context) {
|
||||
super(props, context)
|
||||
this.state = {
|
||||
tryItOutEnabled: false,
|
||||
executeInProgress: false
|
||||
}
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
op: PropTypes.instanceOf(Iterable).isRequired,
|
||||
tag: PropTypes.string.isRequired,
|
||||
path: PropTypes.string.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
operationId: PropTypes.string.isRequired,
|
||||
showSummary: PropTypes.bool.isRequired,
|
||||
isShown: PropTypes.bool.isRequired,
|
||||
jumpToKey: PropTypes.string.isRequired,
|
||||
allowTryItOut: PropTypes.bool,
|
||||
displayOperationId: PropTypes.bool,
|
||||
isAuthorized: PropTypes.bool,
|
||||
displayRequestDuration: PropTypes.bool,
|
||||
response: PropTypes.instanceOf(Iterable),
|
||||
request: PropTypes.instanceOf(Iterable),
|
||||
security: PropTypes.instanceOf(Iterable),
|
||||
isDeepLinkingEnabled: PropTypes.bool.isRequired,
|
||||
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
authActions: PropTypes.object,
|
||||
oas3Actions: PropTypes.object,
|
||||
oas3Selectors: 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,
|
||||
getConfigs: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
showSummary: true,
|
||||
response: null,
|
||||
allowTryItOut: true,
|
||||
displayOperationId: false,
|
||||
displayRequestDuration: false
|
||||
}
|
||||
|
||||
mapStateToProps(nextState, props) {
|
||||
const { op, layoutSelectors, getConfigs } = props
|
||||
const { docExpansion, deepLinking, displayOperationId, displayRequestDuration } = getConfigs()
|
||||
const showSummary = layoutSelectors.showSummary()
|
||||
const operationId = op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), props.path, props.method) || op.get("id")
|
||||
const isShownKey = ["operations", props.tag, operationId]
|
||||
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
|
||||
const allowTryItOut = typeof props.allowTryItOut === "undefined" ?
|
||||
props.specSelectors.allowTryItOutFor(props.path, props.method) : props.allowTryItOut
|
||||
const security = op.getIn(["operation", "security"]) || props.specSelectors.security()
|
||||
|
||||
return {
|
||||
operationId,
|
||||
isDeepLinkingEnabled,
|
||||
showSummary,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
allowTryItOut,
|
||||
security,
|
||||
isAuthorized: props.authSelectors.isAuthorized(security),
|
||||
isShown: layoutSelectors.isShown(isShownKey, docExpansion === "full" ),
|
||||
jumpToKey: `paths.${props.path}.${props.method}`,
|
||||
response: props.specSelectors.responseFor(props.path, props.method),
|
||||
request: props.specSelectors.requestFor(props.path, props.method)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const defaultContentType = "application/json"
|
||||
let { specActions, path, method, op } = nextProps
|
||||
let operation = op.get("operation")
|
||||
let producesValue = operation.get("produces_value")
|
||||
let produces = operation.get("produces")
|
||||
let consumes = operation.get("consumes")
|
||||
let consumesValue = operation.get("consumes_value")
|
||||
|
||||
if(nextProps.response !== this.props.response) {
|
||||
this.setState({ executeInProgress: false })
|
||||
}
|
||||
|
||||
if (producesValue === undefined) {
|
||||
producesValue = produces && produces.size ? produces.first() : defaultContentType
|
||||
specActions.changeProducesValue([path, method], producesValue)
|
||||
}
|
||||
|
||||
if (consumesValue === undefined) {
|
||||
consumesValue = consumes && consumes.size ? consumes.first() : defaultContentType
|
||||
specActions.changeConsumesValue([path, method], consumesValue)
|
||||
}
|
||||
}
|
||||
|
||||
toggleShown =() => {
|
||||
let { layoutActions, tag, operationId, isShown } = this.props
|
||||
layoutActions.show(["operations", tag, operationId], !isShown)
|
||||
}
|
||||
|
||||
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 {
|
||||
op,
|
||||
tag,
|
||||
path,
|
||||
method,
|
||||
security,
|
||||
isAuthorized,
|
||||
operationId,
|
||||
showSummary,
|
||||
isShown,
|
||||
jumpToKey,
|
||||
allowTryItOut,
|
||||
response,
|
||||
request,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
isDeepLinkingEnabled,
|
||||
specSelectors,
|
||||
specActions,
|
||||
getComponent,
|
||||
getConfigs,
|
||||
layoutSelectors,
|
||||
layoutActions,
|
||||
authActions,
|
||||
authSelectors,
|
||||
oas3Actions,
|
||||
oas3Selectors,
|
||||
fn
|
||||
} = this.props
|
||||
|
||||
const Operation = getComponent( "operation" )
|
||||
|
||||
const operationProps = fromJS({
|
||||
op,
|
||||
tag,
|
||||
path,
|
||||
method,
|
||||
security,
|
||||
isAuthorized,
|
||||
operationId,
|
||||
showSummary,
|
||||
isShown,
|
||||
jumpToKey,
|
||||
allowTryItOut,
|
||||
request,
|
||||
displayOperationId,
|
||||
displayRequestDuration,
|
||||
isDeepLinkingEnabled,
|
||||
executeInProgress: this.state.executeInProgress,
|
||||
tryItOutEnabled: this.state.tryItOutEnabled
|
||||
})
|
||||
|
||||
return (
|
||||
<Operation
|
||||
operation={operationProps}
|
||||
response={response}
|
||||
request={request}
|
||||
isShown={isShown}
|
||||
|
||||
toggleShown={this.toggleShown}
|
||||
onTryoutClick={this.onTryoutClick}
|
||||
onCancelClick={this.onCancelClick}
|
||||
onExecute={this.onExecute}
|
||||
|
||||
specActions={ specActions }
|
||||
specSelectors={ specSelectors }
|
||||
oas3Actions={oas3Actions}
|
||||
oas3Selectors={oas3Selectors}
|
||||
layoutActions={ layoutActions }
|
||||
layoutSelectors={ layoutSelectors }
|
||||
authActions={ authActions }
|
||||
authSelectors={ authSelectors }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
fn={fn}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,8 +7,7 @@ import * as AllPlugins from "core/plugins/all"
|
||||
import { parseSearch } from "core/utils"
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
const Perf = require("react-addons-perf")
|
||||
window.Perf = Perf
|
||||
window.Perf = require("react-addons-perf")
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
@@ -47,6 +46,7 @@ module.exports = function SwaggerUI(opts) {
|
||||
showMutatedRequest: true,
|
||||
defaultModelRendering: "example",
|
||||
defaultModelExpandDepth: 1,
|
||||
showExtensions: false,
|
||||
|
||||
// Initial set of plugins ( TODO rename this, or refactor - we don't need presets _and_ plugins. Its just there for performance.
|
||||
// Instead, we can compile the first plugin ( it can be a collection of plugins ), then batch the rest.
|
||||
@@ -58,13 +58,12 @@ module.exports = function SwaggerUI(opts) {
|
||||
plugins: [
|
||||
],
|
||||
|
||||
// Initial state
|
||||
initialState: { },
|
||||
|
||||
// Inline Plugin
|
||||
fn: { },
|
||||
components: { },
|
||||
state: { },
|
||||
|
||||
// Override some core configs... at your own risk
|
||||
store: { },
|
||||
}
|
||||
|
||||
let queryConfig = parseSearch()
|
||||
@@ -74,12 +73,12 @@ module.exports = function SwaggerUI(opts) {
|
||||
|
||||
const constructorConfig = deepExtend({}, defaults, opts, queryConfig)
|
||||
|
||||
const storeConfigs = deepExtend({}, constructorConfig.store, {
|
||||
const storeConfigs = {
|
||||
system: {
|
||||
configs: constructorConfig.configs
|
||||
},
|
||||
plugins: constructorConfig.presets,
|
||||
state: {
|
||||
state: deepExtend({
|
||||
layout: {
|
||||
layout: constructorConfig.layout,
|
||||
filter: constructorConfig.filter
|
||||
@@ -88,8 +87,8 @@ module.exports = function SwaggerUI(opts) {
|
||||
spec: "",
|
||||
url: constructorConfig.url
|
||||
}
|
||||
}
|
||||
})
|
||||
}, constructorConfig.initialState)
|
||||
}
|
||||
|
||||
let inlinePlugin = ()=> {
|
||||
return {
|
||||
|
||||
@@ -36,7 +36,7 @@ export class JsonSchemaForm extends Component {
|
||||
|
||||
let { type, format="" } = schema
|
||||
|
||||
let Comp = getComponent(`JsonSchema_${type}_${format}`) || getComponent(`JsonSchema_${type}`) || getComponent("JsonSchema_string")
|
||||
let Comp = (format ? getComponent(`JsonSchema_${type}_${format}`) : getComponent(`JsonSchema_${type}`)) || getComponent("JsonSchema_string")
|
||||
return <Comp { ...this.props } fn={fn} getComponent={getComponent} value={value} onChange={onChange} schema={schema}/>
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ export class JsonSchema_string extends Component {
|
||||
if ( enumValue ) {
|
||||
const Select = getComponent("Select")
|
||||
return (<Select className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
allowedValues={ enumValue }
|
||||
value={ value }
|
||||
allowEmptyValue={ !required }
|
||||
@@ -67,10 +68,20 @@ export class JsonSchema_string extends Component {
|
||||
const isDisabled = schema["in"] === "formData" && !("FormData" in window)
|
||||
const Input = getComponent("Input")
|
||||
if (schema["type"] === "file") {
|
||||
return <Input type="file" className={ errors.length ? "invalid" : ""} onChange={ this.onChange } disabled={isDisabled}/>
|
||||
return (<Input type="file"
|
||||
className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
onChange={ this.onChange }
|
||||
disabled={isDisabled}/>)
|
||||
}
|
||||
else {
|
||||
return <Input type={ schema.format === "password" ? "password" : "text" } className={ errors.length ? "invalid" : ""} value={value} placeholder={description} onChange={ this.onChange } disabled={isDisabled}/>
|
||||
return (<Input type={ schema.format === "password" ? "password" : "text" }
|
||||
className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
value={value}
|
||||
placeholder={description}
|
||||
onChange={ this.onChange }
|
||||
disabled={isDisabled}/>)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,11 +145,12 @@ export class JsonSchema_array extends PureComponent {
|
||||
if ( enumValue ) {
|
||||
const Select = getComponent("Select")
|
||||
return (<Select className={ errors.length ? "invalid" : ""}
|
||||
multiple={ true }
|
||||
value={ value }
|
||||
allowedValues={ enumValue }
|
||||
allowEmptyValue={ !required }
|
||||
onChange={ this.onEnumChange }/>)
|
||||
title={ errors.length ? errors : ""}
|
||||
multiple={ true }
|
||||
value={ value }
|
||||
allowedValues={ enumValue }
|
||||
allowEmptyValue={ !required }
|
||||
onChange={ this.onEnumChange }/>)
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -175,9 +187,10 @@ export class JsonSchema_boolean extends Component {
|
||||
const Select = getComponent("Select")
|
||||
|
||||
return (<Select className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
value={ String(value) }
|
||||
allowedValues={ fromJS(["true", "false"]) }
|
||||
allowEmptyValue={ true }
|
||||
allowedValues={ fromJS(schema.enum || ["true", "false"]) }
|
||||
allowEmptyValue={ !this.props.required }
|
||||
onChange={ this.onEnumChange }/>)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,16 @@ export default function authorize ( { auth, authActions, errActions, configs, au
|
||||
case "implicit":
|
||||
query.push("response_type=token")
|
||||
break
|
||||
|
||||
case "clientCredentials":
|
||||
// OAS3
|
||||
authActions.authorizeApplication(auth)
|
||||
return
|
||||
|
||||
case "authorizationCode":
|
||||
// OAS3
|
||||
query.push("response_type=code")
|
||||
break
|
||||
}
|
||||
|
||||
if (typeof clientId === "string") {
|
||||
|
||||
@@ -73,7 +73,7 @@ export const authorizePassword = ( auth ) => ( { authActions } ) => {
|
||||
let { schema, name, username, password, passwordType, clientId, clientSecret } = auth
|
||||
let form = {
|
||||
grant_type: "password",
|
||||
scope: encodeURIComponent(auth.scopes.join(scopeSeparator))
|
||||
scope: auth.scopes.join(scopeSeparator)
|
||||
}
|
||||
let query = {}
|
||||
let headers = {}
|
||||
@@ -139,7 +139,7 @@ export const authorizeAccessCodeWithBasicAuthentication = ( { auth, redirectUrl
|
||||
return authActions.authorizeRequest({body: buildFormData(form), name, url: schema.get("tokenUrl"), auth, headers})
|
||||
}
|
||||
|
||||
export const authorizeRequest = ( data ) => ( { fn, authActions, errActions, authSelectors } ) => {
|
||||
export const authorizeRequest = ( data ) => ( { fn, getConfigs, authActions, errActions, authSelectors } ) => {
|
||||
let { body, query={}, headers={}, name, url, auth } = data
|
||||
let { additionalQueryStringParams } = authSelectors.getConfigs() || {}
|
||||
let fetchUrl = url
|
||||
@@ -158,7 +158,9 @@ export const authorizeRequest = ( data ) => ( { fn, authActions, errActions, aut
|
||||
method: "post",
|
||||
headers: _headers,
|
||||
query: query,
|
||||
body: body
|
||||
body: body,
|
||||
requestInterceptor: getConfigs().requestInterceptor,
|
||||
responseInterceptor: getConfigs().responseInterceptor
|
||||
})
|
||||
.then(function (response) {
|
||||
let token = JSON.parse(response.data)
|
||||
|
||||
@@ -22,7 +22,7 @@ export default {
|
||||
securities.entrySeq().forEach( ([ key, security ]) => {
|
||||
let type = security.getIn(["schema", "type"])
|
||||
|
||||
if ( type === "apiKey" ) {
|
||||
if ( type === "apiKey" || type === "http" ) {
|
||||
map = map.set(key, security)
|
||||
} else if ( type === "basic" ) {
|
||||
let username = security.getIn(["value", "username"])
|
||||
|
||||
@@ -10,8 +10,8 @@ export const shownDefinitions = createSelector(
|
||||
|
||||
export const definitionsToAuthorize = createSelector(
|
||||
state,
|
||||
() =>( { specSelectors } ) => {
|
||||
let definitions = specSelectors.securityDefinitions()
|
||||
() => ( { specSelectors } ) => {
|
||||
let definitions = specSelectors.securityDefinitions() || Map({})
|
||||
let list = List()
|
||||
|
||||
//todo refactor
|
||||
@@ -27,7 +27,8 @@ export const definitionsToAuthorize = createSelector(
|
||||
)
|
||||
|
||||
|
||||
export const getDefinitionsByNames = ( state, securities ) =>( { specSelectors } ) => {
|
||||
export const getDefinitionsByNames = ( state, securities ) => ( { specSelectors } ) => {
|
||||
console.warn("WARNING: getDefinitionsByNames is deprecated and will be removed in the next major version.")
|
||||
let securityDefinitions = specSelectors.securityDefinitions()
|
||||
let result = List()
|
||||
|
||||
@@ -58,13 +59,20 @@ export const getDefinitionsByNames = ( state, securities ) =>( { specSelectors }
|
||||
return result
|
||||
}
|
||||
|
||||
export const definitionsForRequirements = (state, securities = List()) => ({ authSelectors }) => {
|
||||
const allDefinitions = authSelectors.definitionsToAuthorize() || List()
|
||||
return allDefinitions.filter((def) => {
|
||||
return securities.some(sec => sec.get(def.keySeq().first()))
|
||||
})
|
||||
}
|
||||
|
||||
export const authorized = createSelector(
|
||||
state,
|
||||
auth => auth.get("authorized") || Map()
|
||||
)
|
||||
|
||||
|
||||
export const isAuthorized = ( state, securities ) =>( { authSelectors } ) => {
|
||||
export const isAuthorized = ( state, securities ) => ( { authSelectors } ) => {
|
||||
let authorized = authSelectors.authorized()
|
||||
|
||||
if(!List.isList(securities)) {
|
||||
|
||||
@@ -7,13 +7,16 @@ export default function downloadUrlPlugin (toolbox) {
|
||||
let { fn } = toolbox
|
||||
|
||||
const actions = {
|
||||
download: (url)=> ({ errActions, specSelectors, specActions }) => {
|
||||
download: (url)=> ({ errActions, specSelectors, specActions, getConfigs }) => {
|
||||
let { fetch } = fn
|
||||
const config = getConfigs()
|
||||
url = url || specSelectors.url()
|
||||
specActions.updateLoadingStatus("loading")
|
||||
fetch({
|
||||
url,
|
||||
loadSpec: true,
|
||||
requestInterceptor: config.requestInterceptor || (a => a),
|
||||
responseInterceptor: config.responseInterceptor || (a => a),
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
"Accept": "application/json,*/*"
|
||||
|
||||
@@ -3,6 +3,7 @@ import serializeError from "serialize-error"
|
||||
export const NEW_THROWN_ERR = "err_new_thrown_err"
|
||||
export const NEW_THROWN_ERR_BATCH = "err_new_thrown_err_batch"
|
||||
export const NEW_SPEC_ERR = "err_new_spec_err"
|
||||
export const NEW_SPEC_ERR_BATCH = "err_new_spec_err_batch"
|
||||
export const NEW_AUTH_ERR = "err_new_auth_err"
|
||||
export const CLEAR = "err_clear"
|
||||
|
||||
@@ -27,6 +28,13 @@ export function newSpecErr(err) {
|
||||
}
|
||||
}
|
||||
|
||||
export function newSpecErrBatch(errArray) {
|
||||
return {
|
||||
type: NEW_SPEC_ERR_BATCH,
|
||||
payload: errArray
|
||||
}
|
||||
}
|
||||
|
||||
export function newAuthErr(err) {
|
||||
return {
|
||||
type: NEW_AUTH_ERR,
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
NEW_THROWN_ERR,
|
||||
NEW_THROWN_ERR_BATCH,
|
||||
NEW_SPEC_ERR,
|
||||
NEW_SPEC_ERR_BATCH,
|
||||
NEW_AUTH_ERR,
|
||||
CLEAR
|
||||
} from "./actions"
|
||||
@@ -45,6 +46,15 @@ export default function(system) {
|
||||
.update("errors", errors => transformErrors(errors, system.getSystem()))
|
||||
},
|
||||
|
||||
[NEW_SPEC_ERR_BATCH]: (state, { payload }) => {
|
||||
payload = payload.map(err => {
|
||||
return fromJS(Object.assign(DEFAULT_ERROR_STRUCTURE, err, { type: "spec" }))
|
||||
})
|
||||
return state
|
||||
.update("errors", errors => (errors || List()).concat( fromJS( payload )) )
|
||||
.update("errors", errors => transformErrors(errors, system.getSystem()))
|
||||
},
|
||||
|
||||
[NEW_AUTH_ERR]: (state, { payload }) => {
|
||||
let error = fromJS(Object.assign({}, payload))
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ export const UPDATE_REQUEST_CONTENT_TYPE = "oas3_set_request_content_type"
|
||||
export const UPDATE_RESPONSE_CONTENT_TYPE = "oas3_set_response_content_type"
|
||||
export const UPDATE_SERVER_VARIABLE_VALUE = "oas3_set_server_variable_value"
|
||||
|
||||
export function setSelectedServer (selectedServerUrl) {
|
||||
export function setSelectedServer (selectedServerUrl, namespace) {
|
||||
return {
|
||||
type: UPDATE_SELECTED_SERVER,
|
||||
payload: selectedServerUrl
|
||||
payload: {selectedServerUrl, namespace}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,16 +28,16 @@ export function setRequestContentType ({ value, pathMethod }) {
|
||||
}
|
||||
}
|
||||
|
||||
export function setResponseContentType ({ value, pathMethod }) {
|
||||
export function setResponseContentType ({ value, path, method }) {
|
||||
return {
|
||||
type: UPDATE_RESPONSE_CONTENT_TYPE,
|
||||
payload: { value, pathMethod }
|
||||
payload: { value, path, method }
|
||||
}
|
||||
}
|
||||
|
||||
export function setServerVariableValue ({ server, key, val }) {
|
||||
export function setServerVariableValue ({ server, namespace, key, val }) {
|
||||
return {
|
||||
type: UPDATE_SERVER_VARIABLE_VALUE,
|
||||
payload: { server, key, val }
|
||||
payload: { server, namespace, key, val }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,60 @@
|
||||
import { createSelector } from "reselect"
|
||||
import { List } from "immutable"
|
||||
import { List, Map, fromJS } from "immutable"
|
||||
import { isOAS3 as isOAS3Helper } from "../helpers"
|
||||
|
||||
|
||||
// Helpers
|
||||
|
||||
const state = state => state
|
||||
|
||||
function onlyOAS3(selector) {
|
||||
return (ori, system) => (...args) => {
|
||||
return (ori, system) => (state, ...args) => {
|
||||
const spec = system.getSystem().specSelectors.specJson()
|
||||
if(isOAS3Helper(spec)) {
|
||||
return selector(...args)
|
||||
return selector(system, ...args)
|
||||
} else {
|
||||
return ori(...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const nullSelector = createSelector(() => null)
|
||||
export const definitionsToAuthorize = onlyOAS3(createSelector(
|
||||
state,
|
||||
({specSelectors}) => specSelectors.securityDefinitions(),
|
||||
(system, definitions) => {
|
||||
// Coerce our OpenAPI 3.0 definitions into monoflow definitions
|
||||
// that look like Swagger2 definitions.
|
||||
let list = List()
|
||||
|
||||
const OAS3NullSelector = onlyOAS3(nullSelector)
|
||||
definitions.entrySeq().forEach( ([ defName, definition ]) => {
|
||||
const type = definition.get("type")
|
||||
|
||||
// Hasta la vista, authentication!
|
||||
export const shownDefinitions = OAS3NullSelector
|
||||
export const definitionsToAuthorize = OAS3NullSelector
|
||||
export const getDefinitionsByNames = OAS3NullSelector
|
||||
export const authorized = onlyOAS3(() => List())
|
||||
export const isAuthorized = OAS3NullSelector
|
||||
export const getConfigs = OAS3NullSelector
|
||||
if(type === "oauth2") {
|
||||
definition.get("flows").entrySeq().forEach(([flowKey, flowVal]) => {
|
||||
let translatedDef = fromJS({
|
||||
flow: flowKey,
|
||||
authorizationUrl: flowVal.get("authorizationUrl"),
|
||||
tokenUrl: flowVal.get("tokenUrl"),
|
||||
scopes: flowVal.get("scopes"),
|
||||
type: definition.get("type")
|
||||
})
|
||||
|
||||
list = list.push(new Map({
|
||||
[defName]: translatedDef.filter((v) => {
|
||||
// filter out unset values, sometimes `authorizationUrl`
|
||||
// and `tokenUrl` come out as `undefined` in the data
|
||||
return v !== undefined
|
||||
})
|
||||
}))
|
||||
})
|
||||
}
|
||||
if(type === "http" || type === "apiKey") {
|
||||
list = list.push(new Map({
|
||||
[defName]: definition
|
||||
}))
|
||||
}
|
||||
})
|
||||
|
||||
return list
|
||||
}
|
||||
))
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { fromJS } from "immutable"
|
||||
|
||||
const Callbacks = (props) => {
|
||||
let { callbacks, getComponent } = props
|
||||
// const Markdown = getComponent("Markdown")
|
||||
const Operation = getComponent("operation", true)
|
||||
const OperationContainer = getComponent("OperationContainer", true)
|
||||
|
||||
if(!callbacks) {
|
||||
return <span>No callbacks</span>
|
||||
@@ -16,24 +18,22 @@ const Callbacks = (props) => {
|
||||
{ callback.map((pathItem, pathItemName) => {
|
||||
return <div key={pathItemName}>
|
||||
{ pathItem.map((operation, method) => {
|
||||
return <Operation
|
||||
operation={operation}
|
||||
let op = fromJS({
|
||||
operation
|
||||
})
|
||||
return <OperationContainer
|
||||
{...props}
|
||||
op={op}
|
||||
key={method}
|
||||
tag={""}
|
||||
method={method}
|
||||
isShownKey={["callbacks", operation.get("id"), callbackName]}
|
||||
path={pathItemName}
|
||||
allowTryItOut={false}
|
||||
{...props}></Operation>
|
||||
// return <pre>{JSON.stringify(operation)}</pre>
|
||||
/>
|
||||
}) }
|
||||
</div>
|
||||
}) }
|
||||
</div>
|
||||
// return <div>
|
||||
// <h2>{name}</h2>
|
||||
// {callback.description && <Markdown source={callback.description}/>}
|
||||
// <pre>{JSON.stringify(callback)}</pre>
|
||||
// </div>
|
||||
})
|
||||
return <div>
|
||||
{callbackElements}
|
||||
@@ -42,7 +42,7 @@ const Callbacks = (props) => {
|
||||
|
||||
Callbacks.propTypes = {
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
callbacks: PropTypes.array.isRequired
|
||||
callbacks: ImPropTypes.iterable.isRequired
|
||||
|
||||
}
|
||||
|
||||
|
||||
131
src/core/plugins/oas3/components/http-auth.jsx
Normal file
131
src/core/plugins/oas3/components/http-auth.jsx
Normal file
@@ -0,0 +1,131 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
export default class HttpAuth 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, name } = e.target
|
||||
|
||||
let newValue = this.state.value || {}
|
||||
if(name) {
|
||||
newValue[name] = value
|
||||
} else {
|
||||
newValue = value
|
||||
}
|
||||
|
||||
this.setState({ value: newValue }, () => onChange(this.state))
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
const scheme = schema.get("scheme")
|
||||
let value = this.getValue()
|
||||
let errors = errSelectors.allErrors().filter( err => err.get("authId") === name)
|
||||
|
||||
if(scheme === "basic") {
|
||||
let username = value ? value.get("username") : null
|
||||
return <div>
|
||||
<h4>
|
||||
<code>{ name || schema.get("name") }</code>
|
||||
(http, Basic)
|
||||
<JumpToPath path={[ "securityDefinitions", name ]} />
|
||||
</h4>
|
||||
{ username && <h6>Authorized</h6> }
|
||||
<Row>
|
||||
<Markdown source={ schema.get("description") } />
|
||||
</Row>
|
||||
<Row>
|
||||
<label>Username:</label>
|
||||
{
|
||||
username ? <code> { username } </code>
|
||||
: <Col><Input type="text" required="required" name="username" onChange={ this.onChange }/></Col>
|
||||
}
|
||||
</Row>
|
||||
<Row>
|
||||
<label>Password:</label>
|
||||
{
|
||||
username ? <code> ****** </code>
|
||||
: <Col><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>
|
||||
}
|
||||
|
||||
if(scheme === "bearer") {
|
||||
return (
|
||||
<div>
|
||||
<h4>
|
||||
<code>{ name || schema.get("name") }</code>
|
||||
(http, Bearer)
|
||||
<JumpToPath path={[ "securityDefinitions", name ]} />
|
||||
</h4>
|
||||
{ value && <h6>Authorized</h6>}
|
||||
<Row>
|
||||
<Markdown source={ schema.get("description") } />
|
||||
</Row>
|
||||
<Row>
|
||||
<label>Value:</label>
|
||||
{
|
||||
value ? <code> ****** </code>
|
||||
: <Col><Input type="text" onChange={ this.onChange }/></Col>
|
||||
}
|
||||
</Row>
|
||||
{
|
||||
errors.valueSeq().map( (error, key) => {
|
||||
return <AuthError error={ error }
|
||||
key={ key }/>
|
||||
} )
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return <div>
|
||||
<em><b>{name}</b> HTTP authentication: unsupported or missing scheme</em>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,15 @@ import RequestBody from "./request-body"
|
||||
import OperationLink from "./operation-link.jsx"
|
||||
import Servers from "./servers"
|
||||
import RequestBodyEditor from "./request-body-editor"
|
||||
import HttpAuth from "./http-auth"
|
||||
import OperationServers from "./operation-servers"
|
||||
|
||||
export default {
|
||||
Callbacks,
|
||||
HttpAuth,
|
||||
RequestBody,
|
||||
Servers,
|
||||
RequestBodyEditor,
|
||||
OperationServers,
|
||||
operationLink: OperationLink
|
||||
}
|
||||
|
||||
102
src/core/plugins/oas3/components/operation-servers.jsx
Normal file
102
src/core/plugins/oas3/components/operation-servers.jsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
export default class OperationServers extends React.Component {
|
||||
static propTypes = {
|
||||
// for self
|
||||
path: PropTypes.string.isRequired,
|
||||
method: PropTypes.string.isRequired,
|
||||
operationServers: ImPropTypes.list,
|
||||
pathServers: ImPropTypes.list,
|
||||
setSelectedServer: PropTypes.func.isRequired,
|
||||
setServerVariableValue: PropTypes.func.isRequired,
|
||||
getSelectedServer: PropTypes.func.isRequired,
|
||||
getServerVariable: PropTypes.func.isRequired,
|
||||
getEffectiveServerValue: PropTypes.func.isRequired,
|
||||
|
||||
// utils
|
||||
getComponent: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
setSelectedServer = (server) => {
|
||||
const { path, method } = this.props
|
||||
// FIXME: we should be keeping up with this in props/state upstream of us
|
||||
// instead of cheating™ with `forceUpdate`
|
||||
this.forceUpdate()
|
||||
return this.props.setSelectedServer(server, `${path}:${method}`)
|
||||
}
|
||||
|
||||
setServerVariableValue = (obj) => {
|
||||
const { path, method } = this.props
|
||||
// FIXME: we should be keeping up with this in props/state upstream of us
|
||||
// instead of cheating™ with `forceUpdate`
|
||||
this.forceUpdate()
|
||||
return this.props.setServerVariableValue({
|
||||
...obj,
|
||||
namespace: `${path}:${method}`
|
||||
})
|
||||
}
|
||||
|
||||
getSelectedServer = () => {
|
||||
const { path, method } = this.props
|
||||
return this.props.getSelectedServer(`${path}:${method}`)
|
||||
}
|
||||
|
||||
getServerVariable = (server, key) => {
|
||||
const { path, method } = this.props
|
||||
return this.props.getServerVariable({
|
||||
namespace: `${path}:${method}`,
|
||||
server
|
||||
}, key)
|
||||
}
|
||||
|
||||
getEffectiveServerValue = (server) => {
|
||||
const { path, method } = this.props
|
||||
return this.props.getEffectiveServerValue({
|
||||
server,
|
||||
namespace: `${path}:${method}`
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
// for self
|
||||
operationServers,
|
||||
pathServers,
|
||||
|
||||
// util
|
||||
getComponent
|
||||
} = this.props
|
||||
|
||||
if(!operationServers && !pathServers) {
|
||||
return null
|
||||
}
|
||||
|
||||
const Servers = getComponent("Servers")
|
||||
|
||||
const serversToDisplay = operationServers || pathServers
|
||||
const displaying = operationServers ? "operation" : "path"
|
||||
|
||||
return <div className="opblock-section operation-servers">
|
||||
<div className="opblock-section-header">
|
||||
<div className="tab-header">
|
||||
<h4 className="opblock-title">Servers</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div className="opblock-description-wrapper">
|
||||
<h4 className="message">
|
||||
These {displaying}-level options override the global server options.
|
||||
</h4>
|
||||
<Servers
|
||||
servers={serversToDisplay}
|
||||
currentServer={this.getSelectedServer()}
|
||||
setSelectedServer={this.setSelectedServer}
|
||||
setServerVariableValue={this.setServerVariableValue}
|
||||
getServerVariable={this.getServerVariable}
|
||||
getEffectiveServerValue={this.getEffectiveServerValue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,13 @@ export default class RequestBodyEditor extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if(this.props.requestBody !== prevProps.requestBody) {
|
||||
// force recalc of value if the request body definition has changed
|
||||
this.setValueToSample(this.props.mediaType)
|
||||
}
|
||||
}
|
||||
|
||||
setValueToSample = (explicitMediaType) => {
|
||||
this.onChange(this.sample(explicitMediaType))
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ const RequestBody = ({
|
||||
|
||||
const mediaTypeValue = requestBodyContent.get(contentType)
|
||||
|
||||
if(!mediaTypeValue) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <div>
|
||||
{ requestBodyDescription &&
|
||||
<Markdown source={requestBodyDescription} />
|
||||
@@ -50,7 +54,7 @@ RequestBody.propTypes = {
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
contentType: PropTypes.string.isRequired,
|
||||
contentType: PropTypes.string,
|
||||
isExecute: PropTypes.bool.isRequired,
|
||||
onChange: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@ export default class Servers extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
let { servers } = this.props
|
||||
let { servers, currentServer } = this.props
|
||||
|
||||
if(currentServer) {
|
||||
return
|
||||
}
|
||||
|
||||
//fire 'change' event to set default 'value' of select
|
||||
this.setServer(servers.first().get("url"))
|
||||
@@ -93,9 +97,8 @@ export default class Servers extends React.Component {
|
||||
let shouldShowVariableUI = currentServerVariableDefs.size !== 0
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="servers">
|
||||
<label htmlFor="servers">
|
||||
<span className="servers-title">Servers</span>
|
||||
<select onChange={ this.onServerChange }>
|
||||
{ servers.valueSeq().map(
|
||||
( server ) =>
|
||||
@@ -109,13 +112,14 @@ export default class Servers extends React.Component {
|
||||
</label>
|
||||
{ shouldShowVariableUI ?
|
||||
<div>
|
||||
<h4>Server variables</h4>
|
||||
|
||||
<div className={"computed-url"}>
|
||||
Computed URL:
|
||||
<code>
|
||||
{getEffectiveServerValue(currentServer)}
|
||||
</code>
|
||||
</div>
|
||||
<h4>Server variables</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
{
|
||||
|
||||
@@ -7,8 +7,9 @@ import {
|
||||
} from "./actions"
|
||||
|
||||
export default {
|
||||
[UPDATE_SELECTED_SERVER]: (state, { payload: selectedServerUrl } ) =>{
|
||||
return state.setIn( [ "selectedServer" ], selectedServerUrl)
|
||||
[UPDATE_SELECTED_SERVER]: (state, { payload: { selectedServerUrl, namespace } } ) =>{
|
||||
const path = namespace ? [ namespace, "selectedServer"] : [ "selectedServer"]
|
||||
return state.setIn( path, selectedServerUrl)
|
||||
},
|
||||
[UPDATE_REQUEST_BODY_VALUE]: (state, { payload: { value, pathMethod } } ) =>{
|
||||
let [path, method] = pathMethod
|
||||
@@ -18,11 +19,11 @@ export default {
|
||||
let [path, method] = pathMethod
|
||||
return state.setIn( [ "requestData", path, method, "requestContentType" ], value)
|
||||
},
|
||||
[UPDATE_RESPONSE_CONTENT_TYPE]: (state, { payload: { value, pathMethod } } ) =>{
|
||||
let [path, method] = pathMethod
|
||||
[UPDATE_RESPONSE_CONTENT_TYPE]: (state, { payload: { value, path, method } } ) =>{
|
||||
return state.setIn( [ "requestData", path, method, "responseContentType" ], value)
|
||||
},
|
||||
[UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, key, val } } ) =>{
|
||||
return state.setIn( [ "serverVariableValues", server, key ], val)
|
||||
[UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, namespace, key, val } } ) =>{
|
||||
const path = namespace ? [ namespace, "serverVariableValues", server, key ] : [ "serverVariableValues", server, key ]
|
||||
return state.setIn(path, val)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@ function onlyOAS3(selector) {
|
||||
}
|
||||
}
|
||||
|
||||
export const selectedServer = onlyOAS3(state => {
|
||||
return state.getIn(["selectedServer"]) || ""
|
||||
export const selectedServer = onlyOAS3((state, namespace) => {
|
||||
const path = namespace ? [namespace, "selectedServer"] : ["selectedServer"]
|
||||
return state.getIn(path) || ""
|
||||
}
|
||||
)
|
||||
|
||||
@@ -35,19 +36,68 @@ export const responseContentType = onlyOAS3((state, path, method) => {
|
||||
}
|
||||
)
|
||||
|
||||
export const serverVariableValue = onlyOAS3((state, server, key) => {
|
||||
return state.getIn(["serverVariableValues", server, key]) || null
|
||||
export const serverVariableValue = onlyOAS3((state, locationData, key) => {
|
||||
let path
|
||||
|
||||
// locationData may take one of two forms, for backwards compatibility
|
||||
// Object: ({server, namespace?}) or String:(server)
|
||||
if(typeof locationData !== "string") {
|
||||
const { server, namespace } = locationData
|
||||
if(namespace) {
|
||||
path = [namespace, "serverVariableValues", server, key]
|
||||
} else {
|
||||
path = ["serverVariableValues", server, key]
|
||||
}
|
||||
} else {
|
||||
const server = locationData
|
||||
path = ["serverVariableValues", server, key]
|
||||
}
|
||||
|
||||
return state.getIn(path) || null
|
||||
}
|
||||
)
|
||||
|
||||
export const serverVariables = onlyOAS3((state, server) => {
|
||||
return state.getIn(["serverVariableValues", server]) || OrderedMap()
|
||||
export const serverVariables = onlyOAS3((state, locationData) => {
|
||||
let path
|
||||
|
||||
// locationData may take one of two forms, for backwards compatibility
|
||||
// Object: ({server, namespace?}) or String:(server)
|
||||
if(typeof locationData !== "string") {
|
||||
const { server, namespace } = locationData
|
||||
if(namespace) {
|
||||
path = [namespace, "serverVariableValues", server]
|
||||
} else {
|
||||
path = ["serverVariableValues", server]
|
||||
}
|
||||
} else {
|
||||
const server = locationData
|
||||
path = ["serverVariableValues", server]
|
||||
}
|
||||
|
||||
return state.getIn(path) || OrderedMap()
|
||||
}
|
||||
)
|
||||
|
||||
export const serverEffectiveValue = onlyOAS3((state, server) => {
|
||||
let varValues = state.getIn(["serverVariableValues", server]) || OrderedMap()
|
||||
let str = server
|
||||
export const serverEffectiveValue = onlyOAS3((state, locationData) => {
|
||||
var varValues, serverValue
|
||||
|
||||
// locationData may take one of two forms, for backwards compatibility
|
||||
// Object: ({server, namespace?}) or String:(server)
|
||||
if(typeof locationData !== "string") {
|
||||
const { server, namespace } = locationData
|
||||
serverValue = server
|
||||
if(namespace) {
|
||||
varValues = state.getIn([namespace, "serverVariableValues", serverValue])
|
||||
} else {
|
||||
varValues = state.getIn(["serverVariableValues", serverValue])
|
||||
}
|
||||
} else {
|
||||
serverValue = locationData
|
||||
varValues = state.getIn(["serverVariableValues", serverValue])
|
||||
}
|
||||
|
||||
varValues = varValues || OrderedMap()
|
||||
let str = serverValue
|
||||
|
||||
varValues.map((val, key) => {
|
||||
str = str.replace(new RegExp(`{${key}}`, "g"), val)
|
||||
|
||||
@@ -48,12 +48,20 @@ export const definitions = onlyOAS3(createSelector(
|
||||
spec => spec.getIn(["components", "schemas"]) || Map()
|
||||
))
|
||||
|
||||
export const hasHost = onlyOAS3((state) => {
|
||||
return spec(state).hasIn(["servers", 0])
|
||||
})
|
||||
|
||||
export const securityDefinitions = onlyOAS3(createSelector(
|
||||
spec,
|
||||
spec => spec.getIn(["components", "securitySchemes"]) || null
|
||||
))
|
||||
|
||||
export const host = OAS3NullSelector
|
||||
export const basePath = OAS3NullSelector
|
||||
export const consumes = OAS3NullSelector
|
||||
export const produces = OAS3NullSelector
|
||||
export const schemes = OAS3NullSelector
|
||||
export const securityDefinitions = OAS3NullSelector
|
||||
|
||||
// New selectors
|
||||
|
||||
|
||||
23
src/core/plugins/oas3/wrap-components/auth-item.jsx
Normal file
23
src/core/plugins/oas3/wrap-components/auth-item.jsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from "react"
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
|
||||
export default OAS3ComponentWrapFactory(({ Ori, ...props }) => {
|
||||
const {
|
||||
schema, getComponent, errSelectors, authorized, onAuthChange, name
|
||||
} = props
|
||||
|
||||
const HttpAuth = getComponent("HttpAuth")
|
||||
const type = schema.get("type")
|
||||
|
||||
if(type === "http") {
|
||||
return <HttpAuth key={ name }
|
||||
schema={ schema }
|
||||
name={ name }
|
||||
errSelectors={ errSelectors }
|
||||
authorized={ authorized }
|
||||
getComponent={ getComponent }
|
||||
onChange={ onAuthChange }/>
|
||||
} else {
|
||||
return <Ori {...props} />
|
||||
}
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
import Markdown from "./markdown"
|
||||
import AuthItem from "./auth-item"
|
||||
import parameters from "./parameters"
|
||||
import VersionStamp from "./version-stamp"
|
||||
import OnlineValidatorBadge from "./online-validator-badge"
|
||||
@@ -6,6 +7,7 @@ import Model from "./model"
|
||||
|
||||
export default {
|
||||
Markdown,
|
||||
AuthItem,
|
||||
parameters,
|
||||
VersionStamp,
|
||||
model: Model,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import React from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ReactMarkdown from "react-markdown"
|
||||
import { Parser, HtmlRenderer } from "commonmark"
|
||||
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||
import { sanitizer } from "core/components/providers/markdown"
|
||||
|
||||
export default OAS3ComponentWrapFactory(({ source }) => {
|
||||
export const Markdown = ({ source }) => {
|
||||
if ( source ) {
|
||||
const parser = new Parser()
|
||||
const writer = new HtmlRenderer()
|
||||
@@ -23,4 +24,9 @@ export default OAS3ComponentWrapFactory(({ source }) => {
|
||||
)
|
||||
}
|
||||
return null
|
||||
})
|
||||
}
|
||||
Markdown.propTypes = {
|
||||
source: PropTypes.string
|
||||
}
|
||||
|
||||
export default OAS3ComponentWrapFactory(Markdown)
|
||||
@@ -8,12 +8,13 @@ class ModelComponent extends Component {
|
||||
schema: PropTypes.object.isRequired,
|
||||
name: PropTypes.string,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
expandDepth: PropTypes.number
|
||||
}
|
||||
|
||||
render(){
|
||||
let { schema } = this.props
|
||||
let { getConfigs, schema } = this.props
|
||||
let classes = ["model-box"]
|
||||
let isDeprecated = schema.get("deprecated") === true
|
||||
let message = null
|
||||
@@ -26,6 +27,7 @@ class ModelComponent extends Component {
|
||||
return <div className={classes.join(" ")}>
|
||||
{message}
|
||||
<Model { ...this.props }
|
||||
getConfigs={ getConfigs }
|
||||
depth={ 1 }
|
||||
expandDepth={ this.props.expandDepth || 0 }
|
||||
/>
|
||||
|
||||
@@ -22,6 +22,7 @@ class Parameters extends Component {
|
||||
specActions: PropTypes.object.isRequired,
|
||||
operation: PropTypes.object.isRequired,
|
||||
getComponent: PropTypes.func.isRequired,
|
||||
getConfigs: PropTypes.func.isRequired,
|
||||
specSelectors: PropTypes.object.isRequired,
|
||||
oas3Actions: PropTypes.object.isRequired,
|
||||
oas3Selectors: PropTypes.object.isRequired,
|
||||
@@ -86,6 +87,7 @@ class Parameters extends Component {
|
||||
|
||||
fn,
|
||||
getComponent,
|
||||
getConfigs,
|
||||
specSelectors,
|
||||
oas3Actions,
|
||||
oas3Selectors,
|
||||
@@ -137,6 +139,7 @@ class Parameters extends Component {
|
||||
eachMap(parameters, (parameter) => (
|
||||
<ParameterRow fn={ fn }
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
param={ parameter }
|
||||
key={ parameter.get( "name" ) }
|
||||
onChange={ this.onChange }
|
||||
|
||||
@@ -80,7 +80,12 @@ export const parseToJson = (str) => ({specActions, specSelectors, errActions}) =
|
||||
}
|
||||
|
||||
export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }, getConfigs}) => {
|
||||
const { modelPropertyMacro, parameterMacro } = getConfigs()
|
||||
const {
|
||||
modelPropertyMacro,
|
||||
parameterMacro,
|
||||
requestInterceptor,
|
||||
responseInterceptor
|
||||
} = getConfigs()
|
||||
|
||||
if(typeof(json) === "undefined") {
|
||||
json = specSelectors.specJson()
|
||||
@@ -93,8 +98,15 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio
|
||||
|
||||
let specStr = specSelectors.specStr()
|
||||
|
||||
return resolve({fetch, spec: json, baseDoc: url, modelPropertyMacro, parameterMacro })
|
||||
.then( ({spec, errors}) => {
|
||||
return resolve({
|
||||
fetch,
|
||||
spec: json,
|
||||
baseDoc: url,
|
||||
modelPropertyMacro,
|
||||
parameterMacro,
|
||||
requestInterceptor,
|
||||
responseInterceptor
|
||||
}).then( ({spec, errors}) => {
|
||||
errActions.clear({
|
||||
type: "thrown"
|
||||
})
|
||||
@@ -137,10 +149,13 @@ export function changeParam( path, paramName, paramIn, value, isXml ){
|
||||
}
|
||||
}
|
||||
|
||||
export function validateParams( payload ){
|
||||
export const validateParams = ( payload, isOAS3 ) =>{
|
||||
return {
|
||||
type: VALIDATE_PARAMS,
|
||||
payload:{ pathMethod: payload }
|
||||
payload:{
|
||||
pathMethod: payload,
|
||||
isOAS3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,9 +228,18 @@ export const executeRequest = (req) =>
|
||||
}
|
||||
|
||||
if(specSelectors.isOAS3()) {
|
||||
// OAS3 request feature support
|
||||
req.server = oas3Selectors.selectedServer()
|
||||
req.serverVariables = oas3Selectors.serverVariables(req.server).toJS()
|
||||
const namespace = `${pathName}:${method}`
|
||||
|
||||
req.server = oas3Selectors.selectedServer(namespace) || oas3Selectors.selectedServer()
|
||||
|
||||
const namespaceVariables = oas3Selectors.serverVariables({
|
||||
server: req.server,
|
||||
namespace
|
||||
}).toJS()
|
||||
const globalVariables = oas3Selectors.serverVariables({ server: req.server }).toJS()
|
||||
|
||||
req.serverVariables = Object.keys(namespaceVariables).length ? namespaceVariables : globalVariables
|
||||
|
||||
req.requestContentType = oas3Selectors.requestContentType(pathName, method)
|
||||
req.responseContentType = oas3Selectors.responseContentType(pathName, method) || "*/*"
|
||||
const requestBody = oas3Selectors.requestBodyValue(pathName, method)
|
||||
|
||||
@@ -51,14 +51,14 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {
|
||||
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
|
||||
let operation = state.getIn( [ "resolved", "paths", ...pathMethod ] )
|
||||
let isXml = /xml/i.test(operation.get("consumes_value"))
|
||||
|
||||
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
||||
return parameters.withMutations( parameters => {
|
||||
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
|
||||
let errors = validateParam(parameters.get(i), isXml)
|
||||
let errors = validateParam(parameters.get(i), isXml, isOAS3)
|
||||
parameters.setIn([i, "errors"], fromJS(errors))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -13,3 +13,7 @@ export const executeRequest = (ori, { specActions }) => (req) => {
|
||||
specActions.logRequest(req)
|
||||
return ori(req)
|
||||
}
|
||||
|
||||
export const validateParams = (ori, { specSelectors }) => (req) => {
|
||||
return ori(req, specSelectors.isOAS3())
|
||||
}
|
||||
@@ -20,8 +20,14 @@ const RootWrapper = (reduxStore, ComponentToWrap) => class extends Component {
|
||||
}
|
||||
|
||||
const makeContainer = (getSystem, component, reduxStore) => {
|
||||
const mapStateToProps = function(state, ownProps) {
|
||||
const propsForContainerComponent = Object.assign({}, ownProps, getSystem())
|
||||
const ori = component.prototype.mapStateToProps || (state => { return {state} })
|
||||
return ori(state, propsForContainerComponent)
|
||||
}
|
||||
|
||||
let wrappedWithSystem = SystemWrapper(getSystem, component, reduxStore)
|
||||
let connected = connect(state => ({state}))(wrappedWithSystem)
|
||||
let connected = connect( mapStateToProps )(wrappedWithSystem)
|
||||
if(reduxStore)
|
||||
return RootWrapper(reduxStore, connected)
|
||||
return connected
|
||||
@@ -114,5 +120,5 @@ export const getComponent = (getSystem, getStore, getComponents, componentName,
|
||||
return makeContainer(getSystem, component, getStore())
|
||||
|
||||
// container == truthy
|
||||
return makeContainer(getSystem, component)
|
||||
return makeContainer(getSystem, wrapRender(component))
|
||||
}
|
||||
|
||||
@@ -10,13 +10,17 @@ import auth from "core/plugins/auth"
|
||||
import util from "core/plugins/util"
|
||||
import SplitPaneModePlugin from "core/plugins/split-pane-mode"
|
||||
import downloadUrlPlugin from "core/plugins/download-url"
|
||||
import configsPlugin from "plugins/configs"
|
||||
import deepLinkingPlugin from "core/plugins/deep-linking"
|
||||
|
||||
import OperationContainer from "core/containers/OperationContainer"
|
||||
|
||||
import App from "core/components/app"
|
||||
import AuthorizationPopup from "core/components/auth/authorization-popup"
|
||||
import AuthorizeBtn from "core/components/auth/authorize-btn"
|
||||
import AuthorizeOperationBtn from "core/components/auth/authorize-operation-btn"
|
||||
import Auths from "core/components/auth/auths"
|
||||
import AuthItem from "core/components/auth/auth-item"
|
||||
import AuthError from "core/components/auth/error"
|
||||
import ApiKeyAuth from "core/components/auth/api-key-auth"
|
||||
import BasicAuth from "core/components/auth/basic-auth"
|
||||
@@ -26,11 +30,14 @@ import LiveResponse from "core/components/live-response"
|
||||
import OnlineValidatorBadge from "core/components/online-validator-badge"
|
||||
import Operations from "core/components/operations"
|
||||
import Operation from "core/components/operation"
|
||||
import OperationExt from "core/components/operation-extensions"
|
||||
import OperationExtRow from "core/components/operation-extension-row"
|
||||
import HighlightCode from "core/components/highlight-code"
|
||||
import Responses from "core/components/responses"
|
||||
import Response from "core/components/response"
|
||||
import ResponseBody from "core/components/response-body"
|
||||
import Parameters from "core/components/parameters"
|
||||
import ParameterExt from "core/components/parameter-extension"
|
||||
import ParameterRow from "core/components/parameter-row"
|
||||
import Execute from "core/components/execute"
|
||||
import Headers from "core/components/headers"
|
||||
@@ -51,6 +58,7 @@ import EnumModel from "core/components/enum-model"
|
||||
import ObjectModel from "core/components/object-model"
|
||||
import ArrayModel from "core/components/array-model"
|
||||
import PrimitiveModel from "core/components/primitive-model"
|
||||
import Property from "core/components/property"
|
||||
import TryItOutButton from "core/components/try-it-out-button"
|
||||
import VersionStamp from "core/components/version-stamp"
|
||||
|
||||
@@ -70,6 +78,7 @@ export default function() {
|
||||
authorizeBtn: AuthorizeBtn,
|
||||
authorizeOperationBtn: AuthorizeOperationBtn,
|
||||
auths: Auths,
|
||||
AuthItem: AuthItem,
|
||||
authError: AuthError,
|
||||
oauth2: Oauth2,
|
||||
apiKeyAuth: ApiKeyAuth,
|
||||
@@ -104,10 +113,15 @@ export default function() {
|
||||
ObjectModel,
|
||||
ArrayModel,
|
||||
PrimitiveModel,
|
||||
Property,
|
||||
TryItOutButton,
|
||||
Markdown,
|
||||
BaseLayout,
|
||||
VersionStamp
|
||||
VersionStamp,
|
||||
OperationExt,
|
||||
OperationExtRow,
|
||||
ParameterExt,
|
||||
OperationContainer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,6 +134,7 @@ export default function() {
|
||||
}
|
||||
|
||||
return [
|
||||
configsPlugin,
|
||||
util,
|
||||
logs,
|
||||
view,
|
||||
|
||||
@@ -294,8 +294,7 @@ export default class Store {
|
||||
|
||||
getMapStateToProps() {
|
||||
return () => {
|
||||
let obj = Object.assign({}, this.getSystem())
|
||||
return obj
|
||||
return Object.assign({}, this.getSystem())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Im from "immutable"
|
||||
|
||||
import { sanitizeUrl as braintreeSanitizeUrl } from "@braintree/sanitize-url"
|
||||
import camelCase from "lodash/camelCase"
|
||||
import upperFirst from "lodash/upperFirst"
|
||||
import _memoize from "lodash/memoize"
|
||||
@@ -155,83 +155,6 @@ export function getList(iterable, keys) {
|
||||
return Im.List.isList(val) ? val : Im.List()
|
||||
}
|
||||
|
||||
// Adapted from http://stackoverflow.com/a/2893259/454004
|
||||
// Note: directly ported from CoffeeScript
|
||||
export function formatXml (xml) {
|
||||
var contexp, fn, formatted, indent, l, lastType, len, lines, ln, reg, transitions, wsexp
|
||||
reg = /(>)(<)(\/*)/g
|
||||
wsexp = /[ ]*(.*)[ ]+\n/g
|
||||
contexp = /(<.+>)(.+\n)/g
|
||||
xml = xml.replace(/\r\n/g, "\n").replace(reg, "$1\n$2$3").replace(wsexp, "$1\n").replace(contexp, "$1\n$2")
|
||||
formatted = ""
|
||||
lines = xml.split("\n")
|
||||
indent = 0
|
||||
lastType = "other"
|
||||
transitions = {
|
||||
"single->single": 0,
|
||||
"single->closing": -1,
|
||||
"single->opening": 0,
|
||||
"single->other": 0,
|
||||
"closing->single": 0,
|
||||
"closing->closing": -1,
|
||||
"closing->opening": 0,
|
||||
"closing->other": 0,
|
||||
"opening->single": 1,
|
||||
"opening->closing": 0,
|
||||
"opening->opening": 1,
|
||||
"opening->other": 1,
|
||||
"other->single": 0,
|
||||
"other->closing": -1,
|
||||
"other->opening": 0,
|
||||
"other->other": 0
|
||||
}
|
||||
fn = function(ln) {
|
||||
var fromTo, key, padding, type, types, value
|
||||
types = {
|
||||
single: Boolean(ln.match(/<.+\/>/)),
|
||||
closing: Boolean(ln.match(/<\/.+>/)),
|
||||
opening: Boolean(ln.match(/<[^!?].*>/))
|
||||
}
|
||||
type = ((function() {
|
||||
var results
|
||||
results = []
|
||||
for (key in types) {
|
||||
value = types[key]
|
||||
if (value) {
|
||||
results.push(key)
|
||||
}
|
||||
}
|
||||
return results
|
||||
})())[0]
|
||||
type = type === void 0 ? "other" : type
|
||||
fromTo = lastType + "->" + type
|
||||
lastType = type
|
||||
padding = ""
|
||||
indent += transitions[fromTo]
|
||||
padding = ((function() {
|
||||
/* eslint-disable no-unused-vars */
|
||||
var m, ref1, results, j
|
||||
results = []
|
||||
for (j = m = 0, ref1 = indent; 0 <= ref1 ? m < ref1 : m > ref1; j = 0 <= ref1 ? ++m : --m) {
|
||||
results.push(" ")
|
||||
}
|
||||
/* eslint-enable no-unused-vars */
|
||||
return results
|
||||
})()).join("")
|
||||
if (fromTo === "opening->closing") {
|
||||
formatted = formatted.substr(0, formatted.length - 1) + ln + "\n"
|
||||
} else {
|
||||
formatted += padding + ln + "\n"
|
||||
}
|
||||
}
|
||||
for (l = 0, len = lines.length; l < len; l++) {
|
||||
ln = lines[l]
|
||||
fn(ln)
|
||||
}
|
||||
return formatted
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adapted from http://github.com/asvd/microlight
|
||||
* @copyright 2016 asvd <heliosframework@gmail.com>
|
||||
@@ -536,17 +459,31 @@ export const validateMinLength = (val, min) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const validatePattern = (val, rxPattern) => {
|
||||
var patt = new RegExp(rxPattern)
|
||||
if (!patt.test(val)) {
|
||||
return "Value must follow pattern " + rxPattern
|
||||
}
|
||||
}
|
||||
|
||||
// validation of parameters before execute
|
||||
export const validateParam = (param, isXml) => {
|
||||
export const validateParam = (param, isXml, isOAS3 = false) => {
|
||||
let errors = []
|
||||
let value = isXml && param.get("in") === "body" ? param.get("value_xml") : param.get("value")
|
||||
let required = param.get("required")
|
||||
let maximum = param.get("maximum")
|
||||
let minimum = param.get("minimum")
|
||||
let type = param.get("type")
|
||||
let format = param.get("format")
|
||||
let maxLength = param.get("maxLength")
|
||||
let minLength = param.get("minLength")
|
||||
|
||||
let paramDetails = isOAS3 ? param.get("schema") : param
|
||||
|
||||
if(!paramDetails) return errors
|
||||
|
||||
let maximum = paramDetails.get("maximum")
|
||||
let minimum = paramDetails.get("minimum")
|
||||
let type = paramDetails.get("type")
|
||||
let format = paramDetails.get("format")
|
||||
let maxLength = paramDetails.get("maxLength")
|
||||
let minLength = paramDetails.get("minLength")
|
||||
let pattern = paramDetails.get("pattern")
|
||||
|
||||
|
||||
/*
|
||||
If the parameter is required OR the parameter has a value (meaning optional, but filled in)
|
||||
@@ -554,14 +491,24 @@ export const validateParam = (param, isXml) => {
|
||||
Only bother validating the parameter if the type was specified.
|
||||
*/
|
||||
if ( type && (required || value) ) {
|
||||
// These checks should evaluate to true if the parameter's value is valid
|
||||
let stringCheck = type === "string" && value && !validateString(value)
|
||||
// These checks should evaluate to true if there is a parameter
|
||||
let stringCheck = type === "string" && value
|
||||
let arrayCheck = type === "array" && Array.isArray(value) && value.length
|
||||
let listCheck = type === "array" && Im.List.isList(value) && value.count()
|
||||
let fileCheck = type === "file" && value instanceof win.File
|
||||
let booleanCheck = type === "boolean" && !validateBoolean(value)
|
||||
let numberCheck = type === "number" && !validateNumber(value) // validateNumber returns undefined if the value is a number
|
||||
let integerCheck = type === "integer" && !validateInteger(value) // validateInteger returns undefined if the value is an integer
|
||||
let booleanCheck = type === "boolean" && (value || value === false)
|
||||
let numberCheck = type === "number" && (value || value === 0)
|
||||
let integerCheck = type === "integer" && (value || value === 0)
|
||||
|
||||
if ( required && !(stringCheck || arrayCheck || listCheck || fileCheck || booleanCheck || numberCheck || integerCheck) ) {
|
||||
errors.push("Required field is not provided")
|
||||
return errors
|
||||
}
|
||||
|
||||
if (pattern) {
|
||||
let err = validatePattern(value, pattern)
|
||||
if (err) errors.push(err)
|
||||
}
|
||||
|
||||
if (maxLength || maxLength === 0) {
|
||||
let err = validateMaxLength(value, maxLength)
|
||||
@@ -573,11 +520,6 @@ export const validateParam = (param, isXml) => {
|
||||
if (err) errors.push(err)
|
||||
}
|
||||
|
||||
if ( required && !(stringCheck || arrayCheck || listCheck || fileCheck || booleanCheck || numberCheck || integerCheck) ) {
|
||||
errors.push("Required field is not provided")
|
||||
return errors
|
||||
}
|
||||
|
||||
if (maximum || maximum === 0) {
|
||||
let err = validateMaximum(value, maximum)
|
||||
if (err) errors.push(err)
|
||||
@@ -616,7 +558,7 @@ export const validateParam = (param, isXml) => {
|
||||
|
||||
if ( !value.count() ) { return errors }
|
||||
|
||||
itemType = param.getIn(["items", "type"])
|
||||
itemType = paramDetails.getIn(["items", "type"])
|
||||
|
||||
value.forEach((item, index) => {
|
||||
let err
|
||||
@@ -720,6 +662,14 @@ export const shallowEqualKeys = (a,b, keys) => {
|
||||
})
|
||||
}
|
||||
|
||||
export function sanitizeUrl(url) {
|
||||
if(typeof url !== "string" || url === "") {
|
||||
return ""
|
||||
}
|
||||
|
||||
return braintreeSanitizeUrl(url)
|
||||
}
|
||||
|
||||
export function getAcceptControllingResponse(responses) {
|
||||
if(!Im.OrderedMap.isOrderedMap(responses)) {
|
||||
// wrong type!
|
||||
@@ -745,3 +695,5 @@ export function getAcceptControllingResponse(responses) {
|
||||
|
||||
export const createDeepLinkPath = (str) => typeof str == "string" || str instanceof String ? str.trim().replace(/\s/g, "_") : ""
|
||||
export const escapeDeepLinkPath = (str) => cssEscape( createDeepLinkPath(str) )
|
||||
|
||||
export const getExtensions = (defObj) => defObj.filter((v, k) => /^x-/.test(k))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Add a plugin
|
||||
|
||||
### Swagger-UX relies on plugins for all the good stuff.
|
||||
### Swagger-UI relies on plugins for all the good stuff.
|
||||
|
||||
Plugins allow you to add
|
||||
- `statePlugins`
|
||||
|
||||
20
src/plugins/configs/actions.js
Normal file
20
src/plugins/configs/actions.js
Normal file
@@ -0,0 +1,20 @@
|
||||
export const UPDATE_CONFIGS = "configs_update"
|
||||
export const TOGGLE_CONFIGS = "configs_toggle"
|
||||
|
||||
// Update the configs, with a merge ( not deep )
|
||||
export function update(configName, configValue) {
|
||||
return {
|
||||
type: UPDATE_CONFIGS,
|
||||
payload: {
|
||||
[configName]: configValue
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Toggle's the config, by name
|
||||
export function toggle(configName) {
|
||||
return {
|
||||
type: TOGGLE_CONFIGS,
|
||||
payload: configName,
|
||||
}
|
||||
}
|
||||
@@ -1,56 +1,66 @@
|
||||
import YAML from "js-yaml"
|
||||
import yamlConfig from "../../../swagger-config.yaml"
|
||||
import * as actions from "./actions"
|
||||
import * as selectors from "./selectors"
|
||||
import reducers from "./reducers"
|
||||
|
||||
const parseYamlConfig = (yaml, system) => {
|
||||
try {
|
||||
return YAML.safeLoad(yaml)
|
||||
} catch(e) {
|
||||
if (system) {
|
||||
system.errActions.newThrownErr( new Error(e) )
|
||||
}
|
||||
return {}
|
||||
try {
|
||||
return YAML.safeLoad(yaml)
|
||||
} catch(e) {
|
||||
if (system) {
|
||||
system.errActions.newThrownErr( new Error(e) )
|
||||
}
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default function configPlugin (toolbox) {
|
||||
let { fn } = toolbox
|
||||
|
||||
const actions = {
|
||||
downloadConfig: (url) => () => {
|
||||
let {fetch} = fn
|
||||
return fetch(url)
|
||||
},
|
||||
|
||||
getConfigByUrl: (configUrl, cb)=> ({ specActions }) => {
|
||||
if (configUrl) {
|
||||
return specActions.downloadConfig(configUrl).then(next, next)
|
||||
}
|
||||
|
||||
function next(res) {
|
||||
if (res instanceof Error || res.status >= 400) {
|
||||
specActions.updateLoadingStatus("failedConfig")
|
||||
specActions.updateLoadingStatus("failedConfig")
|
||||
specActions.updateUrl("")
|
||||
console.error(res.statusText + " " + configUrl)
|
||||
cb(null)
|
||||
} else {
|
||||
cb(parseYamlConfig(res.text))
|
||||
}
|
||||
}
|
||||
}
|
||||
const specActions = {
|
||||
downloadConfig: (url) => ({fn}) => {
|
||||
let {fetch} = fn
|
||||
return fetch(url)
|
||||
},
|
||||
|
||||
getConfigByUrl: (configUrl, cb)=> ({ specActions }) => {
|
||||
if (configUrl) {
|
||||
return specActions.downloadConfig(configUrl).then(next, next)
|
||||
}
|
||||
|
||||
const selectors = {
|
||||
getLocalConfig: () => {
|
||||
return parseYamlConfig(yamlConfig)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
statePlugins: {
|
||||
spec: { actions, selectors }
|
||||
}
|
||||
function next(res) {
|
||||
if (res instanceof Error || res.status >= 400) {
|
||||
specActions.updateLoadingStatus("failedConfig")
|
||||
specActions.updateLoadingStatus("failedConfig")
|
||||
specActions.updateUrl("")
|
||||
console.error(res.statusText + " " + configUrl)
|
||||
cb(null)
|
||||
} else {
|
||||
cb(parseYamlConfig(res.text))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const specSelectors = {
|
||||
getLocalConfig: () => {
|
||||
return parseYamlConfig(yamlConfig)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default function configsPlugin() {
|
||||
|
||||
return {
|
||||
statePlugins: {
|
||||
spec: {
|
||||
actions: specActions,
|
||||
selectors: specSelectors,
|
||||
},
|
||||
configs: {
|
||||
reducers,
|
||||
actions,
|
||||
selectors,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
src/plugins/configs/reducers.js
Normal file
20
src/plugins/configs/reducers.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import { fromJS } from "immutable"
|
||||
|
||||
import {
|
||||
UPDATE_CONFIGS,
|
||||
TOGGLE_CONFIGS,
|
||||
} from "./actions"
|
||||
|
||||
export default {
|
||||
|
||||
[UPDATE_CONFIGS]: (state, action) => {
|
||||
return state.merge(fromJS(action.payload))
|
||||
},
|
||||
|
||||
[TOGGLE_CONFIGS]: (state, action) => {
|
||||
const configName = action.payload
|
||||
const oriVal = state.get(configName)
|
||||
return state.set(configName, !oriVal)
|
||||
},
|
||||
|
||||
}
|
||||
4
src/plugins/configs/selectors.js
Normal file
4
src/plugins/configs/selectors.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// Just get the config value ( it can possibly be an immutable object)
|
||||
export const get = (state, path) => {
|
||||
return state.getIn(Array.isArray(path) ? path : [path])
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from "react"
|
||||
import React, { cloneElement } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
|
||||
//import "./topbar.less"
|
||||
@@ -129,12 +129,12 @@ export default class Topbar extends React.Component {
|
||||
<div className="topbar">
|
||||
<div className="wrapper">
|
||||
<div className="topbar-wrapper">
|
||||
<Link href="#" title="Swagger UX">
|
||||
<Link href="#">
|
||||
<img height="30" width="30" src={ Logo } alt="Swagger UI"/>
|
||||
<span>swagger</span>
|
||||
</Link>
|
||||
<form className="download-url-wrapper" onSubmit={formOnSubmit}>
|
||||
{control}
|
||||
{control.map((el, i) => cloneElement(el, { key: i }))}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,3 +3,8 @@
|
||||
if(!window.Promise) {
|
||||
require("core-js/fn/promise")
|
||||
}
|
||||
|
||||
// Required by IE 11
|
||||
if(!String.prototype.startsWith) {
|
||||
require("core-js/es6/string")
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
padding: 10px 0;
|
||||
|
||||
justify-content: center;
|
||||
|
||||
.btn-done {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.auth-wrapper
|
||||
@@ -25,7 +29,7 @@
|
||||
margin: 0 0 10px 0;
|
||||
padding: 10px 20px;
|
||||
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
border-bottom: 1px solid $auth-container-border-color;
|
||||
|
||||
&:last-of-type
|
||||
{
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
|
||||
transition: all .3s;
|
||||
|
||||
border: 2px solid #888;
|
||||
border: 2px solid $btn-border-color;
|
||||
border-radius: 4px;
|
||||
background: transparent;
|
||||
box-shadow: 0 1px 2px rgba(#000,.1);
|
||||
box-shadow: 0 1px 2px rgba($btn-box-shadow-color,.1);
|
||||
|
||||
@include text_headline();
|
||||
|
||||
@@ -29,14 +29,14 @@
|
||||
|
||||
&:hover
|
||||
{
|
||||
box-shadow: 0 0 5px rgba(#000,.3);
|
||||
box-shadow: 0 0 5px rgba($btn-box-shadow-color,.3);
|
||||
}
|
||||
|
||||
&.cancel
|
||||
{
|
||||
border-color: #ff6060;
|
||||
|
||||
@include text_headline(#ff6060);
|
||||
border-color: $btn-cancel-border-color;
|
||||
background-color: $btn-cancel-background-color;
|
||||
@include text_headline($btn-cancel-font-color);
|
||||
}
|
||||
|
||||
&.authorize
|
||||
@@ -45,9 +45,9 @@
|
||||
|
||||
display: inline;
|
||||
|
||||
color: $_color-post;
|
||||
border-color: $_color-post;
|
||||
|
||||
color: $btn-authorize-font-color;
|
||||
border-color: $btn-authorize-border-color;
|
||||
background-color: $btn-authorize-background-color;
|
||||
|
||||
span
|
||||
{
|
||||
@@ -58,16 +58,17 @@
|
||||
|
||||
svg
|
||||
{
|
||||
fill: $_color-post;
|
||||
fill: $btn-authorize-svg-fill-color;
|
||||
}
|
||||
}
|
||||
|
||||
&.execute
|
||||
{
|
||||
animation: swagger-ui-pulse 2s infinite;
|
||||
|
||||
color: #fff;
|
||||
border-color: #4990e2;
|
||||
will-change: transform;
|
||||
background-color: $btn-execute-background-color;
|
||||
color: $btn-execute-font-color;
|
||||
border-color: $btn-execute-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,21 +77,19 @@
|
||||
{
|
||||
0%
|
||||
{
|
||||
color: #fff;
|
||||
background: #4990e2;
|
||||
box-shadow: 0 0 0 0 rgba(#4990e2, .8);
|
||||
color: $btn-execute-font-color;
|
||||
background: $btn-execute-background-color-alt;
|
||||
box-shadow: 0 0 0 0 rgba($btn-execute-background-color-alt, .8);
|
||||
}
|
||||
70%
|
||||
{
|
||||
//color: #4990e2;
|
||||
//background: transparent;
|
||||
box-shadow: 0 0 0 5px rgba(#4990e2, 0);
|
||||
box-shadow: 0 0 0 5px rgba($btn-execute-background-color-alt, 0);
|
||||
}
|
||||
100%
|
||||
{
|
||||
color: #fff;
|
||||
background: #4990e2;
|
||||
box-shadow: 0 0 0 0 rgba(#4990e2, 0);
|
||||
color: $btn-execute-font-color;
|
||||
background: $btn-execute-background-color-alt;
|
||||
box-shadow: 0 0 0 0 rgba($btn-execute-background-color-alt, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +154,7 @@
|
||||
{
|
||||
svg
|
||||
{
|
||||
fill: #444;
|
||||
fill: $expand-methods-svg-fill-color-hover;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +162,7 @@
|
||||
{
|
||||
transition: all .3s;
|
||||
|
||||
fill: #777;
|
||||
fill: $expand-methods-svg-fill-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
small
|
||||
{
|
||||
color: #666;
|
||||
color: $errors-wrapper-errors-small-font-color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ select
|
||||
|
||||
padding: 5px 40px 5px 10px;
|
||||
|
||||
border: 2px solid #41444e;
|
||||
border: 2px solid $form-select-border-color;
|
||||
border-radius: 4px;
|
||||
background: #f7f7f7 url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAyMCI+ICAgIDxwYXRoIGQ9Ik0xMy40MTggNy44NTljLjI3MS0uMjY4LjcwOS0uMjY4Ljk3OCAwIC4yNy4yNjguMjcyLjcwMSAwIC45NjlsLTMuOTA4IDMuODNjLS4yNy4yNjgtLjcwNy4yNjgtLjk3OSAwbC0zLjkwOC0zLjgzYy0uMjctLjI2Ny0uMjctLjcwMSAwLS45NjkuMjcxLS4yNjguNzA5LS4yNjguOTc4IDBMMTAgMTFsMy40MTgtMy4xNDF6Ii8+PC9zdmc+) right 10px center no-repeat;
|
||||
background: $form-select-background-color url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyMCAyMCI+ICAgIDxwYXRoIGQ9Ik0xMy40MTggNy44NTljLjI3MS0uMjY4LjcwOS0uMjY4Ljk3OCAwIC4yNy4yNjguMjcyLjcwMSAwIC45NjlsLTMuOTA4IDMuODNjLS4yNy4yNjgtLjcwNy4yNjgtLjk3OSAwbC0zLjkwOC0zLjgzYy0uMjctLjI2Ny0uMjctLjcwMSAwLS45NjkuMjcxLS4yNjguNzA5LS4yNjguOTc4IDBMMTAgMTFsMy40MTgtMy4xNDF6Ii8+PC9zdmc+) right 10px center no-repeat;
|
||||
background-size: 20px;
|
||||
box-shadow: 0 1px 2px 0 rgba(0,0,0,.25);
|
||||
box-shadow: 0 1px 2px 0 rgba($form-select-box-shadow-color, .25);
|
||||
|
||||
@include text_headline();
|
||||
appearance: none;
|
||||
@@ -19,7 +19,7 @@ select
|
||||
margin: 5px 0;
|
||||
padding: 5px;
|
||||
|
||||
background: #f7f7f7;
|
||||
background: $form-select-background-color;
|
||||
}
|
||||
|
||||
&.invalid {
|
||||
@@ -30,6 +30,10 @@ select
|
||||
.opblock-body select
|
||||
{
|
||||
min-width: 230px;
|
||||
@media (max-width: 768px)
|
||||
{
|
||||
min-width: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
label
|
||||
@@ -53,9 +57,13 @@ input[type=file]
|
||||
margin: 5px 0;
|
||||
padding: 8px 10px;
|
||||
|
||||
border: 1px solid #d9d9d9;
|
||||
border: 1px solid $form-input-border-color;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
background: $form-input-background-color;
|
||||
@media (max-width: 768px) {
|
||||
max-width: 175px;
|
||||
}
|
||||
|
||||
|
||||
&.invalid
|
||||
{
|
||||
@@ -102,13 +110,13 @@ textarea
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
background: rgba(#fff,.8);
|
||||
background: rgba($form-textarea-background-color,.8);
|
||||
|
||||
@include text_code();
|
||||
|
||||
&:focus
|
||||
{
|
||||
border: 2px solid $_color-get;
|
||||
border: 2px solid $form-textarea-focus-border-color;
|
||||
}
|
||||
|
||||
&.curl
|
||||
@@ -122,9 +130,9 @@ textarea
|
||||
resize: none;
|
||||
|
||||
border-radius: 4px;
|
||||
background: #41444e;
|
||||
background: $form-textarea-curl-background-color;
|
||||
|
||||
@include text_code(#fff);
|
||||
@include text_code($form-textarea-curl-font-color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +143,7 @@ textarea
|
||||
|
||||
transition: opacity .5s;
|
||||
|
||||
color: #333;
|
||||
color: $form-checkbox-label-font-color;
|
||||
|
||||
label
|
||||
{
|
||||
@@ -171,8 +179,8 @@ textarea
|
||||
cursor: pointer;
|
||||
|
||||
border-radius: 1px;
|
||||
background: #e8e8e8;
|
||||
box-shadow: 0 0 0 2px #e8e8e8;
|
||||
background: $form-checkbox-background-color;
|
||||
box-shadow: 0 0 0 2px $form-checkbox-box-shadow-color;
|
||||
|
||||
flex: none;
|
||||
|
||||
@@ -184,7 +192,7 @@ textarea
|
||||
|
||||
&:checked + label > .item
|
||||
{
|
||||
background: #e8e8e8 url(data:image/svg+xml,%0A%3Csvg%20width%3D%2210px%22%20height%3D%228px%22%20viewBox%3D%223%207%2010%208%22%20version%3D%221.1%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%3E%0A%20%20%20%20%3C%21--%20Generator%3A%20Sketch%2042%20%2836781%29%20-%20http%3A//www.bohemiancoding.com/sketch%20--%3E%0A%20%20%20%20%3Cdesc%3ECreated%20with%20Sketch.%3C/desc%3E%0A%20%20%20%20%3Cdefs%3E%3C/defs%3E%0A%20%20%20%20%3Cpolygon%20id%3D%22Rectangle-34%22%20stroke%3D%22none%22%20fill%3D%22%2341474E%22%20fill-rule%3D%22evenodd%22%20points%3D%226.33333333%2015%203%2011.6666667%204.33333333%2010.3333333%206.33333333%2012.3333333%2011.6666667%207%2013%208.33333333%22%3E%3C/polygon%3E%0A%3C/svg%3E) center center no-repeat;
|
||||
background: $form-checkbox-background-color url(data:image/svg+xml,%0A%3Csvg%20width%3D%2210px%22%20height%3D%228px%22%20viewBox%3D%223%207%2010%208%22%20version%3D%221.1%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%3E%0A%20%20%20%20%3C%21--%20Generator%3A%20Sketch%2042%20%2836781%29%20-%20http%3A//www.bohemiancoding.com/sketch%20--%3E%0A%20%20%20%20%3Cdesc%3ECreated%20with%20Sketch.%3C/desc%3E%0A%20%20%20%20%3Cdefs%3E%3C/defs%3E%0A%20%20%20%20%3Cpolygon%20id%3D%22Rectangle-34%22%20stroke%3D%22none%22%20fill%3D%22%2341474E%22%20fill-rule%3D%22evenodd%22%20points%3D%226.33333333%2015%203%2011.6666667%204.33333333%2010.3333333%206.33333333%2012.3333333%2011.6666667%207%2013%208.33333333%22%3E%3C/polygon%3E%0A%3C/svg%3E) center center no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,9 +30,9 @@
|
||||
padding: 3px 5px;
|
||||
|
||||
border-radius: 4px;
|
||||
background: rgba(#000,.05);
|
||||
background: rgba($info-code-background-color,.05);
|
||||
|
||||
@include text_code(#9012fe);
|
||||
@include text_code($info-code-font-color);
|
||||
}
|
||||
|
||||
a
|
||||
@@ -41,11 +41,11 @@
|
||||
|
||||
transition: all .4s;
|
||||
|
||||
@include text_body(#4990e2);
|
||||
@include text_body($info-link-font-color);
|
||||
|
||||
&:hover
|
||||
{
|
||||
color: darken(#4990e2, 15%);
|
||||
color: darken($info-link-font-color-hover, 15%);
|
||||
}
|
||||
}
|
||||
> div
|
||||
@@ -86,13 +86,13 @@
|
||||
vertical-align: super;
|
||||
|
||||
border-radius: 57px;
|
||||
background: #7d8492;
|
||||
background: $info-title-small-background-color;
|
||||
|
||||
pre
|
||||
{
|
||||
margin: 0;
|
||||
|
||||
@include text_headline(#fff);
|
||||
@include text_headline($info-title-small-pre-font-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,11 +34,11 @@
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
|
||||
border-bottom: 1px solid rgba(#3b4151, .3);
|
||||
border-bottom: 1px solid rgba($opblock-tag-border-bottom-color, .3);
|
||||
|
||||
&:hover
|
||||
{
|
||||
background: rgba(#000,.02);
|
||||
background: rgba($opblock-tag-background-color-hover,.02);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,9 +127,9 @@
|
||||
{
|
||||
margin: 0 0 15px 0;
|
||||
|
||||
border: 1px solid #000;
|
||||
border: 1px solid $opblock-border-color;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 3px rgba(#000,.19);
|
||||
box-shadow: 0 0 3px rgba($opblock-box-shadow-color,.19);
|
||||
|
||||
.tab-header
|
||||
{
|
||||
@@ -168,7 +168,7 @@
|
||||
content: '';
|
||||
transform: translateX(-50%);
|
||||
|
||||
background: #888;
|
||||
background: $opblock-tab-header-tab-item-active-h4-span-after-background-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -181,7 +181,7 @@
|
||||
{
|
||||
.opblock-summary
|
||||
{
|
||||
border-bottom: 1px solid #000;
|
||||
border-bottom: 1px solid $opblock-isopen-summary-border-bottom-color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,8 +194,8 @@
|
||||
|
||||
min-height: 50px;
|
||||
|
||||
background: rgba(#fff,.8);
|
||||
box-shadow: 0 1px 2px rgba(#000,.1);
|
||||
background: rgba($opblock-isopen-section-header-background-color,.8);
|
||||
box-shadow: 0 1px 2px rgba($opblock-isopen-section-header-box-shadow-color,.1);
|
||||
|
||||
label
|
||||
{
|
||||
@@ -239,10 +239,10 @@
|
||||
text-align: center;
|
||||
|
||||
border-radius: 3px;
|
||||
background: #000;
|
||||
text-shadow: 0 1px 0 rgba(#000,.1);
|
||||
background: $opblock-summary-method-background-color;
|
||||
text-shadow: 0 1px 0 rgba($opblock-summary-method-text-shadow-color,.1);
|
||||
|
||||
@include text_headline(#fff);
|
||||
@include text_headline($opblock-summary-method-font-color);
|
||||
}
|
||||
|
||||
.opblock-summary-path,
|
||||
@@ -250,6 +250,10 @@
|
||||
.opblock-summary-path__deprecated
|
||||
{
|
||||
font-size: 16px;
|
||||
@media (max-width: 768px) {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
display: flex;
|
||||
flex: 0 3 auto;
|
||||
@@ -373,7 +377,7 @@
|
||||
margin: 20px 0;
|
||||
padding: 10px 10px;
|
||||
|
||||
border: 2px solid #d8dde7;
|
||||
border: 2px solid $operational-filter-input-border-color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -416,7 +420,7 @@
|
||||
|
||||
content: '';
|
||||
|
||||
background: rgba(#000,.2);
|
||||
background: rgba($tab-list-item-first-background-color,.2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,7 +525,7 @@
|
||||
{
|
||||
font-size: 11px;
|
||||
|
||||
@include text_code(#999);
|
||||
@include text_code($response-col-status-undocumented-font-color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,40 +541,47 @@
|
||||
{
|
||||
font-size: 11px;
|
||||
|
||||
@include text_code(#999);
|
||||
@include text_code($response-col-links-font-color);
|
||||
}
|
||||
}
|
||||
|
||||
.response-col_description__inner
|
||||
{
|
||||
span
|
||||
div.markdown, div.renderedMarkdown
|
||||
{
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
|
||||
display: block;
|
||||
|
||||
margin: 10px 0;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
|
||||
border-radius: 4px;
|
||||
background: #41444e;
|
||||
background: $response-col-description-inner-markdown-background-color;
|
||||
|
||||
@include text_code(#fff);
|
||||
@include text_code($response-col-description-inner-markdown-font-color);
|
||||
|
||||
p
|
||||
{
|
||||
margin: 0;
|
||||
@include text_code($response-col-description-inner-markdown-font-color);
|
||||
}
|
||||
|
||||
a
|
||||
{
|
||||
@include text_code(#89bf04);
|
||||
@include text_code($response-col-description-inner-markdown-link-font-color);
|
||||
text-decoration: underline;
|
||||
&:hover {
|
||||
color: #81b10c;
|
||||
color: $response-col-description-inner-markdown-link-font-color-hover;
|
||||
}
|
||||
}
|
||||
|
||||
th
|
||||
{
|
||||
@include text_code($response-col-description-inner-markdown-font-color);
|
||||
border-bottom: 1px solid $response-col-description-inner-markdown-font-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,13 +600,13 @@
|
||||
hyphens: auto;
|
||||
|
||||
border-radius: 4px;
|
||||
background: #41444e;
|
||||
background: $opblock-body-background-color;
|
||||
|
||||
overflow-wrap: break-word;
|
||||
@include text_code(#fff);
|
||||
@include text_code($opblock-body-font-color);
|
||||
span
|
||||
{
|
||||
color: #fff !important;
|
||||
color: $opblock-body-font-color !important;
|
||||
}
|
||||
|
||||
.headerline
|
||||
@@ -609,8 +620,8 @@
|
||||
margin: 0 0 20px 0;
|
||||
padding: 30px 0;
|
||||
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 2px 0 rgba(0,0,0,.15);
|
||||
background: $scheme-container-background-color;
|
||||
box-shadow: 0 1px 2px 0 rgba($scheme-container-box-shadow-color,.15);
|
||||
|
||||
.schemes
|
||||
{
|
||||
@@ -639,78 +650,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.server-container
|
||||
{
|
||||
margin: 0 0 20px 0;
|
||||
padding: 30px 0;
|
||||
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 2px 0 rgba(0,0,0,.15);
|
||||
|
||||
.computed-url {
|
||||
margin: 2em 0;
|
||||
|
||||
code {
|
||||
color: grey;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
font-size: 16px;
|
||||
margin: 0 1em;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.servers
|
||||
{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.servers-title {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
> label
|
||||
{
|
||||
font-size: 12px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
margin: -20px 15px 0 0;
|
||||
|
||||
@include text_headline();
|
||||
|
||||
select
|
||||
{
|
||||
min-width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
tr {
|
||||
width: 30em;
|
||||
}
|
||||
td {
|
||||
display: inline-block;
|
||||
max-width: 15em;
|
||||
vertical-align: middle;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
&:first-of-type {
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.loading-container
|
||||
{
|
||||
padding: 40px 0 60px;
|
||||
@@ -751,8 +690,8 @@
|
||||
animation: rotation 1s infinite linear, opacity .5s;
|
||||
|
||||
opacity: 1;
|
||||
border: 2px solid rgba(#555, .1);
|
||||
border-top-color: rgba(#000, .6);
|
||||
border: 2px solid rgba($loading-container-before-border-color, .1);
|
||||
border-top-color: rgba($loading-container-before-border-top-color, .6);
|
||||
border-radius: 100%;
|
||||
|
||||
backface-visibility: hidden;
|
||||
@@ -768,24 +707,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.renderedMarkdown {
|
||||
p {
|
||||
@include text_body();
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.response-content-type {
|
||||
padding-top: 1em;
|
||||
|
||||
&.controls-accept-header {
|
||||
select {
|
||||
border-color: green;
|
||||
border-color: $response-content-type-controls-accept-header-select-border-color;
|
||||
}
|
||||
|
||||
small {
|
||||
color: green;
|
||||
color: $response-content-type-controls-accept-header-small-font-color;
|
||||
font-size: .7em;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
background: rgba(#000,.8);
|
||||
background: rgba($dialog-ux-backdrop-background-color,.8);
|
||||
}
|
||||
|
||||
.modal-ux
|
||||
@@ -31,10 +31,10 @@
|
||||
|
||||
transform: translate(-50%,-50%);
|
||||
|
||||
border: 1px solid #ebebeb;
|
||||
border: 1px solid $dialog-ux-modal-border-color;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
box-shadow: 0 10px 30px 0 rgba(0,0,0,.20);
|
||||
background: $dialog-ux-modal-background-color;
|
||||
box-shadow: 0 10px 30px 0 rgba($dialog-ux-modal-box-shadow-color,.20);
|
||||
}
|
||||
|
||||
.modal-ux-content
|
||||
@@ -50,7 +50,7 @@
|
||||
|
||||
margin: 0 0 5px 0;
|
||||
|
||||
color: #41444e;
|
||||
color: $dialog-ux-modal-content-font-color;
|
||||
|
||||
@include text_body();
|
||||
}
|
||||
@@ -72,7 +72,7 @@
|
||||
|
||||
padding: 12px 0;
|
||||
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
border-bottom: 1px solid $dialog-ux-modal-header-border-bottom-color;
|
||||
|
||||
align-items: center;
|
||||
|
||||
|
||||
@@ -3,14 +3,20 @@
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
|
||||
@include text_code();
|
||||
|
||||
.deprecated
|
||||
{
|
||||
span, td {
|
||||
color: #aaa !important;
|
||||
}
|
||||
}
|
||||
span,
|
||||
td
|
||||
{
|
||||
color: $model-deprecated-font-color !important;
|
||||
}
|
||||
|
||||
@include text_code();
|
||||
> td:first-of-type {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
&-toggle
|
||||
{
|
||||
font-size: 10px;
|
||||
@@ -82,12 +88,13 @@
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
color: #ebebeb;
|
||||
color: $model-hint-font-color;
|
||||
border-radius: 4px;
|
||||
background: rgba(#000,.7);
|
||||
background: rgba($model-hint-background-color,.7);
|
||||
}
|
||||
|
||||
p {
|
||||
p
|
||||
{
|
||||
margin: 0 0 1em 0;
|
||||
}
|
||||
}
|
||||
@@ -97,7 +104,7 @@ section.models
|
||||
{
|
||||
margin: 30px 0;
|
||||
|
||||
border: 1px solid rgba(#3b4151, .3);
|
||||
border: 1px solid rgba($section-models-border-color, .3);
|
||||
border-radius: 4px;
|
||||
|
||||
&.is-open
|
||||
@@ -106,7 +113,8 @@ section.models
|
||||
h4
|
||||
{
|
||||
margin: 0 0 5px 0;
|
||||
border-bottom: 1px solid rgba(#3b4151, .3);
|
||||
|
||||
border-bottom: 1px solid rgba($section-models-isopen-h4-border-bottom-color, .3);
|
||||
}
|
||||
}
|
||||
h4
|
||||
@@ -114,6 +122,7 @@ section.models
|
||||
font-size: 16px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
margin: 0;
|
||||
padding: 10px 20px 10px 10px;
|
||||
@@ -121,8 +130,7 @@ section.models
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
|
||||
@include text_headline(#777);
|
||||
align-items: center;
|
||||
@include text_headline($section-models-h4-font-color);
|
||||
|
||||
svg
|
||||
{
|
||||
@@ -136,7 +144,7 @@ section.models
|
||||
|
||||
&:hover
|
||||
{
|
||||
background: rgba(#000,.02);
|
||||
background: rgba($section-models-h4-background-color-hover,.02);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,7 +154,7 @@ section.models
|
||||
|
||||
margin: 0 0 10px 0;
|
||||
|
||||
@include text_headline(#777);
|
||||
@include text_headline($section-models-h5-font-color);
|
||||
}
|
||||
|
||||
.model-jump-to-path
|
||||
@@ -162,11 +170,11 @@ section.models
|
||||
transition: all .5s;
|
||||
|
||||
border-radius: 4px;
|
||||
background: rgba(#000,.05);
|
||||
background: rgba($section-models-model-container-background-color,.05);
|
||||
|
||||
&:hover
|
||||
{
|
||||
background: rgba(#000,.07);
|
||||
background: rgba($section-models-model-container-background-color,.07);
|
||||
}
|
||||
|
||||
&:first-of-type
|
||||
@@ -192,7 +200,7 @@ section.models
|
||||
padding: 10px;
|
||||
|
||||
border-radius: 4px;
|
||||
background: rgba(#000,.1);
|
||||
background: rgba($section-models-model-box-background-color,.1);
|
||||
|
||||
.model-jump-to-path
|
||||
{
|
||||
@@ -202,7 +210,7 @@ section.models
|
||||
|
||||
&.deprecated
|
||||
{
|
||||
opacity: .5;
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,21 +219,23 @@ section.models
|
||||
{
|
||||
font-size: 16px;
|
||||
|
||||
@include text_headline(#555);
|
||||
@include text_headline($section-models-model-title-font-color);
|
||||
}
|
||||
|
||||
.model-deprecated-warning
|
||||
{
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
|
||||
margin-right: 1em;
|
||||
|
||||
@include text_headline($_color-delete);
|
||||
}
|
||||
|
||||
|
||||
span
|
||||
{
|
||||
> span.model
|
||||
> span.model
|
||||
{
|
||||
.brace-close
|
||||
{
|
||||
@@ -237,13 +247,13 @@ span
|
||||
.prop-name
|
||||
{
|
||||
display: inline-block;
|
||||
|
||||
margin-right: 1em;
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
.prop-type
|
||||
{
|
||||
color: #55a;
|
||||
color: $prop-type-font-color;
|
||||
}
|
||||
|
||||
.prop-enum
|
||||
@@ -252,5 +262,5 @@ span
|
||||
}
|
||||
.prop-format
|
||||
{
|
||||
color: #999;
|
||||
color: $prop-format-font-color;
|
||||
}
|
||||
|
||||
74
src/style/_servers.scss
Normal file
74
src/style/_servers.scss
Normal file
@@ -0,0 +1,74 @@
|
||||
.servers
|
||||
{
|
||||
> label
|
||||
{
|
||||
font-size: 12px;
|
||||
|
||||
margin: -20px 15px 0 0;
|
||||
|
||||
@include text_headline();
|
||||
|
||||
select
|
||||
{
|
||||
min-width: 130px;
|
||||
}
|
||||
}
|
||||
|
||||
h4.message {
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
table {
|
||||
tr {
|
||||
width: 30em;
|
||||
}
|
||||
td {
|
||||
display: inline-block;
|
||||
max-width: 15em;
|
||||
vertical-align: middle;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
&:first-of-type {
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.computed-url {
|
||||
margin: 2em 0;
|
||||
|
||||
code {
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
font-size: 16px;
|
||||
margin: 0 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.global-server-container
|
||||
{
|
||||
margin: 0 0 20px 0;
|
||||
padding: 30px 0;
|
||||
|
||||
background: $server-container-background-color;
|
||||
box-shadow: 0 1px 2px 0 rgba($server-container-box-shadow-color,.15);
|
||||
|
||||
.servers-title {
|
||||
line-height: 2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.operation-servers {
|
||||
h4.message {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ table
|
||||
|
||||
&:first-of-type
|
||||
{
|
||||
width: 124px;
|
||||
width: 174px;
|
||||
padding: 0 0 0 2em;
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ table
|
||||
|
||||
text-align: left;
|
||||
|
||||
border-bottom: 1px solid rgba(#3b4151, .2);
|
||||
border-bottom: 1px solid rgba($table-thead-td-border-bottom-color, .2);
|
||||
|
||||
@include text_body();
|
||||
}
|
||||
@@ -126,17 +126,18 @@ table
|
||||
|
||||
content: 'required';
|
||||
|
||||
color: rgba(#f00, .6);
|
||||
color: rgba($table-parameter-name-required-font-color, .6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.parameter__in
|
||||
.parameter__in,
|
||||
.parameter__extension
|
||||
{
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
|
||||
@include text_code(#888);
|
||||
@include text_code($table-parameter-in-font-color);
|
||||
}
|
||||
|
||||
.parameter__deprecated
|
||||
@@ -144,7 +145,7 @@ table
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
|
||||
@include text_code(#f00);
|
||||
@include text_code($table-parameter-deprecated-font-color);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
.topbar
|
||||
{
|
||||
padding: 8px 30px;
|
||||
padding: 8px 0;
|
||||
|
||||
background-color: #89bf04;
|
||||
background-color: $topbar-background-color;
|
||||
.topbar-wrapper
|
||||
{
|
||||
display: flex;
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
text-decoration: none;
|
||||
|
||||
@include text_headline(#fff);
|
||||
@include text_headline($topbar-link-font-color);
|
||||
|
||||
span
|
||||
{
|
||||
@@ -39,10 +39,9 @@
|
||||
input[type=text]
|
||||
{
|
||||
width: 100%;
|
||||
min-width: 350px;
|
||||
margin: 0;
|
||||
|
||||
border: 2px solid #547f00;
|
||||
border: 2px solid $topbar-download-url-wrapper-element-border-color;
|
||||
border-radius: 4px 0 0 4px;
|
||||
outline: none;
|
||||
}
|
||||
@@ -72,7 +71,7 @@
|
||||
|
||||
width: 100%;
|
||||
|
||||
border: 2px solid #547f00;
|
||||
border: 2px solid $topbar-download-url-wrapper-element-border-color;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
@@ -84,13 +83,13 @@
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
|
||||
padding: 4px 40px;
|
||||
padding: 4px 30px;
|
||||
|
||||
border: none;
|
||||
border-radius: 0 4px 4px 0;
|
||||
background: #547f00;
|
||||
background: $topbar-download-url-button-background-color;
|
||||
|
||||
@include text_headline(#fff);
|
||||
@include text_headline($topbar-download-url-button-font-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
@mixin text_body($color: #3b4151)
|
||||
@mixin text_body($color: $text-body-default-font-color)
|
||||
{
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
|
||||
color: $color;
|
||||
}
|
||||
|
||||
@mixin text_code($color: #3b4151)
|
||||
@mixin text_code($color: $text-code-default-font-color)
|
||||
{
|
||||
font-family: 'Source Code Pro', monospace;
|
||||
font-weight: 600;
|
||||
@@ -13,7 +13,7 @@
|
||||
color: $color;
|
||||
}
|
||||
|
||||
@mixin text_headline($color: #3b4151)
|
||||
@mixin text_headline($color: $text-headline-default-font-color)
|
||||
{
|
||||
font-family: 'Titillium Web', sans-serif;
|
||||
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
|
||||
$gray-base: #000 !default;
|
||||
$white: #fff !default;
|
||||
$gray-50: #ebebeb !default;
|
||||
$gray-100: #d8dde7 !default;
|
||||
$gray-200: lighten($gray-base, 62.75%) !default; // #aaa
|
||||
$gray-300: lighten($gray-base, 56.5%) !default; // #999
|
||||
$gray-400: lighten($gray-base, 50%) !default; // #888
|
||||
$gray-500: lighten($gray-base, 43.75%) !default; // #777
|
||||
$gray-600: lighten($gray-base, 37.5%) !default; // #666
|
||||
$gray-650: lighten($gray-base, 33.3%) !default; // ##555555
|
||||
$gray-700: lighten($gray-base, 31.25%) !default; // #555
|
||||
$gray-800: lighten($gray-base, 25%) !default; // #444
|
||||
$gray-900: lighten($gray-base, 18.75%) !default; // #333
|
||||
|
||||
$gray-custom-1: #41444e !default;
|
||||
$gray-custom-2: #3b4151 !default;
|
||||
|
||||
$color-primary: #89bf04 !default;
|
||||
$color-secondary: #9012fe !default;
|
||||
$color-info: #4990e2 !default;
|
||||
$color-warning: #ff6060 !default;
|
||||
$color-danger: #f00 !default;
|
||||
|
||||
$_color-post: #49cc90 !default;
|
||||
$_color-get: #61affe !default;
|
||||
$_color-put: #fca130 !default;
|
||||
$_color-delete: #f93e3e !default;
|
||||
$_color-head: #9012fe !default;
|
||||
$_color-patch: #50e3c2 !default;
|
||||
$_color-disabled: #ebebeb !default;
|
||||
$_color-options: #0d5aa7 !default;
|
||||
|
||||
$color-green: #008000 !default;
|
||||
|
||||
$color-primary-hover: #81b10c !default;
|
||||
|
||||
// Authorize
|
||||
|
||||
$auth-container-border-color: $gray-50 !default;
|
||||
|
||||
// Buttons
|
||||
|
||||
$btn-background-color: transparent !default;
|
||||
$btn-border-color: $gray-400 !default;
|
||||
$btn-font-color: inherit !default;
|
||||
$btn-box-shadow-color: $gray-base !default;
|
||||
|
||||
$btn-authorize-background-color: transparent !default;
|
||||
$btn-authorize-border-color: $_color-post !default;
|
||||
$btn-authorize-font-color: $_color-post !default;
|
||||
$btn-authorize-svg-fill-color: $_color-post !default;
|
||||
|
||||
$btn-cancel-background-color: transparent !default;
|
||||
$btn-cancel-border-color: $color-warning !default;
|
||||
$btn-cancel-font-color: $color-warning !default;
|
||||
|
||||
$btn-execute-background-color: transparent !default;
|
||||
$btn-execute-border-color: $color-info !default;
|
||||
$btn-execute-font-color: $white !default;
|
||||
$btn-execute-background-color-alt: $color-info !default;
|
||||
|
||||
$expand-methods-svg-fill-color: $gray-500 !default;
|
||||
$expand-methods-svg-fill-color-hover: $gray-800 !default;
|
||||
|
||||
// Errors
|
||||
|
||||
$errors-wrapper-background-color: $_color-delete !default;
|
||||
$errors-wrapper-border-color: $_color-delete !default;
|
||||
|
||||
$errors-wrapper-errors-small-font-color: $gray-600 !default;
|
||||
|
||||
// Form
|
||||
|
||||
$form-select-background-color: #f7f7f7 !default;
|
||||
$form-select-border-color: $gray-custom-1 !default;
|
||||
$form-select-box-shadow-color: $gray-base !default;
|
||||
|
||||
$form-input-border-color: #d9d9d9 !default;
|
||||
$form-input-background-color: $white !default;
|
||||
|
||||
$form-textarea-background-color: $white !default;
|
||||
$form-textarea-focus-border-color: $_color-get !default;
|
||||
|
||||
$form-textarea-curl-background-color: $gray-custom-1 !default;
|
||||
$form-textarea-curl-font-color: $white !default;
|
||||
|
||||
$form-checkbox-label-font-color: $gray-900 !default;
|
||||
$form-checkbox-background-color: #e8e8e8 !default;
|
||||
$form-checkbox-box-shadow-color: #e8e8e8 !default;
|
||||
|
||||
// Information
|
||||
|
||||
$info-code-background-color: $gray-base !default;
|
||||
$info-code-font-color: $_color-head !default;
|
||||
|
||||
$info-link-font-color: $color-info !default;
|
||||
$info-link-font-color-hover: $info-link-font-color !default;
|
||||
|
||||
$info-title-small-background-color: #7d8492 !default;
|
||||
|
||||
$info-title-small-pre-font-color: $white !default;
|
||||
|
||||
// Layout
|
||||
|
||||
$opblock-border-color: $gray-base !default;
|
||||
$opblock-box-shadow-color: $gray-base !default;
|
||||
|
||||
$opblock-tag-border-bottom-color: $gray-custom-2 !default;
|
||||
$opblock-tag-background-color-hover: $gray-base !default;
|
||||
|
||||
$opblock-tab-header-tab-item-active-h4-span-after-background-color: $gray-400 !default;
|
||||
|
||||
$opblock-isopen-summary-border-bottom-color: $gray-base !default;
|
||||
|
||||
$opblock-isopen-section-header-background-color: $white !default;
|
||||
$opblock-isopen-section-header-box-shadow-color: $gray-base !default;
|
||||
|
||||
$opblock-summary-method-background-color: $gray-base !default;
|
||||
$opblock-summary-method-font-color: $white !default;
|
||||
$opblock-summary-method-text-shadow-color: $gray-base !default;
|
||||
|
||||
$operational-filter-input-border-color: #d8dde7 !default;
|
||||
|
||||
$tab-list-item-first-background-color: $gray-base !default;
|
||||
|
||||
$response-col-status-undocumented-font-color: $gray-300 !default;
|
||||
|
||||
$response-col-links-font-color: $gray-300 !default;
|
||||
|
||||
$response-col-description-inner-markdown-font-color: $white !default;
|
||||
$response-col-description-inner-markdown-background-color: $gray-custom-1 !default;
|
||||
|
||||
$response-col-description-inner-markdown-link-font-color: $color-primary !default;
|
||||
$response-col-description-inner-markdown-link-font-color-hover: $color-primary-hover !default;
|
||||
|
||||
$opblock-body-background-color: $gray-custom-1 !default;
|
||||
$opblock-body-font-color: $white !default;
|
||||
|
||||
$scheme-container-background-color: $white !default;
|
||||
$scheme-container-box-shadow-color: $gray-base !default;
|
||||
|
||||
$server-container-background-color: $white !default;
|
||||
$server-container-box-shadow-color: $gray-base !default;
|
||||
|
||||
$server-container-computed-url-code-font-color: $gray-400 !default;
|
||||
|
||||
$loading-container-before-border-color: $gray-650 !default;
|
||||
$loading-container-before-border-top-color: $gray-base !default;
|
||||
|
||||
$response-content-type-controls-accept-header-select-border-color: $color-green !default;
|
||||
$response-content-type-controls-accept-header-small-font-color: $color-green !default;
|
||||
|
||||
// Modal
|
||||
|
||||
$dialog-ux-backdrop-background-color: $gray-base !default;
|
||||
|
||||
|
||||
$dialog-ux-modal-background-color: $white !default;
|
||||
$dialog-ux-modal-border-color: $gray-50 !default;
|
||||
$dialog-ux-modal-box-shadow-color: $gray-base !default;
|
||||
|
||||
$dialog-ux-modal-content-font-color: $gray-custom-1 !default;
|
||||
|
||||
$dialog-ux-modal-header-border-bottom-color: $gray-50 !default;
|
||||
|
||||
// Models
|
||||
|
||||
$model-deprecated-font-color: $gray-200 !default;
|
||||
|
||||
$model-hint-font-color: $gray-50 !default;
|
||||
$model-hint-background-color: $gray-base !default;
|
||||
|
||||
$section-models-border-color: $gray-custom-2 !default;
|
||||
|
||||
$section-models-isopen-h4-border-bottom-color: $section-models-border-color !default;
|
||||
|
||||
$section-models-h4-font-color: $gray-500 !default;
|
||||
$section-models-h4-background-color-hover: $gray-base !default;
|
||||
|
||||
$section-models-h5-font-color: $gray-500 !default;
|
||||
|
||||
$section-models-model-container-background-color: $gray-base !default;
|
||||
|
||||
$section-models-model-box-background-color: $gray-base !default;
|
||||
|
||||
$section-models-model-title-font-color: $gray-700 !default;
|
||||
|
||||
$prop-type-font-color: #55a !default;
|
||||
|
||||
$prop-format-font-color: $gray-300 !default;
|
||||
|
||||
// Tables
|
||||
|
||||
$table-thead-td-border-bottom-color: $gray-custom-2 !default;
|
||||
|
||||
$table-parameter-name-required-font-color: $color-danger !default;
|
||||
|
||||
$table-parameter-in-font-color: $gray-400 !default;
|
||||
|
||||
$table-parameter-deprecated-font-color: $color-danger !default;
|
||||
|
||||
// Topbar
|
||||
|
||||
$topbar-background-color: $color-primary !default;
|
||||
|
||||
$topbar-link-font-color: $white !default;
|
||||
|
||||
$topbar-download-url-wrapper-element-border-color: #547f00 !default;
|
||||
|
||||
$topbar-download-url-button-background-color: #547f00 !default;
|
||||
$topbar-download-url-button-font-color: $white !default;
|
||||
|
||||
// Type
|
||||
|
||||
$text-body-default-font-color: $gray-custom-2 !default;
|
||||
$text-code-default-font-color: $gray-custom-2 !default;
|
||||
$text-headline-default-font-color: $gray-custom-2 !default;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
@import 'form';
|
||||
@import 'modal';
|
||||
@import 'models';
|
||||
@import 'servers';
|
||||
@import 'table';
|
||||
@import 'topbar';
|
||||
@import 'information';
|
||||
|
||||
Reference in New Issue
Block a user