feat: lazy resolver (#4249)
* default to empty `ImmutableMap` when grabbing op metadata
* pass `errors` into JsonSchema components
* Account for Immutable data structure in JavaScriptonSchema...
...and create empty Lists instead of Maps by default.
* Pass ImmutableList through to JsonSchema child components
* Add lazy resolving spec state extensions
* TEMPORARY: disable conventional resolved spec
* WIP
* Use resolveSubtree in Operation display
* Freebie: short-circuit Markdown component if it is given plaintext
* NEW DEFAULT BEHAVIOR: `defaultModelsExpandDepth: 1` does not expand individual models
* Render faked Model expander to trigger resolution
* Baseline support for Editor lifecycles
* Display operation summaries before the operation is resolved
* Test migrations
* WIP
* Swagger2 TIO Body params
* a bit of cleanup
* Debounce string param inputs
* Reach into unresolved operation for deprecated flag, if available
* Fire subtree request outside of render
* Remove debugging flags
* Fix logical errors in spec statePlugins
* TODOs become TODONEs!
* Migrate deeplinking feature to non-resolved spec action
* ESLint fixes
This commit is contained in:
@@ -63,6 +63,7 @@
|
|||||||
"react-addons-shallow-compare": "0.14.8",
|
"react-addons-shallow-compare": "0.14.8",
|
||||||
"react-addons-test-utils": "^15.6.2",
|
"react-addons-test-utils": "^15.6.2",
|
||||||
"react-collapse": "^4.0.3",
|
"react-collapse": "^4.0.3",
|
||||||
|
"react-debounce-input": "^3.2.0",
|
||||||
"react-dom": "^15.6.2",
|
"react-dom": "^15.6.2",
|
||||||
"react-height": "^2.0.0",
|
"react-height": "^2.0.0",
|
||||||
"react-hot-loader": "1.3.1",
|
"react-hot-loader": "1.3.1",
|
||||||
|
|||||||
@@ -8,14 +8,17 @@ export default class ModelCollapse extends Component {
|
|||||||
children: PropTypes.any,
|
children: PropTypes.any,
|
||||||
title: PropTypes.element,
|
title: PropTypes.element,
|
||||||
modelName: PropTypes.string,
|
modelName: PropTypes.string,
|
||||||
onToggle: PropTypes.func
|
classes: PropTypes.string,
|
||||||
|
onToggle: PropTypes.func,
|
||||||
|
hideSelfOnExpand: PropTypes.bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
collapsedContent: "{...}",
|
collapsedContent: "{...}",
|
||||||
expanded: false,
|
expanded: false,
|
||||||
title: null,
|
title: null,
|
||||||
onToggle: () => {}
|
onToggle: () => {},
|
||||||
|
hideSelfOnExpand: false
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
@@ -29,17 +32,23 @@ export default class ModelCollapse extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps){
|
componentDidMount() {
|
||||||
|
const { hideSelfOnExpand, expanded, modelName } = this.props
|
||||||
if(this.props.expanded!= nextProps.expanded){
|
if(hideSelfOnExpand && expanded) {
|
||||||
this.setState({expanded: nextProps.expanded})
|
// We just mounted pre-expanded, and we won't be going back..
|
||||||
|
// So let's give our parent an `onToggle` call..
|
||||||
|
// Since otherwise it will never be called.
|
||||||
|
this.props.onToggle(modelName, expanded)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps){
|
||||||
|
if(this.props.expanded !== nextProps.expanded){
|
||||||
|
this.setState({expanded: nextProps.expanded})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCollapsed=()=>{
|
toggleCollapsed=()=>{
|
||||||
|
|
||||||
|
|
||||||
if(this.props.onToggle){
|
if(this.props.onToggle){
|
||||||
this.props.onToggle(this.props.modelName,!this.state.expanded)
|
this.props.onToggle(this.props.modelName,!this.state.expanded)
|
||||||
}
|
}
|
||||||
@@ -50,9 +59,18 @@ export default class ModelCollapse extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const {title} = this.props
|
const { title, classes } = this.props
|
||||||
|
|
||||||
|
if(this.state.expanded ) {
|
||||||
|
if(this.props.hideSelfOnExpand) {
|
||||||
|
return <span className={classes || ""}>
|
||||||
|
{this.props.children}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<span className={classes || ""}>
|
||||||
{ title && <span onClick={this.toggleCollapsed} style={{ "cursor": "pointer" }}>{title}</span> }
|
{ title && <span onClick={this.toggleCollapsed} style={{ "cursor": "pointer" }}>{title}</span> }
|
||||||
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
|
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
|
||||||
<span className={ "model-toggle" + ( this.state.expanded ? "" : " collapsed" ) }></span>
|
<span className={ "model-toggle" + ( this.state.expanded ? "" : " collapsed" ) }></span>
|
||||||
|
|||||||
@@ -6,11 +6,29 @@ export default class Models extends Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
getComponent: PropTypes.func,
|
getComponent: PropTypes.func,
|
||||||
specSelectors: PropTypes.object,
|
specSelectors: PropTypes.object,
|
||||||
|
specActions: PropTypes.object.isRequired,
|
||||||
layoutSelectors: PropTypes.object,
|
layoutSelectors: PropTypes.object,
|
||||||
layoutActions: PropTypes.object,
|
layoutActions: PropTypes.object,
|
||||||
getConfigs: PropTypes.func.isRequired
|
getConfigs: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSchemaBasePath = () => {
|
||||||
|
const isOAS3 = this.props.specSelectors.isOAS3()
|
||||||
|
return isOAS3 ? ["components", "schemas"] : ["definitions"]
|
||||||
|
}
|
||||||
|
|
||||||
|
getCollapsedContent = () => {
|
||||||
|
return " "
|
||||||
|
}
|
||||||
|
|
||||||
|
handleToggle = (name, isExpanded) => {
|
||||||
|
const { layoutActions } = this.props
|
||||||
|
layoutActions.show(["models", name], isExpanded)
|
||||||
|
if(isExpanded) {
|
||||||
|
this.props.specActions.requestResolvedSubtree([...this.getSchemaBasePath(), name])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props
|
let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props
|
||||||
let definitions = specSelectors.definitions()
|
let definitions = specSelectors.definitions()
|
||||||
@@ -18,10 +36,11 @@ export default class Models extends Component {
|
|||||||
if (!definitions.size || defaultModelsExpandDepth < 0) return null
|
if (!definitions.size || defaultModelsExpandDepth < 0) return null
|
||||||
|
|
||||||
let showModels = layoutSelectors.isShown("models", defaultModelsExpandDepth > 0 && docExpansion !== "none")
|
let showModels = layoutSelectors.isShown("models", defaultModelsExpandDepth > 0 && docExpansion !== "none")
|
||||||
const specPathBase = specSelectors.isOAS3() ? ["components", "schemas"] : ["definitions"]
|
const specPathBase = this.getSchemaBasePath()
|
||||||
|
|
||||||
const ModelWrapper = getComponent("ModelWrapper")
|
const ModelWrapper = getComponent("ModelWrapper")
|
||||||
const Collapse = getComponent("Collapse")
|
const Collapse = getComponent("Collapse")
|
||||||
|
const ModelCollapse = getComponent("ModelCollapse")
|
||||||
|
|
||||||
return <section className={ showModels ? "models is-open" : "models"}>
|
return <section className={ showModels ? "models is-open" : "models"}>
|
||||||
<h4 onClick={() => layoutActions.show("models", !showModels)}>
|
<h4 onClick={() => layoutActions.show("models", !showModels)}>
|
||||||
@@ -32,18 +51,40 @@ export default class Models extends Component {
|
|||||||
</h4>
|
</h4>
|
||||||
<Collapse isOpened={showModels}>
|
<Collapse isOpened={showModels}>
|
||||||
{
|
{
|
||||||
definitions.entrySeq().map( ( [ name, model ])=>{
|
definitions.entrySeq().map( ( [ name ])=>{
|
||||||
|
|
||||||
return <div id={ `model-${name}` } className="model-container" key={ `models-section-${name}` }>
|
const schema = specSelectors.specResolvedSubtree([...specPathBase, name])
|
||||||
<ModelWrapper name={ name }
|
|
||||||
|
if(layoutSelectors.isShown(["models", name], false) && schema === undefined) {
|
||||||
|
// Firing an action in a container render is not great,
|
||||||
|
// but it works for now.
|
||||||
|
this.props.specActions.requestResolvedSubtree([...this.getSchemaBasePath(), name])
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = <ModelWrapper name={ name }
|
||||||
expandDepth={ defaultModelsExpandDepth }
|
expandDepth={ defaultModelsExpandDepth }
|
||||||
schema={ model }
|
schema={ schema }
|
||||||
specPath={Im.List([...specPathBase, name])}
|
specPath={Im.List([...specPathBase, name])}
|
||||||
getComponent={ getComponent }
|
getComponent={ getComponent }
|
||||||
specSelectors={ specSelectors }
|
specSelectors={ specSelectors }
|
||||||
getConfigs = {getConfigs}
|
getConfigs = {getConfigs}
|
||||||
layoutSelectors = {layoutSelectors}
|
layoutSelectors = {layoutSelectors}
|
||||||
layoutActions = {layoutActions}/>
|
layoutActions = {layoutActions}/>
|
||||||
|
|
||||||
|
const title = <span className="model-box">
|
||||||
|
<span className="model model-title">{name}</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
return <div id={ `model-${name}` } className="model-container" key={ `models-section-${name}` }>
|
||||||
|
<ModelCollapse
|
||||||
|
classes="model-box"
|
||||||
|
collapsedContent={this.getCollapsedContent(name)}
|
||||||
|
onToggle={this.handleToggle}
|
||||||
|
title={title}
|
||||||
|
modelName={name}
|
||||||
|
hideSelfOnExpand={true}
|
||||||
|
expanded={defaultModelsExpandDepth > 1}
|
||||||
|
>{content}</ModelCollapse>
|
||||||
</div>
|
</div>
|
||||||
}).toArray()
|
}).toArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export default class Operation extends PureComponent {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
specPath: ImPropTypes.list.isRequired,
|
specPath: ImPropTypes.list.isRequired,
|
||||||
operation: PropTypes.instanceOf(Iterable).isRequired,
|
operation: PropTypes.instanceOf(Iterable).isRequired,
|
||||||
|
summary: PropTypes.string,
|
||||||
response: PropTypes.instanceOf(Iterable),
|
response: PropTypes.instanceOf(Iterable),
|
||||||
request: PropTypes.instanceOf(Iterable),
|
request: PropTypes.instanceOf(Iterable),
|
||||||
|
|
||||||
@@ -34,7 +35,8 @@ export default class Operation extends PureComponent {
|
|||||||
operation: null,
|
operation: null,
|
||||||
response: null,
|
response: null,
|
||||||
request: null,
|
request: null,
|
||||||
specPath: List()
|
specPath: List(),
|
||||||
|
summary: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -59,6 +61,8 @@ export default class Operation extends PureComponent {
|
|||||||
let operationProps = this.props.operation
|
let operationProps = this.props.operation
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
summary,
|
||||||
|
deprecated,
|
||||||
isShown,
|
isShown,
|
||||||
isAuthorized,
|
isAuthorized,
|
||||||
path,
|
path,
|
||||||
@@ -76,14 +80,13 @@ export default class Operation extends PureComponent {
|
|||||||
} = operationProps.toJS()
|
} = operationProps.toJS()
|
||||||
|
|
||||||
let {
|
let {
|
||||||
summary,
|
summary: resolvedSummary,
|
||||||
description,
|
description,
|
||||||
deprecated,
|
|
||||||
externalDocs,
|
externalDocs,
|
||||||
schemes
|
schemes
|
||||||
} = op.operation
|
} = op
|
||||||
|
|
||||||
let operation = operationProps.getIn(["op", "operation"])
|
let operation = operationProps.getIn(["op"])
|
||||||
let security = operationProps.get("security")
|
let security = operationProps.get("security")
|
||||||
let responses = operation.get("responses")
|
let responses = operation.get("responses")
|
||||||
let produces = operation.get("produces")
|
let produces = operation.get("produces")
|
||||||
@@ -132,7 +135,7 @@ export default class Operation extends PureComponent {
|
|||||||
|
|
||||||
{ !showSummary ? null :
|
{ !showSummary ? null :
|
||||||
<div className="opblock-summary-description">
|
<div className="opblock-summary-description">
|
||||||
{ summary }
|
{ resolvedSummary || summary }
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default class ParamBody extends PureComponent {
|
|||||||
|
|
||||||
updateValues = (props) => {
|
updateValues = (props) => {
|
||||||
let { specSelectors, pathMethod, param, isExecute, consumesValue="" } = props
|
let { specSelectors, pathMethod, param, isExecute, consumesValue="" } = props
|
||||||
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name"), param.get("in")) : fromJS({})
|
let parameter = specSelectors ? specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) : fromJS({})
|
||||||
let isXml = /xml/i.test(consumesValue)
|
let isXml = /xml/i.test(consumesValue)
|
||||||
let isJson = /json/i.test(consumesValue)
|
let isJson = /json/i.test(consumesValue)
|
||||||
let paramValue = isXml ? parameter.get("value_xml") : parameter.get("value")
|
let paramValue = isXml ? parameter.get("value_xml") : parameter.get("value")
|
||||||
@@ -107,7 +107,7 @@ export default class ParamBody extends PureComponent {
|
|||||||
const HighlightCode = getComponent("highlightCode")
|
const HighlightCode = getComponent("highlightCode")
|
||||||
const ContentType = getComponent("contentType")
|
const ContentType = getComponent("contentType")
|
||||||
// for domains where specSelectors not passed
|
// for domains where specSelectors not passed
|
||||||
let parameter = specSelectors ? specSelectors.getParameter(pathMethod, param.get("name"), param.get("in")) : param
|
let parameter = specSelectors ? specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) : param
|
||||||
let errors = parameter.get("errors", List())
|
let errors = parameter.get("errors", List())
|
||||||
let consumesValue = specSelectors.contentTypeValues(pathMethod).get("requestContentType")
|
let consumesValue = specSelectors.contentTypeValues(pathMethod).get("requestContentType")
|
||||||
let consumes = this.props.consumes && this.props.consumes.size ? this.props.consumes : ParamBody.defaultProp.consumes
|
let consumes = this.props.consumes && this.props.consumes.size ? this.props.consumes : ParamBody.defaultProp.consumes
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export default class ParameterRow extends Component {
|
|||||||
|
|
||||||
let { specSelectors, pathMethod, param } = props
|
let { specSelectors, pathMethod, param } = props
|
||||||
let defaultValue = param.get("default")
|
let defaultValue = param.get("default")
|
||||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
|
||||||
let value = parameter ? parameter.get("value") : ""
|
let value = parameter ? parameter.get("value") : ""
|
||||||
if ( defaultValue !== undefined && value === undefined ) {
|
if ( defaultValue !== undefined && value === undefined ) {
|
||||||
this.onChangeWrapper(defaultValue)
|
this.onChangeWrapper(defaultValue)
|
||||||
@@ -37,7 +37,7 @@ export default class ParameterRow extends Component {
|
|||||||
|
|
||||||
let example = param.get("example")
|
let example = param.get("example")
|
||||||
let defaultValue = param.get("default")
|
let defaultValue = param.get("default")
|
||||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
|
||||||
let enumValue
|
let enumValue
|
||||||
|
|
||||||
if(isOAS3()) {
|
if(isOAS3()) {
|
||||||
@@ -104,8 +104,7 @@ export default class ParameterRow extends Component {
|
|||||||
let isFormDataSupported = "FormData" in win
|
let isFormDataSupported = "FormData" in win
|
||||||
let required = param.get("required")
|
let required = param.get("required")
|
||||||
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
|
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
|
||||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
let value = param ? param.get("value") : ""
|
||||||
let value = parameter ? parameter.get("value") : ""
|
|
||||||
let extensions = getExtensions(param)
|
let extensions = getExtensions(param)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export default class Parameters extends Component {
|
|||||||
specPath={specPath.push(i.toString())}
|
specPath={specPath.push(i.toString())}
|
||||||
getComponent={ getComponent }
|
getComponent={ getComponent }
|
||||||
getConfigs={ getConfigs }
|
getConfigs={ getConfigs }
|
||||||
param={ parameter }
|
param={ specSelectors.parameterWithMeta(pathMethod, parameter.get("name"), parameter.get("in")) }
|
||||||
key={ `${parameter.get( "in" )}.${parameter.get("name")}` }
|
key={ `${parameter.get( "in" )}.${parameter.get("name")}` }
|
||||||
onChange={ this.onChange }
|
onChange={ this.onChange }
|
||||||
onChangeConsumes={this.onChangeConsumesWrapper}
|
onChangeConsumes={this.onChangeConsumesWrapper}
|
||||||
|
|||||||
@@ -3,7 +3,17 @@ import PropTypes from "prop-types"
|
|||||||
import Remarkable from "remarkable"
|
import Remarkable from "remarkable"
|
||||||
import sanitize from "sanitize-html"
|
import sanitize from "sanitize-html"
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-useless-escape
|
||||||
|
const isPlainText = (str) => /^[A-Z\s0-9!?\.]+$/gi.test(str)
|
||||||
|
|
||||||
function Markdown({ source }) {
|
function Markdown({ source }) {
|
||||||
|
if(isPlainText(source)) {
|
||||||
|
// If the source text is not Markdown,
|
||||||
|
// let's save some time and just render it.
|
||||||
|
return <div className="markdown">
|
||||||
|
{source}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
const html = new Remarkable({
|
const html = new Remarkable({
|
||||||
html: true,
|
html: true,
|
||||||
typographer: true,
|
typographer: true,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { PureComponent } 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 { helpers } from "swagger-client"
|
import { helpers } from "swagger-client"
|
||||||
import { Iterable, fromJS } from "immutable"
|
import { Iterable, fromJS, Map } from "immutable"
|
||||||
|
|
||||||
const { opId } = helpers
|
const { opId } = helpers
|
||||||
|
|
||||||
@@ -82,13 +82,24 @@ export default class OperationContainer extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
if(nextProps.response !== this.props.response) {
|
const { path, method, specActions, specSelectors, response, isShown } = nextProps
|
||||||
|
const resolvedSubtree = specSelectors.specResolvedSubtree(["paths", path, method])
|
||||||
|
|
||||||
|
if(response !== this.props.response) {
|
||||||
this.setState({ executeInProgress: false })
|
this.setState({ executeInProgress: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isShown && resolvedSubtree === undefined) {
|
||||||
|
specActions.requestResolvedSubtree(["paths", path, method])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleShown =() => {
|
toggleShown =() => {
|
||||||
let { layoutActions, tag, operationId, isShown } = this.props
|
let { layoutActions, specActions, tag, operationId, path, method, isShown } = this.props
|
||||||
|
if(!isShown) {
|
||||||
|
// transitioning from collapsed to expanded
|
||||||
|
specActions.requestResolvedSubtree(["paths", path, method])
|
||||||
|
}
|
||||||
layoutActions.show(["operations", tag, operationId], !isShown)
|
layoutActions.show(["operations", tag, operationId], !isShown)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +119,7 @@ export default class OperationContainer extends PureComponent {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {
|
let {
|
||||||
op,
|
op: unresolvedOp,
|
||||||
tag,
|
tag,
|
||||||
path,
|
path,
|
||||||
method,
|
method,
|
||||||
@@ -140,10 +151,14 @@ export default class OperationContainer extends PureComponent {
|
|||||||
|
|
||||||
const Operation = getComponent( "operation" )
|
const Operation = getComponent( "operation" )
|
||||||
|
|
||||||
|
const resolvedSubtree = specSelectors.specResolvedSubtree(["paths", path, method]) || Map()
|
||||||
|
|
||||||
const operationProps = fromJS({
|
const operationProps = fromJS({
|
||||||
op,
|
op: resolvedSubtree || Map(),
|
||||||
tag,
|
tag,
|
||||||
path,
|
path,
|
||||||
|
summary: unresolvedOp.getIn(["operation", "summary"]) || "",
|
||||||
|
deprecated: resolvedSubtree.get("deprecated") || unresolvedOp.getIn(["operation", "deprecated"]) || false,
|
||||||
method,
|
method,
|
||||||
security,
|
security,
|
||||||
isAuthorized,
|
isAuthorized,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { PureComponent, Component } from "react"
|
|||||||
import PropTypes from "prop-types"
|
import PropTypes from "prop-types"
|
||||||
import { List, fromJS } from "immutable"
|
import { List, fromJS } from "immutable"
|
||||||
import ImPropTypes from "react-immutable-proptypes"
|
import ImPropTypes from "react-immutable-proptypes"
|
||||||
|
import DebounceInput from "react-debounce-input"
|
||||||
//import "less/json-schema-form"
|
//import "less/json-schema-form"
|
||||||
|
|
||||||
const noop = ()=> {}
|
const noop = ()=> {}
|
||||||
@@ -79,10 +80,13 @@ export class JsonSchema_string extends Component {
|
|||||||
disabled={isDisabled}/>)
|
disabled={isDisabled}/>)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return (<Input type={ schema.format === "password" ? "password" : "text" }
|
return (<DebounceInput
|
||||||
|
type={ schema.format === "password" ? "password" : "text" }
|
||||||
className={ errors.length ? "invalid" : ""}
|
className={ errors.length ? "invalid" : ""}
|
||||||
title={ errors.length ? errors : ""}
|
title={ errors.length ? errors : ""}
|
||||||
value={value}
|
value={value}
|
||||||
|
minLength={0}
|
||||||
|
debounceTimeout={350}
|
||||||
placeholder={description}
|
placeholder={description}
|
||||||
onChange={ this.onChange }
|
onChange={ this.onChange }
|
||||||
disabled={isDisabled}/>)
|
disabled={isDisabled}/>)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { escapeDeepLinkPath } from "core/utils"
|
|||||||
let hasHashBeenParsed = false //TODO this forces code to only run once which may prevent scrolling if page not refreshed
|
let hasHashBeenParsed = false //TODO this forces code to only run once which may prevent scrolling if page not refreshed
|
||||||
|
|
||||||
|
|
||||||
export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) => {
|
export const updateJsonSpec = (ori, { layoutActions, getConfigs }) => (...args) => {
|
||||||
ori(...args)
|
ori(...args)
|
||||||
|
|
||||||
const isDeepLinkingEnabled = getConfigs().deepLinking
|
const isDeepLinkingEnabled = getConfigs().deepLinking
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export const NEW_SPEC_ERR = "err_new_spec_err"
|
|||||||
export const NEW_SPEC_ERR_BATCH = "err_new_spec_err_batch"
|
export const NEW_SPEC_ERR_BATCH = "err_new_spec_err_batch"
|
||||||
export const NEW_AUTH_ERR = "err_new_auth_err"
|
export const NEW_AUTH_ERR = "err_new_auth_err"
|
||||||
export const CLEAR = "err_clear"
|
export const CLEAR = "err_clear"
|
||||||
|
export const CLEAR_BY = "err_clear_by"
|
||||||
|
|
||||||
export function newThrownErr(err) {
|
export function newThrownErr(err) {
|
||||||
return {
|
return {
|
||||||
@@ -49,3 +50,11 @@ export function clear(filter = {}) {
|
|||||||
payload: filter
|
payload: filter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function clearBy(filter = () => true) {
|
||||||
|
// filter is a function
|
||||||
|
return {
|
||||||
|
type: CLEAR_BY,
|
||||||
|
payload: filter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ import {
|
|||||||
NEW_SPEC_ERR,
|
NEW_SPEC_ERR,
|
||||||
NEW_SPEC_ERR_BATCH,
|
NEW_SPEC_ERR_BATCH,
|
||||||
NEW_AUTH_ERR,
|
NEW_AUTH_ERR,
|
||||||
CLEAR
|
CLEAR,
|
||||||
|
CLEAR_BY,
|
||||||
} from "./actions"
|
} from "./actions"
|
||||||
|
|
||||||
import reject from "lodash/reject"
|
import { fromJS, List } from "immutable"
|
||||||
|
|
||||||
import Im, { fromJS, List } from "immutable"
|
|
||||||
|
|
||||||
import transformErrors from "./error-transformers/hook"
|
import transformErrors from "./error-transformers/hook"
|
||||||
|
|
||||||
@@ -65,11 +64,34 @@ export default function(system) {
|
|||||||
},
|
},
|
||||||
|
|
||||||
[CLEAR]: (state, { payload }) => {
|
[CLEAR]: (state, { payload }) => {
|
||||||
if(!payload) {
|
if(!payload || !state.get("errors")) {
|
||||||
return
|
return state
|
||||||
}
|
}
|
||||||
// TODO: Rework, to use immutable only, no need for lodash
|
|
||||||
let newErrors = Im.fromJS(reject((state.get("errors") || List()).toJS(), payload))
|
let newErrors = state.get("errors")
|
||||||
|
.filter(err => {
|
||||||
|
return err.keySeq().every(k => {
|
||||||
|
const errValue = err.get(k)
|
||||||
|
const filterValue = payload[k]
|
||||||
|
|
||||||
|
if(!filterValue) return true
|
||||||
|
|
||||||
|
return errValue !== filterValue
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return state.merge({
|
||||||
|
errors: newErrors
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
[CLEAR_BY]: (state, { payload }) => {
|
||||||
|
if(!payload || typeof payload !== "function") {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
let newErrors = state.get("errors")
|
||||||
|
.filter(err => {
|
||||||
|
return payload(err)
|
||||||
|
})
|
||||||
return state.merge({
|
return state.merge({
|
||||||
errors: newErrors
|
errors: newErrors
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import YAML from "js-yaml"
|
import YAML from "js-yaml"
|
||||||
import parseUrl from "url-parse"
|
import parseUrl from "url-parse"
|
||||||
import serializeError from "serialize-error"
|
import serializeError from "serialize-error"
|
||||||
|
import { Map } from "immutable"
|
||||||
import isString from "lodash/isString"
|
import isString from "lodash/isString"
|
||||||
import { isJSONObject } from "core/utils"
|
import { isJSONObject } from "core/utils"
|
||||||
|
|
||||||
@@ -21,6 +22,7 @@ export const CLEAR_REQUEST = "spec_clear_request"
|
|||||||
export const CLEAR_VALIDATE_PARAMS = "spec_clear_validate_param"
|
export const CLEAR_VALIDATE_PARAMS = "spec_clear_validate_param"
|
||||||
export const UPDATE_OPERATION_META_VALUE = "spec_update_operation_meta_value"
|
export const UPDATE_OPERATION_META_VALUE = "spec_update_operation_meta_value"
|
||||||
export const UPDATE_RESOLVED = "spec_update_resolved"
|
export const UPDATE_RESOLVED = "spec_update_resolved"
|
||||||
|
export const UPDATE_RESOLVED_SUBTREE = "spec_update_resolved_subtree"
|
||||||
export const SET_SCHEME = "set_scheme"
|
export const SET_SCHEME = "set_scheme"
|
||||||
|
|
||||||
const toStr = (str) => isString(str) ? str : ""
|
const toStr = (str) => isString(str) ? str : ""
|
||||||
@@ -74,7 +76,14 @@ export const parseToJson = (str) => ({specActions, specSelectors, errActions}) =
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let hasWarnedAboutResolveSpecDeprecation = false
|
||||||
|
|
||||||
export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }, getConfigs}) => {
|
export const resolveSpec = (json, url) => ({specActions, specSelectors, errActions, fn: { fetch, resolve, AST }, getConfigs}) => {
|
||||||
|
if(!hasWarnedAboutResolveSpecDeprecation) {
|
||||||
|
console.warn(`specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use resolveIn instead!`)
|
||||||
|
hasWarnedAboutResolveSpecDeprecation = true
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
modelPropertyMacro,
|
modelPropertyMacro,
|
||||||
parameterMacro,
|
parameterMacro,
|
||||||
@@ -124,6 +133,55 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const requestResolvedSubtree = path => system => {
|
||||||
|
const {
|
||||||
|
errActions,
|
||||||
|
fn: {
|
||||||
|
resolveSubtree,
|
||||||
|
AST: { getLineNumberForPath }
|
||||||
|
},
|
||||||
|
specSelectors,
|
||||||
|
specActions,
|
||||||
|
} = system
|
||||||
|
|
||||||
|
const specStr = specSelectors.specStr()
|
||||||
|
|
||||||
|
if(!resolveSubtree) {
|
||||||
|
console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentValue = specSelectors.specResolvedSubtree(path)
|
||||||
|
|
||||||
|
if(currentValue) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolveSubtree(specSelectors.specJson().toJS(), path)
|
||||||
|
.then(({ spec, errors }) => {
|
||||||
|
errActions.clear({
|
||||||
|
type: "thrown"
|
||||||
|
})
|
||||||
|
if(Array.isArray(errors) && errors.length > 0) {
|
||||||
|
let preparedErrors = errors
|
||||||
|
.map(err => {
|
||||||
|
console.error(err)
|
||||||
|
err.line = err.fullPath ? getLineNumberForPath(specStr, err.fullPath) : null
|
||||||
|
err.path = err.fullPath ? err.fullPath.join(".") : null
|
||||||
|
err.level = "error"
|
||||||
|
err.type = "thrown"
|
||||||
|
err.source = "resolver"
|
||||||
|
Object.defineProperty(err, "message", { enumerable: true, value: err.message })
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
errActions.newThrownErrBatch(preparedErrors)
|
||||||
|
}
|
||||||
|
|
||||||
|
return specActions.updateResolvedSubtree(path, spec)
|
||||||
|
})
|
||||||
|
.catch(e => console.error(e))
|
||||||
|
}
|
||||||
|
|
||||||
export function changeParam( path, paramName, paramIn, value, isXml ){
|
export function changeParam( path, paramName, paramIn, value, isXml ){
|
||||||
return {
|
return {
|
||||||
type: UPDATE_PARAM,
|
type: UPDATE_PARAM,
|
||||||
@@ -131,6 +189,23 @@ export function changeParam( path, paramName, paramIn, value, isXml ){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateResolvedSubtree = (path, value) => {
|
||||||
|
return {
|
||||||
|
type: UPDATE_RESOLVED_SUBTREE,
|
||||||
|
payload: { path, value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const invalidateResolvedSubtreeCache = () => {
|
||||||
|
return {
|
||||||
|
type: UPDATE_RESOLVED_SUBTREE,
|
||||||
|
payload: {
|
||||||
|
path: [],
|
||||||
|
value: Map()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const validateParams = ( payload, isOAS3 ) =>{
|
export const validateParams = ( payload, isOAS3 ) =>{
|
||||||
return {
|
return {
|
||||||
type: VALIDATE_PARAMS,
|
type: VALIDATE_PARAMS,
|
||||||
@@ -251,6 +326,7 @@ export const executeRequest = (req) =>
|
|||||||
// track duration of request
|
// track duration of request
|
||||||
const startTime = Date.now()
|
const startTime = Date.now()
|
||||||
|
|
||||||
|
|
||||||
return fn.execute(req)
|
return fn.execute(req)
|
||||||
.then( res => {
|
.then( res => {
|
||||||
res.duration = Date.now() - startTime
|
res.duration = Date.now() - startTime
|
||||||
@@ -267,13 +343,22 @@ export const executeRequest = (req) =>
|
|||||||
// I'm using extras as a way to inject properties into the final, `execute` method - It's not great. Anyone have a better idea? @ponelat
|
// I'm using extras as a way to inject properties into the final, `execute` method - It's not great. Anyone have a better idea? @ponelat
|
||||||
export const execute = ( { path, method, ...extras }={} ) => (system) => {
|
export const execute = ( { path, method, ...extras }={} ) => (system) => {
|
||||||
let { fn:{fetch}, specSelectors, specActions } = system
|
let { fn:{fetch}, specSelectors, specActions } = system
|
||||||
let spec = specSelectors.spec().toJS()
|
let spec = specSelectors.specJsonWithResolvedSubtrees().toJS()
|
||||||
let scheme = specSelectors.operationScheme(path, method)
|
let scheme = specSelectors.operationScheme(path, method)
|
||||||
let { requestContentType, responseContentType } = specSelectors.contentTypeValues([path, method]).toJS()
|
let { requestContentType, responseContentType } = specSelectors.contentTypeValues([path, method]).toJS()
|
||||||
let isXml = /xml/i.test(requestContentType)
|
let isXml = /xml/i.test(requestContentType)
|
||||||
let parameters = specSelectors.parameterValues([path, method], isXml).toJS()
|
let parameters = specSelectors.parameterValues([path, method], isXml).toJS()
|
||||||
|
|
||||||
return specActions.executeRequest({fetch, spec, pathName: path, method, parameters, requestContentType, scheme, responseContentType, ...extras })
|
return specActions.executeRequest({
|
||||||
|
...extras,
|
||||||
|
fetch,
|
||||||
|
spec,
|
||||||
|
pathName: path,
|
||||||
|
method, parameters,
|
||||||
|
requestContentType,
|
||||||
|
scheme,
|
||||||
|
responseContentType
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function clearResponse (path, method) {
|
export function clearResponse (path, method) {
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import { fromJS } from "immutable"
|
import { fromJS, List } from "immutable"
|
||||||
import { fromJSOrdered, validateParam } from "core/utils"
|
import { fromJSOrdered, validateParam } from "core/utils"
|
||||||
import win from "../../window"
|
import win from "../../window"
|
||||||
|
|
||||||
|
// selector-in-reducer is suboptimal, but `operationWithMeta` is more of a helper
|
||||||
|
import {
|
||||||
|
operationWithMeta
|
||||||
|
} from "./selectors"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
UPDATE_SPEC,
|
UPDATE_SPEC,
|
||||||
UPDATE_URL,
|
UPDATE_URL,
|
||||||
@@ -12,6 +17,7 @@ import {
|
|||||||
SET_REQUEST,
|
SET_REQUEST,
|
||||||
SET_MUTATED_REQUEST,
|
SET_MUTATED_REQUEST,
|
||||||
UPDATE_RESOLVED,
|
UPDATE_RESOLVED,
|
||||||
|
UPDATE_RESOLVED_SUBTREE,
|
||||||
UPDATE_OPERATION_META_VALUE,
|
UPDATE_OPERATION_META_VALUE,
|
||||||
CLEAR_RESPONSE,
|
CLEAR_RESPONSE,
|
||||||
CLEAR_REQUEST,
|
CLEAR_REQUEST,
|
||||||
@@ -39,38 +45,38 @@ export default {
|
|||||||
return state.setIn(["resolved"], fromJSOrdered(action.payload))
|
return state.setIn(["resolved"], fromJSOrdered(action.payload))
|
||||||
},
|
},
|
||||||
|
|
||||||
[UPDATE_PARAM]: ( state, {payload} ) => {
|
[UPDATE_RESOLVED_SUBTREE]: (state, action) => {
|
||||||
let { path, paramName, paramIn, value, isXml } = payload
|
const { value, path } = action.payload
|
||||||
|
return state.setIn(["resolvedSubtrees", ...path], fromJSOrdered(value))
|
||||||
|
},
|
||||||
|
|
||||||
return state.updateIn( [ "resolved", "paths", ...path, "parameters" ], fromJS([]), parameters => {
|
[UPDATE_PARAM]: ( state, {payload} ) => {
|
||||||
const index = parameters.findIndex(p => p.get( "name" ) === paramName && p.get("in") === paramIn )
|
let { path: pathMethod, paramName, paramIn, value, isXml } = payload
|
||||||
if (!(value instanceof win.File)) {
|
|
||||||
value = fromJSOrdered( value )
|
const valueKey = isXml ? "value_xml" : "value"
|
||||||
}
|
|
||||||
return parameters.setIn( [ index, isXml ? "value_xml" : "value" ], value)
|
return state.setIn(
|
||||||
})
|
["meta", "paths", ...pathMethod, "parameters", `${paramName}.${paramIn}`, valueKey],
|
||||||
|
value
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
|
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
|
||||||
let meta = state.getIn( [ "meta", "paths", ...pathMethod ], fromJS({}) )
|
let meta = state.getIn( [ "meta", "paths", ...pathMethod ], fromJS({}) )
|
||||||
let isXml = /xml/i.test(meta.get("consumes_value"))
|
let isXml = /xml/i.test(meta.get("consumes_value"))
|
||||||
|
|
||||||
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
const op = operationWithMeta(state, ...pathMethod)
|
||||||
return parameters.withMutations( parameters => {
|
|
||||||
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
|
return state.updateIn(["meta", "paths", ...pathMethod, "parameters"], fromJS({}), paramMeta => {
|
||||||
let errors = validateParam(parameters.get(i), isXml, isOAS3)
|
return op.get("parameters", List()).reduce((res, param) => {
|
||||||
parameters.setIn([i, "errors"], fromJS(errors))
|
const errors = validateParam(param, isXml, isOAS3)
|
||||||
}
|
return res.setIn([`${param.get("name")}.${param.get("in")}`, "errors"], fromJS(errors))
|
||||||
})
|
}, paramMeta)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[CLEAR_VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {
|
[CLEAR_VALIDATE_PARAMS]: ( state, { payload: { pathMethod } } ) => {
|
||||||
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
return state.updateIn( [ "meta", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
||||||
return parameters.withMutations( parameters => {
|
return parameters.map(param => param.set("errors", fromJS([])))
|
||||||
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
|
|
||||||
parameters.setIn([i, "errors"], fromJS([]))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -109,10 +115,10 @@ export default {
|
|||||||
|
|
||||||
[UPDATE_OPERATION_META_VALUE]: (state, { payload: { path, value, key } }) => {
|
[UPDATE_OPERATION_META_VALUE]: (state, { payload: { path, value, key } }) => {
|
||||||
// path is a pathMethod tuple... can't change the name now.
|
// path is a pathMethod tuple... can't change the name now.
|
||||||
let operationPath = ["resolved", "paths", ...path]
|
let operationPath = ["paths", ...path]
|
||||||
let metaPath = ["meta", "paths", ...path]
|
let metaPath = ["meta", "paths", ...path]
|
||||||
|
|
||||||
if(!state.getIn(operationPath)) {
|
if(!state.getIn(["json", ...operationPath]) && !state.getIn(["resolved", ...operationPath])) {
|
||||||
// do nothing if the operation does not exist
|
// do nothing if the operation does not exist
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,9 +42,18 @@ export const specResolved = createSelector(
|
|||||||
spec => spec.get("resolved", Map())
|
spec => spec.get("resolved", Map())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const specResolvedSubtree = (state, path) => {
|
||||||
|
return state.getIn(["resolvedSubtrees", ...path], undefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const specJsonWithResolvedSubtrees = createSelector(
|
||||||
|
state,
|
||||||
|
spec => Map().merge(spec.get("json"), spec.get("resolvedSubtrees"))
|
||||||
|
)
|
||||||
|
|
||||||
// Default Spec ( as an object )
|
// Default Spec ( as an object )
|
||||||
export const spec = state => {
|
export const spec = state => {
|
||||||
let res = specResolved(state)
|
let res = specJson(state)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +146,9 @@ export const securityDefinitions = createSelector(
|
|||||||
|
|
||||||
|
|
||||||
export const findDefinition = ( state, name ) => {
|
export const findDefinition = ( state, name ) => {
|
||||||
return specResolved(state).getIn(["definitions", name], null)
|
const resolvedRes = state.getIn(["resolvedSubtrees", "definitions", name], null)
|
||||||
|
const unresolvedRes = state.getIn(["json", "definitions", name], null)
|
||||||
|
return resolvedRes || unresolvedRes || null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const definitions = createSelector(
|
export const definitions = createSelector(
|
||||||
@@ -261,10 +272,40 @@ export const allowTryItOutFor = () => {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const operationWithMeta = (state, path, method) => {
|
||||||
|
const op = specJsonWithResolvedSubtrees(state).getIn(["paths", path, method], Map())
|
||||||
|
const meta = state.getIn(["meta", "paths", path, method], Map())
|
||||||
|
|
||||||
|
const mergedParams = op.get("parameters", List()).map((param) => {
|
||||||
|
return Map().merge(
|
||||||
|
param,
|
||||||
|
meta.getIn(["parameters", `${param.get("name")}.${param.get("in")}`])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return Map()
|
||||||
|
.merge(op, meta)
|
||||||
|
.set("parameters", mergedParams)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parameterWithMeta = (state, pathMethod, paramName, paramIn) => {
|
||||||
|
const opParams = specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod, "parameters"], Map())
|
||||||
|
const metaParams = state.getIn(["meta", "paths", ...pathMethod, "parameters"], Map())
|
||||||
|
|
||||||
|
const mergedParams = opParams.map((param) => {
|
||||||
|
return Map().merge(
|
||||||
|
param,
|
||||||
|
metaParams.get(`${param.get("name")}.${param.get("in")}`)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return mergedParams.find(param => param.get("in") === paramIn && param.get("name") === paramName, Map())
|
||||||
|
}
|
||||||
|
|
||||||
// Get the parameter value by parameter name
|
// Get the parameter value by parameter name
|
||||||
export function getParameter(state, pathMethod, name, inType) {
|
export function getParameter(state, pathMethod, name, inType) {
|
||||||
pathMethod = pathMethod || []
|
pathMethod = pathMethod || []
|
||||||
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
|
let params = state.getIn(["meta", "paths", ...pathMethod, "parameters"], fromJS([]))
|
||||||
return params.find( (p) => {
|
return params.find( (p) => {
|
||||||
return Map.isMap(p) && p.get("name") === name && p.get("in") === inType
|
return Map.isMap(p) && p.get("name") === name && p.get("in") === inType
|
||||||
}) || Map() // Always return a map
|
}) || Map() // Always return a map
|
||||||
@@ -281,8 +322,9 @@ export const hasHost = createSelector(
|
|||||||
// Get the parameter values, that the user filled out
|
// Get the parameter values, that the user filled out
|
||||||
export function parameterValues(state, pathMethod, isXml) {
|
export function parameterValues(state, pathMethod, isXml) {
|
||||||
pathMethod = pathMethod || []
|
pathMethod = pathMethod || []
|
||||||
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
|
// let paramValues = state.getIn(["meta", "paths", ...pathMethod, "parameters"], fromJS([]))
|
||||||
return params.reduce( (hash, p) => {
|
let paramValues = operationWithMeta(state, ...pathMethod).get("parameters", List())
|
||||||
|
return paramValues.reduce( (hash, p) => {
|
||||||
let value = isXml && p.get("in") === "body" ? p.get("value_xml") : p.get("value")
|
let value = isXml && p.get("in") === "body" ? p.get("value_xml") : p.get("value")
|
||||||
return hash.set(`${p.get("in")}.${p.get("name")}`, value)
|
return hash.set(`${p.get("in")}.${p.get("name")}`, value)
|
||||||
}, fromJS({}))
|
}, fromJS({}))
|
||||||
@@ -305,7 +347,7 @@ export function parametersIncludeType(parameters, typeValue="") {
|
|||||||
// Get the consumes/produces value that the user selected
|
// Get the consumes/produces value that the user selected
|
||||||
export function contentTypeValues(state, pathMethod) {
|
export function contentTypeValues(state, pathMethod) {
|
||||||
pathMethod = pathMethod || []
|
pathMethod = pathMethod || []
|
||||||
let op = spec(state).getIn(["paths", ...pathMethod], fromJS({}))
|
let op = specJsonWithResolvedSubtrees(state).getIn(["paths", ...pathMethod], fromJS({}))
|
||||||
let meta = state.getIn(["meta", "paths", ...pathMethod], fromJS({}))
|
let meta = state.getIn(["meta", "paths", ...pathMethod], fromJS({}))
|
||||||
let producesValue = currentProducesFor(state, pathMethod)
|
let producesValue = currentProducesFor(state, pathMethod)
|
||||||
|
|
||||||
@@ -327,14 +369,14 @@ export function contentTypeValues(state, pathMethod) {
|
|||||||
// Get the consumes/produces by path
|
// Get the consumes/produces by path
|
||||||
export function operationConsumes(state, pathMethod) {
|
export function operationConsumes(state, pathMethod) {
|
||||||
pathMethod = pathMethod || []
|
pathMethod = pathMethod || []
|
||||||
return spec(state).getIn(["paths", ...pathMethod, "consumes"], fromJS({}))
|
return state.getIn(["meta", ...pathMethod, "consumes"], fromJS({}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the currently selected produces value for an operation
|
// Get the currently selected produces value for an operation
|
||||||
export function currentProducesFor(state, pathMethod) {
|
export function currentProducesFor(state, pathMethod) {
|
||||||
pathMethod = pathMethod || []
|
pathMethod = pathMethod || []
|
||||||
|
|
||||||
const operation = spec(state).getIn(["paths", ...pathMethod], null)
|
const operation = specJsonWithResolvedSubtrees(state).getIn([ "paths", ...pathMethod], null)
|
||||||
|
|
||||||
if(operation === null) {
|
if(operation === null) {
|
||||||
// return nothing if the operation does not exist
|
// return nothing if the operation does not exist
|
||||||
@@ -362,10 +404,10 @@ export const canExecuteScheme = ( state, path, method ) => {
|
|||||||
|
|
||||||
export const validateBeforeExecute = ( state, pathMethod ) => {
|
export const validateBeforeExecute = ( state, pathMethod ) => {
|
||||||
pathMethod = pathMethod || []
|
pathMethod = pathMethod || []
|
||||||
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
|
let paramValues = state.getIn(["meta", "paths", ...pathMethod, "parameters"], fromJS([]))
|
||||||
let isValid = true
|
let isValid = true
|
||||||
|
|
||||||
params.forEach( (p) => {
|
paramValues.forEach( (p) => {
|
||||||
let errors = p.get("errors")
|
let errors = p.get("errors")
|
||||||
if ( errors && errors.count() ) {
|
if ( errors && errors.count() ) {
|
||||||
isValid = false
|
isValid = false
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export const updateSpec = (ori, {specActions}) => (...args) => {
|
|||||||
|
|
||||||
export const updateJsonSpec = (ori, {specActions}) => (...args) => {
|
export const updateJsonSpec = (ori, {specActions}) => (...args) => {
|
||||||
ori(...args)
|
ori(...args)
|
||||||
specActions.resolveSpec(...args)
|
specActions.invalidateResolvedSubtreeCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log the request ( just for debugging, shouldn't affect prod )
|
// Log the request ( just for debugging, shouldn't affect prod )
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ module.exports = function({ configs }) {
|
|||||||
buildRequest: Swagger.buildRequest,
|
buildRequest: Swagger.buildRequest,
|
||||||
execute: Swagger.execute,
|
execute: Swagger.execute,
|
||||||
resolve: Swagger.resolve,
|
resolve: Swagger.resolve,
|
||||||
|
resolveSubtree: Swagger.resolveSubtree,
|
||||||
serializeRes: Swagger.serializeRes,
|
serializeRes: Swagger.serializeRes,
|
||||||
opId: Swagger.helpers.opId
|
opId: Swagger.helpers.opId
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ section.models
|
|||||||
.model-box
|
.model-box
|
||||||
{
|
{
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: rgba($section-models-model-box-background-color,.1);
|
background: rgba($section-models-model-box-background-color,.1);
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ describe("<Models/>", function(){
|
|||||||
def1: {},
|
def1: {},
|
||||||
def2: {}
|
def2: {}
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
|
specResolvedSubtree: () => {}
|
||||||
},
|
},
|
||||||
layoutSelectors: {
|
layoutSelectors: {
|
||||||
isShown: createSpy()
|
isShown: createSpy()
|
||||||
|
|||||||
@@ -177,6 +177,10 @@ describe("spec plugin - actions", function(){
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("requestResolvedSubtree", () => {
|
||||||
|
it("should return a promise ")
|
||||||
|
})
|
||||||
|
|
||||||
it.skip("should call errActions.newErr, if the fn.execute rejects", function(){
|
it.skip("should call errActions.newErr, if the fn.execute rejects", function(){
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ describe("spec plugin - selectors", function(){
|
|||||||
|
|
||||||
// Given
|
// Given
|
||||||
const spec = fromJS({
|
const spec = fromJS({
|
||||||
resolved: {
|
json: {
|
||||||
paths: {
|
paths: {
|
||||||
"/one": {
|
"/one": {
|
||||||
get: {
|
get: {
|
||||||
@@ -55,7 +55,7 @@ describe("spec plugin - selectors", function(){
|
|||||||
it("should return { requestContentType, responseContentType } from an operation", function(){
|
it("should return { requestContentType, responseContentType } from an operation", function(){
|
||||||
// Given
|
// Given
|
||||||
let state = fromJS({
|
let state = fromJS({
|
||||||
resolved: {
|
json: {
|
||||||
paths: {
|
paths: {
|
||||||
"/one": {
|
"/one": {
|
||||||
get: {}
|
get: {}
|
||||||
@@ -86,7 +86,7 @@ describe("spec plugin - selectors", function(){
|
|||||||
it("should default to the first `produces` array value if current is not set", function(){
|
it("should default to the first `produces` array value if current is not set", function(){
|
||||||
// Given
|
// Given
|
||||||
let state = fromJS({
|
let state = fromJS({
|
||||||
resolved: {
|
json: {
|
||||||
paths: {
|
paths: {
|
||||||
"/one": {
|
"/one": {
|
||||||
get: {
|
get: {
|
||||||
@@ -121,7 +121,7 @@ describe("spec plugin - selectors", function(){
|
|||||||
it("should default to `application/json` if a default produces value is not available", function(){
|
it("should default to `application/json` if a default produces value is not available", function(){
|
||||||
// Given
|
// Given
|
||||||
let state = fromJS({
|
let state = fromJS({
|
||||||
resolved: {
|
json: {
|
||||||
paths: {
|
paths: {
|
||||||
"/one": {
|
"/one": {
|
||||||
get: {}
|
get: {}
|
||||||
@@ -151,7 +151,7 @@ describe("spec plugin - selectors", function(){
|
|||||||
it("should prioritize consumes value first from an operation", function(){
|
it("should prioritize consumes value first from an operation", function(){
|
||||||
// Given
|
// Given
|
||||||
let state = fromJS({
|
let state = fromJS({
|
||||||
resolved: {
|
json: {
|
||||||
paths: {
|
paths: {
|
||||||
"/one": {
|
"/one": {
|
||||||
get: {
|
get: {
|
||||||
@@ -182,7 +182,7 @@ describe("spec plugin - selectors", function(){
|
|||||||
it("should fallback to multipart/form-data if there is no consumes value but there is a file parameter", function(){
|
it("should fallback to multipart/form-data if there is no consumes value but there is a file parameter", function(){
|
||||||
// Given
|
// Given
|
||||||
let state = fromJS({
|
let state = fromJS({
|
||||||
resolved: {
|
json: {
|
||||||
paths: {
|
paths: {
|
||||||
"/one": {
|
"/one": {
|
||||||
get: {
|
get: {
|
||||||
@@ -204,7 +204,7 @@ describe("spec plugin - selectors", function(){
|
|||||||
it("should fallback to application/x-www-form-urlencoded if there is no consumes value, no file parameter, but there is a formData parameter", function(){
|
it("should fallback to application/x-www-form-urlencoded if there is no consumes value, no file parameter, but there is a formData parameter", function(){
|
||||||
// Given
|
// Given
|
||||||
let state = fromJS({
|
let state = fromJS({
|
||||||
resolved: {
|
json: {
|
||||||
paths: {
|
paths: {
|
||||||
"/one": {
|
"/one": {
|
||||||
get: {
|
get: {
|
||||||
@@ -244,7 +244,7 @@ describe("spec plugin - selectors", function(){
|
|||||||
// Given
|
// Given
|
||||||
let state = fromJS({
|
let state = fromJS({
|
||||||
url: "https://generator.swagger.io/api/swagger.json",
|
url: "https://generator.swagger.io/api/swagger.json",
|
||||||
resolved: {
|
json: {
|
||||||
paths: {
|
paths: {
|
||||||
"/one": {
|
"/one": {
|
||||||
get: {
|
get: {
|
||||||
|
|||||||
Reference in New Issue
Block a user