Merge branch 'master' into bug/3897-markdown-table-headings

This commit is contained in:
kyle
2017-11-17 20:47:34 -08:00
committed by GitHub
22 changed files with 604 additions and 198 deletions

View File

@@ -45,6 +45,7 @@
"brace": "0.7.0", "brace": "0.7.0",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"commonmark": "^0.28.1", "commonmark": "^0.28.1",
"core-js": "^2.5.1",
"css.escape": "1.5.1", "css.escape": "1.5.1",
"deep-extend": "0.4.1", "deep-extend": "0.4.1",
"expect": "1.20.2", "expect": "1.20.2",
@@ -70,7 +71,7 @@
"react-motion": "^0.5.2", "react-motion": "^0.5.2",
"react-object-inspector": "0.2.1", "react-object-inspector": "0.2.1",
"react-redux": "^4.x.x", "react-redux": "^4.x.x",
"react-split-pane": "0.1.57", "react-split-pane": "0.1.70",
"redux": "^3.x.x", "redux": "^3.x.x",
"redux-immutable": "3.0.8", "redux-immutable": "3.0.8",
"redux-logger": "*", "redux-logger": "*",

View File

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

View File

@@ -27,6 +27,16 @@ export default class ContentType extends React.Component {
} }
} }
componentWillReceiveProps(nextProps) {
if(!nextProps.contentTypes || !nextProps.contentTypes.size) {
return
}
if(!nextProps.contentTypes.includes(nextProps.value)) {
nextProps.onChange(nextProps.contentTypes.first())
}
}
onChangeWrapper = e => this.props.onChange(e.target.value) onChangeWrapper = e => this.props.onChange(e.target.value)
render() { render() {

View File

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

View File

@@ -1,6 +1,7 @@
import React from "react" import React from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes" import ImPropTypes from "react-immutable-proptypes"
import { Iterable } from "immutable"
const Headers = ( { headers } )=>{ const Headers = ( { headers } )=>{
return ( return (
@@ -28,19 +29,29 @@ Duration.propTypes = {
export default class LiveResponse extends React.Component { export default class LiveResponse extends React.Component {
static propTypes = { static propTypes = {
response: PropTypes.object.isRequired, response: PropTypes.instanceOf(Iterable).isRequired,
specSelectors: PropTypes.object.isRequired, path: PropTypes.string.isRequired,
pathMethod: PropTypes.object.isRequired, method: PropTypes.string.isRequired,
getComponent: PropTypes.func.isRequired,
displayRequestDuration: PropTypes.bool.isRequired, displayRequestDuration: PropTypes.bool.isRequired,
specSelectors: PropTypes.object.isRequired,
getComponent: PropTypes.func.isRequired,
getConfigs: 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() { render() {
const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, pathMethod } = this.props const { response, getComponent, getConfigs, displayRequestDuration, specSelectors, path, method } = this.props
const { showMutatedRequest } = getConfigs() 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 status = response.get("status")
const url = response.get("url") const url = response.get("url")
const headers = response.get("headers").toJS() const headers = response.get("headers").toJS()
@@ -118,7 +129,6 @@ export default class LiveResponse extends React.Component {
static propTypes = { static propTypes = {
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
request: ImPropTypes.map,
response: ImPropTypes.map response: ImPropTypes.map
} }
} }

View File

@@ -76,13 +76,14 @@ export default class ObjectModel extends Component {
{ {
!(properties && properties.size) ? null : properties.entrySeq().map( !(properties && properties.size) ? null : properties.entrySeq().map(
([key, value]) => { ([key, value]) => {
let isDeprecated = isOAS3() && value.get("deprecated")
let isRequired = List.isList(requiredProperties) && requiredProperties.contains(key) let isRequired = List.isList(requiredProperties) && requiredProperties.contains(key)
let propertyStyle = { verticalAlign: "top", paddingRight: "0.2em" } let propertyStyle = { verticalAlign: "top", paddingRight: "0.2em" }
if ( isRequired ) { if ( isRequired ) {
propertyStyle.fontWeight = "bold" propertyStyle.fontWeight = "bold"
} }
return (<tr key={key}> return (<tr key={key} className={isDeprecated && "deprecated"}>
<td style={ propertyStyle }> <td style={ propertyStyle }>
{ key }{ isRequired && <span style={{ color: "red" }}>*</span> } { key }{ isRequired && <span style={{ color: "red" }}>*</span> }
</td> </td>

View File

@@ -1,32 +1,22 @@
import React, { PureComponent } from "react" import React, { PureComponent } from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import { getList } from "core/utils" import { getList } from "core/utils"
import * as CustomPropTypes from "core/proptypes"
import { sanitizeUrl } from "core/utils" import { sanitizeUrl } from "core/utils"
import { Iterable } from "immutable"
//import "less/opblock"
export default class Operation extends PureComponent { export default class Operation extends PureComponent {
static propTypes = { static propTypes = {
path: PropTypes.string.isRequired, operation: PropTypes.instanceOf(Iterable).isRequired,
method: PropTypes.string.isRequired, response: PropTypes.instanceOf(Iterable),
operation: PropTypes.object.isRequired, request: PropTypes.instanceOf(Iterable),
showSummary: PropTypes.bool,
isShown: PropTypes.bool.isRequired,
tagKey: PropTypes.string, toggleShown: PropTypes.func.isRequired,
operationKey: PropTypes.string, onTryoutClick: PropTypes.func.isRequired,
jumpToKey: CustomPropTypes.arrayOrString.isRequired, onCancelClick: PropTypes.func.isRequired,
onExecute: PropTypes.func.isRequired,
allowTryItOut: PropTypes.bool,
displayOperationId: PropTypes.bool,
displayRequestDuration: PropTypes.bool,
response: PropTypes.object,
request: PropTypes.object,
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired,
authActions: PropTypes.object, authActions: PropTypes.object,
authSelectors: PropTypes.object, authSelectors: PropTypes.object,
specActions: PropTypes.object.isRequired, specActions: PropTypes.object.isRequired,
@@ -34,88 +24,66 @@ export default class Operation extends PureComponent {
oas3Actions: PropTypes.object.isRequired, oas3Actions: PropTypes.object.isRequired,
layoutActions: PropTypes.object.isRequired, layoutActions: PropTypes.object.isRequired,
layoutSelectors: PropTypes.object.isRequired, layoutSelectors: PropTypes.object.isRequired,
fn: PropTypes.object.isRequired, fn: PropTypes.object.isRequired
getConfigs: PropTypes.func.isRequired
} }
static defaultProps = { static defaultProps = {
showSummary: true, operation: null,
response: null, response: null,
allowTryItOut: true, request: null
displayOperationId: false,
displayRequestDuration: false
}
constructor(props, context) {
super(props, context)
this.state = {
tryItOutEnabled: false
}
}
componentWillReceiveProps(nextProps) {
if(nextProps.response !== this.props.response) {
this.setState({ executeInProgress: false })
}
}
toggleShown =() => {
let { layoutActions, tagKey, operationKey, isShown } = this.props
const isShownKey = ["operations", tagKey, operationKey]
layoutActions.show(isShownKey, !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() { render() {
let { let {
operationKey,
tagKey,
isShown,
jumpToKey,
path,
method,
operation,
showSummary,
response, response,
request, request,
allowTryItOut, toggleShown,
displayOperationId, onTryoutClick,
displayRequestDuration, onCancelClick,
onExecute,
fn, fn,
getComponent, getComponent,
getConfigs,
specActions, specActions,
specSelectors, specSelectors,
authActions, authActions,
authSelectors, authSelectors,
getConfigs,
oas3Actions oas3Actions
} = this.props } = this.props
let operationProps = this.props.operation
let summary = operation.get("summary") let {
let description = operation.get("description") isShown,
let deprecated = operation.get("deprecated") jumpToKey,
let externalDocs = operation.get("externalDocs") 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 responses = operation.get("responses") let responses = operation.get("responses")
let security = operation.get("security") || specSelectors.security()
let produces = operation.get("produces") let produces = operation.get("produces")
let schemes = operation.get("schemes") let security = operation.get("security") || specSelectors.security()
let parameters = getList(operation, ["parameters"]) let parameters = getList(operation, ["parameters"])
let operationId = operation.get("__originalOperationId")
let operationScheme = specSelectors.operationScheme(path, method) let operationScheme = specSelectors.operationScheme(path, method)
let isShownKey = ["operations", tag, operationId]
const Responses = getComponent("responses") const Responses = getComponent("responses")
const Parameters = getComponent( "parameters" ) const Parameters = getComponent( "parameters" )
@@ -127,28 +95,23 @@ export default class Operation extends PureComponent {
const Markdown = getComponent( "Markdown" ) const Markdown = getComponent( "Markdown" )
const Schemes = getComponent( "schemes" ) const Schemes = getComponent( "schemes" )
const { deepLinking } = getConfigs()
const isDeepLinkingEnabled = deepLinking && deepLinking !== "false"
// Merge in Live Response // Merge in Live Response
if(responses && response && response.size > 0) { if(responses && response && response.size > 0) {
let notDocumented = !responses.get(String(response.get("status"))) let notDocumented = !responses.get(String(response.get("status")))
response = response.set("notDocumented", notDocumented) response = response.set("notDocumented", notDocumented)
} }
let { tryItOutEnabled } = this.state
let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method ) let onChangeKey = [ path, method ] // Used to add values to _this_ operation ( indexed by path and method )
return ( return (
<div className={deprecated ? "opblock opblock-deprecated" : isShown ? `opblock opblock-${method} is-open` : `opblock opblock-${method}`} id={`operations-${tagKey}-${operationKey}`} > <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={this.toggleShown} > <div className={`opblock-summary opblock-summary-${method}`} onClick={toggleShown} >
<span className="opblock-summary-method">{method.toUpperCase()}</span> <span className="opblock-summary-method">{method.toUpperCase()}</span>
<span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } > <span className={ deprecated ? "opblock-summary-path__deprecated" : "opblock-summary-path" } >
<a <a
className="nostyle" className="nostyle"
onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null} onClick={isDeepLinkingEnabled ? (e) => e.preventDefault() : null}
href={isDeepLinkingEnabled ? `#/${tagKey}/${operationKey}` : null}> href={isDeepLinkingEnabled ? `#/${isShownKey.join("/")}` : null}>
<span>{path}</span> <span>{path}</span>
</a> </a>
<JumpToPath path={jumpToKey} /> <JumpToPath path={jumpToKey} />
@@ -200,8 +163,8 @@ export default class Operation extends PureComponent {
parameters={parameters} parameters={parameters}
operation={operation} operation={operation}
onChangeKey={onChangeKey} onChangeKey={onChangeKey}
onTryoutClick = { this.onTryoutClick } onTryoutClick = { onTryoutClick }
onCancelClick = { this.onCancelClick } onCancelClick = { onCancelClick }
tryItOutEnabled = { tryItOutEnabled } tryItOutEnabled = { tryItOutEnabled }
allowTryItOut={allowTryItOut} allowTryItOut={allowTryItOut}
@@ -226,25 +189,23 @@ export default class Operation extends PureComponent {
{ !tryItOutEnabled || !allowTryItOut ? null : { !tryItOutEnabled || !allowTryItOut ? null :
<Execute <Execute
getComponent={getComponent}
operation={ operation } operation={ operation }
specActions={ specActions } specActions={ specActions }
specSelectors={ specSelectors } specSelectors={ specSelectors }
path={ path } path={ path }
method={ method } method={ method }
onExecute={ this.onExecute } /> onExecute={ onExecute } />
} }
{ (!tryItOutEnabled || !response || !allowTryItOut) ? null : { (!tryItOutEnabled || !response || !allowTryItOut) ? null :
<Clear <Clear
onClick={ this.onClearClick }
specActions={ specActions } specActions={ specActions }
path={ path } path={ path }
method={ method }/> method={ method }/>
} }
</div> </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 ? null :
<Responses <Responses
@@ -258,7 +219,8 @@ export default class Operation extends PureComponent {
specActions={ specActions } specActions={ specActions }
produces={ produces } produces={ produces }
producesValue={ operation.get("produces_value") } producesValue={ operation.get("produces_value") }
pathMethod={ [path, method] } path={ path }
method={ method }
displayRequestDuration={ displayRequestDuration } displayRequestDuration={ displayRequestDuration }
fn={fn} /> fn={fn} />
} }

View File

@@ -1,8 +1,6 @@
import React from "react" import React from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import { helpers } from "swagger-client"
import { createDeepLinkPath, sanitizeUrl } from "core/utils" import { createDeepLinkPath, sanitizeUrl } from "core/utils"
const { opId } = helpers
export default class Operations extends React.Component { export default class Operations extends React.Component {
@@ -21,28 +19,20 @@ export default class Operations extends React.Component {
render() { render() {
let { let {
specSelectors, specSelectors,
specActions,
oas3Actions,
getComponent, getComponent,
layoutSelectors, layoutSelectors,
layoutActions, layoutActions,
authActions, getConfigs
authSelectors,
getConfigs,
fn
} = this.props } = this.props
let taggedOps = specSelectors.taggedOperations() let taggedOps = specSelectors.taggedOperations()
const Operation = getComponent("operation") const OperationContainer = getComponent("OperationContainer", true)
const Collapse = getComponent("Collapse") const Collapse = getComponent("Collapse")
const Markdown = getComponent("Markdown") const Markdown = getComponent("Markdown")
let showSummary = layoutSelectors.showSummary()
let { let {
docExpansion, docExpansion,
displayOperationId,
displayRequestDuration,
maxDisplayedTags, maxDisplayedTags,
deepLinking deepLinking
} = getConfigs() } = getConfigs()
@@ -120,49 +110,15 @@ export default class Operations extends React.Component {
<Collapse isOpened={showTag}> <Collapse isOpened={showTag}>
{ {
operations.map( op => { operations.map( op => {
const path = op.get("path")
const method = op.get("method")
const path = op.get("path", "") return <OperationContainer
const method = op.get("method", "") key={`${path}-${method}`}
const jumpToKey = `paths.${path}.${method}` op={op}
path={path}
const operationId = method={method}
op.getIn(["operation", "operationId"]) || op.getIn(["operation", "__originalOperationId"]) || opId(op.get("operation"), path, method) || op.get("id") tag={tag}
const tagKey = createDeepLinkPath(tag)
const operationKey = 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()}
tagKey={tagKey}
operationKey={operationKey}
isShown={layoutSelectors.isShown(["operations", tagKey, operationKey], docExpansion === "full")}
jumpToKey={jumpToKey}
showSummary={showSummary}
key={tagKey + operationKey}
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 }
/> />
}).toArray() }).toArray()
} }

View File

@@ -1,7 +1,7 @@
import React from "react" import React from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import cx from "classnames" import cx from "classnames"
import { fromJS, Seq } from "immutable" import { fromJS, Seq, Iterable } from "immutable"
import { getSampleSchema, fromJSOrdered } from "core/utils" import { getSampleSchema, fromJSOrdered } from "core/utils"
const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => { const getExampleComponent = ( sampleResponse, examples, HighlightCode ) => {
@@ -42,7 +42,7 @@ export default class Response extends React.Component {
static propTypes = { static propTypes = {
code: PropTypes.string.isRequired, code: PropTypes.string.isRequired,
response: PropTypes.object, response: PropTypes.instanceOf(Iterable),
className: PropTypes.string, className: PropTypes.string,
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired, getConfigs: PropTypes.func.isRequired,

View File

@@ -1,41 +1,52 @@
import React from "react" import React from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import { fromJS } from "immutable" import { fromJS, Iterable } from "immutable"
import { defaultStatusCode, getAcceptControllingResponse } from "core/utils" import { defaultStatusCode, getAcceptControllingResponse } from "core/utils"
export default class Responses extends React.Component { export default class Responses extends React.Component {
static propTypes = { static propTypes = {
request: PropTypes.object, tryItOutResponse: PropTypes.instanceOf(Iterable),
tryItOutResponse: PropTypes.object, responses: PropTypes.instanceOf(Iterable).isRequired,
responses: PropTypes.object.isRequired, produces: PropTypes.instanceOf(Iterable),
produces: PropTypes.object,
producesValue: PropTypes.any, producesValue: PropTypes.any,
displayRequestDuration: PropTypes.bool.isRequired,
path: PropTypes.string.isRequired,
method: PropTypes.string.isRequired,
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
getConfigs: PropTypes.func.isRequired, getConfigs: PropTypes.func.isRequired,
specSelectors: PropTypes.object.isRequired, specSelectors: PropTypes.object.isRequired,
specActions: PropTypes.object.isRequired, specActions: PropTypes.object.isRequired,
oas3Actions: PropTypes.object.isRequired, oas3Actions: PropTypes.object.isRequired,
pathMethod: PropTypes.array.isRequired,
displayRequestDuration: PropTypes.bool.isRequired,
fn: PropTypes.object.isRequired fn: PropTypes.object.isRequired
} }
static defaultProps = { static defaultProps = {
request: null,
tryItOutResponse: null, tryItOutResponse: null,
produces: fromJS(["application/json"]), produces: fromJS(["application/json"]),
displayRequestDuration: false 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 }) => { onResponseContentTypeChange = ({ controlsAcceptHeader, value }) => {
const { oas3Actions, pathMethod } = this.props const { oas3Actions, path, method } = this.props
if(controlsAcceptHeader) { if(controlsAcceptHeader) {
oas3Actions.setResponseContentType({ oas3Actions.setResponseContentType({
value, value,
pathMethod path,
method
}) })
} }
} }
@@ -43,7 +54,6 @@ export default class Responses extends React.Component {
render() { render() {
let { let {
responses, responses,
request,
tryItOutResponse, tryItOutResponse,
getComponent, getComponent,
getConfigs, getConfigs,
@@ -81,12 +91,12 @@ export default class Responses extends React.Component {
{ {
!tryItOutResponse ? null !tryItOutResponse ? null
: <div> : <div>
<LiveResponse request={ request } <LiveResponse response={ tryItOutResponse }
response={ tryItOutResponse }
getComponent={ getComponent } getComponent={ getComponent }
getConfigs={ getConfigs } getConfigs={ getConfigs }
specSelectors={ specSelectors } specSelectors={ specSelectors }
pathMethod={ this.props.pathMethod } path={ this.props.path }
method={ this.props.method }
displayRequestDuration={ displayRequestDuration } /> displayRequestDuration={ displayRequestDuration } />
<h4>Responses</h4> <h4>Responses</h4>
</div> </div>

View File

@@ -0,0 +1,194 @@
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,
displayRequestDuration: PropTypes.bool,
response: PropTypes.instanceOf(Iterable),
request: PropTypes.instanceOf(Iterable),
isDeepLinkingEnabled: PropTypes.bool.isRequired,
getComponent: PropTypes.func.isRequired,
authActions: PropTypes.object,
oas3Actions: 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
return {
operationId,
isDeepLinkingEnabled,
showSummary,
displayOperationId,
displayRequestDuration,
allowTryItOut,
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,
operationId,
showSummary,
isShown,
jumpToKey,
allowTryItOut,
response,
request,
displayOperationId,
displayRequestDuration,
isDeepLinkingEnabled,
specSelectors,
specActions,
getComponent,
getConfigs,
layoutSelectors,
layoutActions,
authActions,
authSelectors,
oas3Actions,
fn
} = this.props
const Operation = getComponent( "operation" )
const operationProps = fromJS({
op,
tag,
path,
method,
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}
layoutActions={ layoutActions }
layoutSelectors={ layoutSelectors }
authActions={ authActions }
authSelectors={ authSelectors }
getComponent={ getComponent }
getConfigs={ getConfigs }
fn={fn}
/>
)
}
}

View File

@@ -7,8 +7,7 @@ import * as AllPlugins from "core/plugins/all"
import { parseSearch } from "core/utils" import { parseSearch } from "core/utils"
if (process.env.NODE_ENV !== "production") { if (process.env.NODE_ENV !== "production") {
const Perf = require("react-addons-perf") window.Perf = require("react-addons-perf")
window.Perf = Perf
} }
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef

View File

@@ -28,10 +28,10 @@ export function setRequestContentType ({ value, pathMethod }) {
} }
} }
export function setResponseContentType ({ value, pathMethod }) { export function setResponseContentType ({ value, path, method }) {
return { return {
type: UPDATE_RESPONSE_CONTENT_TYPE, type: UPDATE_RESPONSE_CONTENT_TYPE,
payload: { value, pathMethod } payload: { value, path, method }
} }
} }

View File

@@ -1,10 +1,12 @@
import React from "react" import React from "react"
import PropTypes from "prop-types" import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import { fromJS } from "immutable"
const Callbacks = (props) => { const Callbacks = (props) => {
let { callbacks, getComponent } = props let { callbacks, getComponent } = props
// const Markdown = getComponent("Markdown") // const Markdown = getComponent("Markdown")
const Operation = getComponent("operation", true) const OperationContainer = getComponent("OperationContainer", true)
if(!callbacks) { if(!callbacks) {
return <span>No callbacks</span> return <span>No callbacks</span>
@@ -16,24 +18,22 @@ const Callbacks = (props) => {
{ callback.map((pathItem, pathItemName) => { { callback.map((pathItem, pathItemName) => {
return <div key={pathItemName}> return <div key={pathItemName}>
{ pathItem.map((operation, method) => { { pathItem.map((operation, method) => {
return <Operation let op = fromJS({
operation={operation} operation
})
return <OperationContainer
{...props}
op={op}
key={method} key={method}
tag={""}
method={method} method={method}
isShownKey={["callbacks", operation.get("id"), callbackName]}
path={pathItemName} path={pathItemName}
allowTryItOut={false} allowTryItOut={false}
{...props}></Operation> />
// return <pre>{JSON.stringify(operation)}</pre>
}) } }) }
</div> </div>
}) } }) }
</div> </div>
// return <div>
// <h2>{name}</h2>
// {callback.description && <Markdown source={callback.description}/>}
// <pre>{JSON.stringify(callback)}</pre>
// </div>
}) })
return <div> return <div>
{callbackElements} {callbackElements}
@@ -42,7 +42,7 @@ const Callbacks = (props) => {
Callbacks.propTypes = { Callbacks.propTypes = {
getComponent: PropTypes.func.isRequired, getComponent: PropTypes.func.isRequired,
callbacks: PropTypes.array.isRequired callbacks: ImPropTypes.iterable.isRequired
} }

View File

@@ -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) => { setValueToSample = (explicitMediaType) => {
this.onChange(this.sample(explicitMediaType)) this.onChange(this.sample(explicitMediaType))
} }

View File

@@ -22,6 +22,10 @@ const RequestBody = ({
const mediaTypeValue = requestBodyContent.get(contentType) const mediaTypeValue = requestBodyContent.get(contentType)
if(!mediaTypeValue) {
return null
}
return <div> return <div>
{ requestBodyDescription && { requestBodyDescription &&
<Markdown source={requestBodyDescription} /> <Markdown source={requestBodyDescription} />

View File

@@ -18,8 +18,7 @@ export default {
let [path, method] = pathMethod let [path, method] = pathMethod
return state.setIn( [ "requestData", path, method, "requestContentType" ], value) return state.setIn( [ "requestData", path, method, "requestContentType" ], value)
}, },
[UPDATE_RESPONSE_CONTENT_TYPE]: (state, { payload: { value, pathMethod } } ) =>{ [UPDATE_RESPONSE_CONTENT_TYPE]: (state, { payload: { value, path, method } } ) =>{
let [path, method] = pathMethod
return state.setIn( [ "requestData", path, method, "responseContentType" ], value) return state.setIn( [ "requestData", path, method, "responseContentType" ], value)
}, },
[UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, key, val } } ) =>{ [UPDATE_SERVER_VARIABLE_VALUE]: (state, { payload: { server, key, val } } ) =>{

View File

@@ -20,8 +20,14 @@ const RootWrapper = (reduxStore, ComponentToWrap) => class extends Component {
} }
const makeContainer = (getSystem, component, reduxStore) => { 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 wrappedWithSystem = SystemWrapper(getSystem, component, reduxStore)
let connected = connect(state => ({state}))(wrappedWithSystem) let connected = connect( mapStateToProps )(wrappedWithSystem)
if(reduxStore) if(reduxStore)
return RootWrapper(reduxStore, connected) return RootWrapper(reduxStore, connected)
return connected return connected
@@ -114,5 +120,5 @@ export const getComponent = (getSystem, getStore, getComponents, componentName,
return makeContainer(getSystem, component, getStore()) return makeContainer(getSystem, component, getStore())
// container == truthy // container == truthy
return makeContainer(getSystem, component) return makeContainer(getSystem, wrapRender(component))
} }

View File

@@ -13,6 +13,8 @@ import downloadUrlPlugin from "core/plugins/download-url"
import configsPlugin from "plugins/configs" import configsPlugin from "plugins/configs"
import deepLinkingPlugin from "core/plugins/deep-linking" import deepLinkingPlugin from "core/plugins/deep-linking"
import OperationContainer from "core/containers/OperationContainer"
import App from "core/components/app" import App from "core/components/app"
import AuthorizationPopup from "core/components/auth/authorization-popup" import AuthorizationPopup from "core/components/auth/authorization-popup"
import AuthorizeBtn from "core/components/auth/authorize-btn" import AuthorizeBtn from "core/components/auth/authorize-btn"
@@ -112,7 +114,8 @@ export default function() {
TryItOutButton, TryItOutButton,
Markdown, Markdown,
BaseLayout, BaseLayout,
VersionStamp VersionStamp,
OperationContainer
} }
} }

View File

@@ -289,8 +289,7 @@ export default class Store {
getMapStateToProps() { getMapStateToProps() {
return () => { return () => {
let obj = Object.assign({}, this.getSystem()) return Object.assign({}, this.getSystem())
return obj
} }
} }

View File

@@ -12,6 +12,10 @@
{ {
color: $model-deprecated-font-color !important; color: $model-deprecated-font-color !important;
} }
> td:first-of-type {
text-decoration: line-through;
}
} }
&-toggle &-toggle
{ {

View File

@@ -1,7 +1,11 @@
/* eslint-env mocha */ /* eslint-env mocha */
import React, { PureComponent } from "react"
import expect from "expect" import expect from "expect"
import System from "core/system" import System from "core/system"
import { fromJS } from "immutable" import { fromJS } from "immutable"
import { render } from "enzyme"
import ViewPlugin from "core/plugins/view/index.js"
import { connect, Provider } from "react-redux"
describe("bound system", function(){ describe("bound system", function(){
@@ -444,4 +448,239 @@ describe("bound system", function(){
}) })
describe("getComponent", function() {
it("returns a component from the system", function() {
const system = new System({
plugins: [
ViewPlugin,
{
components: {
test: ({ name }) => <div>{name} component</div>
}
}
]
})
// When
var Component = system.getSystem().getComponent("test")
const renderedComponent = render(<Component name="Test" />)
expect(renderedComponent.text()).toEqual("Test component")
})
it("allows container components to provide their own `mapStateToProps` function", function() {
// Given
class ContainerComponent extends PureComponent {
mapStateToProps(nextState, props) {
return {
"fromMapState": "This came from mapStateToProps"
}
}
static defaultProps = {
"fromMapState" : ""
}
render() {
const { exampleSelectors, fromMapState, fromOwnProps } = this.props
return (
<div>{ fromMapState } {exampleSelectors.foo()} {fromOwnProps}</div>
)
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
ContainerComponent
}
},
{
statePlugins: {
example: {
selectors: {
foo() { return "and this came from the system" }
}
}
}
}
]
})
// When
var Component = system.getSystem().getComponent("ContainerComponent", true)
const renderedComponent = render(
<Provider store={system.getStore()}>
<Component fromOwnProps="and this came from my own props" />
</Provider>
)
// Then
expect(renderedComponent.text()).toEqual("This came from mapStateToProps and this came from the system and this came from my own props")
})
it("gives the system and own props as props to a container's `mapStateToProps` function", function() {
// Given
class ContainerComponent extends PureComponent {
mapStateToProps(nextState, props) {
const { exampleSelectors, fromMapState, fromOwnProps } = props
return {
"fromMapState": `This came from mapStateToProps ${exampleSelectors.foo()} ${fromOwnProps}`
}
}
static defaultProps = {
"fromMapState" : ""
}
render() {
const { fromMapState } = this.props
return (
<div>{ fromMapState }</div>
)
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
ContainerComponent
}
},
{
statePlugins: {
example: {
selectors: {
foo() { return "and this came from the system" }
}
}
}
}
]
})
// When
var Component = system.getSystem().getComponent("ContainerComponent", true)
const renderedComponent = render(
<Provider store={system.getStore()}>
<Component fromOwnProps="and this came from my own props" />
</Provider>
)
// Then
expect(renderedComponent.text()).toEqual("This came from mapStateToProps and this came from the system and this came from my own props")
})
it("should catch errors thrown inside of React Component Class render methods", function() {
// Given
// eslint-disable-next-line react/require-render-return
class BrokenComponent extends React.Component {
render() {
throw new Error("This component is broken")
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
}
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent")
const renderedComponent = render(<Component />)
// Then
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
it("should catch errors thrown inside of pure component render methods", function() {
// Given
// eslint-disable-next-line react/require-render-return
class BrokenComponent extends PureComponent {
render() {
throw new Error("This component is broken")
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
}
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent")
const renderedComponent = render(<Component />)
// Then
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
it("should catch errors thrown inside of stateless component functions", function() {
// Given
// eslint-disable-next-line react/require-render-return
let BrokenComponent = function BrokenComponent() { throw new Error("This component is broken") }
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
}
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent")
const renderedComponent = render(<Component />)
// Then
expect(renderedComponent.text().startsWith("😱 Could not render")).toEqual(true)
})
it("should catch errors thrown inside of container components", function() {
// Given
// eslint-disable-next-line react/require-render-return
class BrokenComponent extends React.Component {
render() {
throw new Error("This component is broken")
}
}
const system = new System({
plugins: [
ViewPlugin,
{
components: {
BrokenComponent
}
}
]
})
// When
var Component = system.getSystem().getComponent("BrokenComponent", true)
const renderedComponent = render(
<Provider store={system.getStore()}>
<Component />
</Provider>
)
// Then
expect(renderedComponent.text()).toEqual("😱 Could not render BrokenComponent, see the console.")
})
})
}) })