Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e41067b4c1 | ||
|
|
558d054986 | ||
|
|
d1779dc47b | ||
|
|
ba1603a7e2 | ||
|
|
ecf688171f | ||
|
|
54ed39d69f | ||
|
|
48a65983c1 | ||
|
|
1f565b4847 | ||
|
|
ba59db2449 |
@@ -22,7 +22,7 @@ The OpenAPI Specification has undergone 5 revisions since initial creation in 20
|
||||
|
||||
Swagger UI Version | Release Date | OpenAPI Spec compatibility | Notes
|
||||
------------------ | ------------ | -------------------------- | -----
|
||||
3.10.0 | 2018-02-09 | 2.0, 3.0 | [tag v3.10.0](https://github.com/swagger-api/swagger-ui/tree/v3.10.0)
|
||||
3.11.0 | 2018-02-09 | 2.0, 3.0 | [tag v3.11.0](https://github.com/swagger-api/swagger-ui/tree/v3.11.0)
|
||||
3.0.21 | 2017-07-26 | 2.0 | [tag v3.0.21](https://github.com/swagger-api/swagger-ui/tree/v3.0.21)
|
||||
2.2.10 | 2017-01-04 | 1.1, 1.2, 2.0 | [tag v2.2.10](https://github.com/swagger-api/swagger-ui/tree/v2.2.10)
|
||||
2.1.5 | 2016-07-20 | 1.1, 1.2, 2.0 | [tag v2.1.5](https://github.com/swagger-api/swagger-ui/tree/v2.1.5)
|
||||
|
||||
@@ -45,11 +45,18 @@
|
||||
oauth2.auth.code = qp.code;
|
||||
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||
} else {
|
||||
let oauthErrorMsg
|
||||
if (qp.error) {
|
||||
oauthErrorMsg = "["+qp.error+"]: " +
|
||||
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||
}
|
||||
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "error",
|
||||
message: "Authorization failed: no accessCode received from the server"
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
Vendored
+8
-1
@@ -45,11 +45,18 @@
|
||||
oauth2.auth.code = qp.code;
|
||||
oauth2.callback({auth: oauth2.auth, redirectUrl: redirectUrl});
|
||||
} else {
|
||||
let oauthErrorMsg
|
||||
if (qp.error) {
|
||||
oauthErrorMsg = "["+qp.error+"]: " +
|
||||
(qp.error_description ? qp.error_description+ ". " : "no accessCode received from the server. ") +
|
||||
(qp.error_uri ? "More info: "+qp.error_uri : "");
|
||||
}
|
||||
|
||||
oauth2.errCb({
|
||||
authId: oauth2.auth.name,
|
||||
source: "auth",
|
||||
level: "error",
|
||||
message: "Authorization failed: no accessCode received from the server"
|
||||
message: oauthErrorMsg || "[Authorization failed]: no accessCode received from the server"
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
||||
Vendored
+15
-15
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+12
-12
@@ -1,17 +1,17 @@
|
||||
#### Usage
|
||||
- [Installation](docs/usage/installation.md)
|
||||
- [Configuration](docs/usage/configuration.md)
|
||||
- [CORS](docs/usage/cors.md)
|
||||
- [OAuth2](docs/usage/oauth2.md)
|
||||
- [Deep Linking](docs/usage/deep-linking.md)
|
||||
- [Limitations](docs/usage/limitations.md)
|
||||
- [Version detection](docs/usage/version-detection.md)
|
||||
- [Installation](usage/installation.md)
|
||||
- [Configuration](usage/configuration.md)
|
||||
- [CORS](usage/cors.md)
|
||||
- [OAuth2](usage/oauth2.md)
|
||||
- [Deep Linking](usage/deep-linking.md)
|
||||
- [Limitations](usage/limitations.md)
|
||||
- [Version detection](usage/version-detection.md)
|
||||
|
||||
#### Customization
|
||||
- [Overview](docs/customization/overview.md)
|
||||
- [Plugin API](docs/customization/plugin-api.md)
|
||||
- [Custom layout](docs/customization/custom-layout.md)
|
||||
- [Overview](customization/overview.md)
|
||||
- [Plugin API](customization/plugin-api.md)
|
||||
- [Custom layout](customization/custom-layout.md)
|
||||
|
||||
#### Development
|
||||
- [Setting up](docs/development/setting-up.md)
|
||||
- [Scripts](docs/development/scripts.md)
|
||||
- [Setting up](development/setting-up.md)
|
||||
- [Scripts](development/scripts.md)
|
||||
|
||||
+4
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "swagger-ui",
|
||||
"version": "3.10.0",
|
||||
"version": "3.11.0",
|
||||
"main": "dist/swagger-ui.js",
|
||||
"repository": "git@github.com:swagger-api/swagger-ui.git",
|
||||
"contributors": [
|
||||
@@ -53,7 +53,7 @@
|
||||
"ieee754": "^1.1.8",
|
||||
"immutable": "^3.x.x",
|
||||
"js-yaml": "^3.5.5",
|
||||
"lodash": "4.17.2",
|
||||
"lodash": "4.17.5",
|
||||
"matcher": "^0.1.2",
|
||||
"memoizee": "0.4.1",
|
||||
"promise-worker": "^1.1.1",
|
||||
@@ -63,6 +63,7 @@
|
||||
"react-addons-shallow-compare": "0.14.8",
|
||||
"react-addons-test-utils": "^15.6.2",
|
||||
"react-collapse": "^4.0.3",
|
||||
"react-debounce-input": "^3.2.0",
|
||||
"react-dom": "^15.6.2",
|
||||
"react-height": "^2.0.0",
|
||||
"react-hot-loader": "1.3.1",
|
||||
@@ -82,7 +83,7 @@
|
||||
"scroll-to-element": "^2.0.0",
|
||||
"serialize-error": "2.0.0",
|
||||
"shallowequal": "0.2.2",
|
||||
"swagger-client": "^3.4.11",
|
||||
"swagger-client": "^3.5.0",
|
||||
"url-parse": "^1.1.8",
|
||||
"whatwg-fetch": "0.11.1",
|
||||
"worker-loader": "^0.7.1",
|
||||
|
||||
@@ -8,14 +8,17 @@ export default class ModelCollapse extends Component {
|
||||
children: PropTypes.any,
|
||||
title: PropTypes.element,
|
||||
modelName: PropTypes.string,
|
||||
onToggle: PropTypes.func
|
||||
classes: PropTypes.string,
|
||||
onToggle: PropTypes.func,
|
||||
hideSelfOnExpand: PropTypes.bool,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
collapsedContent: "{...}",
|
||||
expanded: false,
|
||||
title: null,
|
||||
onToggle: () => {}
|
||||
onToggle: () => {},
|
||||
hideSelfOnExpand: false
|
||||
}
|
||||
|
||||
constructor(props, context) {
|
||||
@@ -29,17 +32,23 @@ export default class ModelCollapse extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps){
|
||||
componentDidMount() {
|
||||
const { hideSelfOnExpand, expanded, modelName } = this.props
|
||||
if(hideSelfOnExpand && 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)
|
||||
}
|
||||
}
|
||||
|
||||
if(this.props.expanded!= nextProps.expanded){
|
||||
componentWillReceiveProps(nextProps){
|
||||
if(this.props.expanded !== nextProps.expanded){
|
||||
this.setState({expanded: nextProps.expanded})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
toggleCollapsed=()=>{
|
||||
|
||||
|
||||
if(this.props.onToggle){
|
||||
this.props.onToggle(this.props.modelName,!this.state.expanded)
|
||||
}
|
||||
@@ -50,9 +59,18 @@ export default class ModelCollapse extends Component {
|
||||
}
|
||||
|
||||
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 (
|
||||
<span>
|
||||
<span className={classes || ""}>
|
||||
{ title && <span onClick={this.toggleCollapsed} style={{ "cursor": "pointer" }}>{title}</span> }
|
||||
<span onClick={ this.toggleCollapsed } style={{ "cursor": "pointer" }}>
|
||||
<span className={ "model-toggle" + ( this.state.expanded ? "" : " collapsed" ) }></span>
|
||||
|
||||
@@ -49,6 +49,17 @@ export default class Model extends ImmutablePureComponent {
|
||||
schema = this.getRefSchema( name )
|
||||
}
|
||||
|
||||
if(!schema) {
|
||||
return <span className="model model-title">
|
||||
<span className="model-title__text">{ name }</span>
|
||||
<img src={require("core/../img/rolling-load.svg")} height={"20px"} width={"20px"} style={{
|
||||
marginLeft: "1em",
|
||||
position: "relative",
|
||||
bottom: "0px"
|
||||
}} />
|
||||
</span>
|
||||
}
|
||||
|
||||
const deprecated = specSelectors.isOAS3() && schema.get("deprecated")
|
||||
isRef = isRef !== undefined ? isRef : !!$$ref
|
||||
type = schema && schema.get("type") || type
|
||||
|
||||
@@ -6,11 +6,29 @@ export default class Models extends Component {
|
||||
static propTypes = {
|
||||
getComponent: PropTypes.func,
|
||||
specSelectors: PropTypes.object,
|
||||
specActions: PropTypes.object.isRequired,
|
||||
layoutSelectors: PropTypes.object,
|
||||
layoutActions: PropTypes.object,
|
||||
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(){
|
||||
let { specSelectors, getComponent, layoutSelectors, layoutActions, getConfigs } = this.props
|
||||
let definitions = specSelectors.definitions()
|
||||
@@ -18,10 +36,11 @@ export default class Models extends Component {
|
||||
if (!definitions.size || defaultModelsExpandDepth < 0) return null
|
||||
|
||||
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 Collapse = getComponent("Collapse")
|
||||
const ModelCollapse = getComponent("ModelCollapse")
|
||||
|
||||
return <section className={ showModels ? "models is-open" : "models"}>
|
||||
<h4 onClick={() => layoutActions.show("models", !showModels)}>
|
||||
@@ -32,18 +51,40 @@ export default class Models extends Component {
|
||||
</h4>
|
||||
<Collapse isOpened={showModels}>
|
||||
{
|
||||
definitions.entrySeq().map( ( [ name, model ])=>{
|
||||
definitions.entrySeq().map( ( [ name ])=>{
|
||||
|
||||
const schema = specSelectors.specResolvedSubtree([...specPathBase, 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 }
|
||||
schema={ schema }
|
||||
specPath={Im.List([...specPathBase, name])}
|
||||
getComponent={ getComponent }
|
||||
specSelectors={ specSelectors }
|
||||
getConfigs = {getConfigs}
|
||||
layoutSelectors = {layoutSelectors}
|
||||
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}` }>
|
||||
<ModelWrapper name={ name }
|
||||
expandDepth={ defaultModelsExpandDepth }
|
||||
schema={ model }
|
||||
specPath={Im.List([...specPathBase, name])}
|
||||
getComponent={ getComponent }
|
||||
specSelectors={ specSelectors }
|
||||
getConfigs = {getConfigs}
|
||||
layoutSelectors = {layoutSelectors}
|
||||
layoutActions = {layoutActions}/>
|
||||
<ModelCollapse
|
||||
classes="model-box"
|
||||
collapsedContent={this.getCollapsedContent(name)}
|
||||
onToggle={this.handleToggle}
|
||||
title={title}
|
||||
modelName={name}
|
||||
hideSelfOnExpand={true}
|
||||
expanded={defaultModelsExpandDepth > 1}
|
||||
>{content}</ModelCollapse>
|
||||
</div>
|
||||
}).toArray()
|
||||
}
|
||||
|
||||
@@ -5,10 +5,12 @@ import { getExtensions, sanitizeUrl } from "core/utils"
|
||||
import { Iterable, List } from "immutable"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
|
||||
|
||||
export default class Operation extends PureComponent {
|
||||
static propTypes = {
|
||||
specPath: ImPropTypes.list.isRequired,
|
||||
operation: PropTypes.instanceOf(Iterable).isRequired,
|
||||
summary: PropTypes.string,
|
||||
response: PropTypes.instanceOf(Iterable),
|
||||
request: PropTypes.instanceOf(Iterable),
|
||||
|
||||
@@ -34,7 +36,8 @@ export default class Operation extends PureComponent {
|
||||
operation: null,
|
||||
response: null,
|
||||
request: null,
|
||||
specPath: List()
|
||||
specPath: List(),
|
||||
summary: ""
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -59,6 +62,8 @@ export default class Operation extends PureComponent {
|
||||
let operationProps = this.props.operation
|
||||
|
||||
let {
|
||||
summary,
|
||||
deprecated,
|
||||
isShown,
|
||||
isAuthorized,
|
||||
path,
|
||||
@@ -76,14 +81,13 @@ export default class Operation extends PureComponent {
|
||||
} = operationProps.toJS()
|
||||
|
||||
let {
|
||||
summary,
|
||||
summary: resolvedSummary,
|
||||
description,
|
||||
deprecated,
|
||||
externalDocs,
|
||||
schemes
|
||||
} = op.operation
|
||||
} = op
|
||||
|
||||
let operation = operationProps.getIn(["op", "operation"])
|
||||
let operation = operationProps.getIn(["op"])
|
||||
let security = operationProps.get("security")
|
||||
let responses = operation.get("responses")
|
||||
let produces = operation.get("produces")
|
||||
@@ -132,7 +136,7 @@ export default class Operation extends PureComponent {
|
||||
|
||||
{ !showSummary ? null :
|
||||
<div className="opblock-summary-description">
|
||||
{ summary }
|
||||
{ resolvedSummary || summary }
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -152,6 +156,9 @@ export default class Operation extends PureComponent {
|
||||
|
||||
<Collapse isOpened={isShown}>
|
||||
<div className="opblock-body">
|
||||
{ operation && operation.size ? null :
|
||||
<img height={"32px"} width={"32px"} src={require("core/../img/rolling-load.svg")} className="opblock-loading-animation" />
|
||||
}
|
||||
{ deprecated && <h4 className="opblock-title_normal"> Warning: Deprecated</h4>}
|
||||
{ description &&
|
||||
<div className="opblock-description-wrapper">
|
||||
@@ -173,23 +180,25 @@ export default class Operation extends PureComponent {
|
||||
</div> : null
|
||||
}
|
||||
|
||||
<Parameters
|
||||
parameters={parameters}
|
||||
specPath={specPath.push("parameters")}
|
||||
operation={operation}
|
||||
onChangeKey={onChangeKey}
|
||||
onTryoutClick = { onTryoutClick }
|
||||
onCancelClick = { onCancelClick }
|
||||
tryItOutEnabled = { tryItOutEnabled }
|
||||
allowTryItOut={allowTryItOut}
|
||||
{ !operation || !operation.size ? null :
|
||||
<Parameters
|
||||
parameters={parameters}
|
||||
specPath={specPath.push("parameters")}
|
||||
operation={operation}
|
||||
onChangeKey={onChangeKey}
|
||||
onTryoutClick = { onTryoutClick }
|
||||
onCancelClick = { onCancelClick }
|
||||
tryItOutEnabled = { tryItOutEnabled }
|
||||
allowTryItOut={allowTryItOut}
|
||||
|
||||
fn={fn}
|
||||
getComponent={ getComponent }
|
||||
specActions={ specActions }
|
||||
specSelectors={ specSelectors }
|
||||
pathMethod={ [path, method] }
|
||||
getConfigs={ getConfigs }
|
||||
/>
|
||||
fn={fn}
|
||||
getComponent={ getComponent }
|
||||
specActions={ specActions }
|
||||
specSelectors={ specSelectors }
|
||||
pathMethod={ [path, method] }
|
||||
getConfigs={ getConfigs }
|
||||
/>
|
||||
}
|
||||
|
||||
{ !tryItOutEnabled ? null :
|
||||
<OperationServers
|
||||
|
||||
@@ -47,7 +47,7 @@ export default class ParamBody extends PureComponent {
|
||||
|
||||
updateValues = (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 isJson = /json/i.test(consumesValue)
|
||||
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 ContentType = getComponent("contentType")
|
||||
// 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 consumesValue = specSelectors.contentTypeValues(pathMethod).get("requestContentType")
|
||||
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 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") : ""
|
||||
if ( defaultValue !== undefined && value === undefined ) {
|
||||
this.onChangeWrapper(defaultValue)
|
||||
@@ -37,7 +37,7 @@ export default class ParameterRow extends Component {
|
||||
|
||||
let example = param.get("example")
|
||||
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
|
||||
|
||||
if(isOAS3()) {
|
||||
@@ -104,8 +104,7 @@ export default class ParameterRow extends Component {
|
||||
let isFormDataSupported = "FormData" in win
|
||||
let required = param.get("required")
|
||||
let itemType = param.getIn(isOAS3 && isOAS3() ? ["schema", "items", "type"] : ["items", "type"])
|
||||
let parameter = specSelectors.getParameter(pathMethod, param.get("name"), param.get("in"))
|
||||
let value = parameter ? parameter.get("value") : ""
|
||||
let value = param ? param.get("value") : ""
|
||||
let extensions = getExtensions(param)
|
||||
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ export default class Parameters extends Component {
|
||||
specPath={specPath.push(i.toString())}
|
||||
getComponent={ getComponent }
|
||||
getConfigs={ getConfigs }
|
||||
param={ parameter }
|
||||
param={ specSelectors.parameterWithMeta(pathMethod, parameter.get("name"), parameter.get("in")) }
|
||||
key={ `${parameter.get( "in" )}.${parameter.get("name")}` }
|
||||
onChange={ this.onChange }
|
||||
onChangeConsumes={this.onChangeConsumesWrapper}
|
||||
|
||||
@@ -3,7 +3,17 @@ import PropTypes from "prop-types"
|
||||
import Remarkable from "remarkable"
|
||||
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 }) {
|
||||
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({
|
||||
html: true,
|
||||
typographer: true,
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { PureComponent } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import { helpers } from "swagger-client"
|
||||
import { Iterable, fromJS } from "immutable"
|
||||
import { Iterable, fromJS, Map } from "immutable"
|
||||
|
||||
const { opId } = helpers
|
||||
|
||||
@@ -82,13 +82,24 @@ export default class OperationContainer extends PureComponent {
|
||||
}
|
||||
|
||||
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 })
|
||||
}
|
||||
|
||||
if(isShown && resolvedSubtree === undefined) {
|
||||
specActions.requestResolvedSubtree(["paths", path, method])
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -108,7 +119,7 @@ export default class OperationContainer extends PureComponent {
|
||||
|
||||
render() {
|
||||
let {
|
||||
op,
|
||||
op: unresolvedOp,
|
||||
tag,
|
||||
path,
|
||||
method,
|
||||
@@ -140,10 +151,14 @@ export default class OperationContainer extends PureComponent {
|
||||
|
||||
const Operation = getComponent( "operation" )
|
||||
|
||||
const resolvedSubtree = specSelectors.specResolvedSubtree(["paths", path, method]) || Map()
|
||||
|
||||
const operationProps = fromJS({
|
||||
op,
|
||||
op: resolvedSubtree || Map(),
|
||||
tag,
|
||||
path,
|
||||
summary: unresolvedOp.getIn(["operation", "summary"]) || "",
|
||||
deprecated: resolvedSubtree.get("deprecated") || unresolvedOp.getIn(["operation", "deprecated"]) || false,
|
||||
method,
|
||||
security,
|
||||
isAuthorized,
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { PureComponent, Component } from "react"
|
||||
import PropTypes from "prop-types"
|
||||
import { List, fromJS } from "immutable"
|
||||
import ImPropTypes from "react-immutable-proptypes"
|
||||
import DebounceInput from "react-debounce-input"
|
||||
//import "less/json-schema-form"
|
||||
|
||||
const noop = ()=> {}
|
||||
@@ -79,10 +80,13 @@ export class JsonSchema_string extends Component {
|
||||
disabled={isDisabled}/>)
|
||||
}
|
||||
else {
|
||||
return (<Input type={ schema.format === "password" ? "password" : "text" }
|
||||
return (<DebounceInput
|
||||
type={ schema.format === "password" ? "password" : "text" }
|
||||
className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
value={value}
|
||||
minLength={0}
|
||||
debounceTimeout={350}
|
||||
placeholder={description}
|
||||
onChange={ this.onChange }
|
||||
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
|
||||
|
||||
|
||||
export const updateResolved = (ori, { layoutActions, getConfigs }) => (...args) => {
|
||||
export const updateJsonSpec = (ori, { layoutActions, getConfigs }) => (...args) => {
|
||||
ori(...args)
|
||||
|
||||
const isDeepLinkingEnabled = getConfigs().deepLinking
|
||||
|
||||
@@ -30,7 +30,9 @@ export default function downloadUrlPlugin (toolbox) {
|
||||
}
|
||||
specActions.updateLoadingStatus("success")
|
||||
specActions.updateSpec(res.text)
|
||||
specActions.updateUrl(url)
|
||||
if(specSelectors.url() !== url) {
|
||||
specActions.updateUrl(url)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
@@ -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_AUTH_ERR = "err_new_auth_err"
|
||||
export const CLEAR = "err_clear"
|
||||
export const CLEAR_BY = "err_clear_by"
|
||||
|
||||
export function newThrownErr(err) {
|
||||
return {
|
||||
@@ -49,3 +50,11 @@ export function clear(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_BATCH,
|
||||
NEW_AUTH_ERR,
|
||||
CLEAR
|
||||
CLEAR,
|
||||
CLEAR_BY,
|
||||
} from "./actions"
|
||||
|
||||
import reject from "lodash/reject"
|
||||
|
||||
import Im, { fromJS, List } from "immutable"
|
||||
import { fromJS, List } from "immutable"
|
||||
|
||||
import transformErrors from "./error-transformers/hook"
|
||||
|
||||
@@ -65,11 +64,34 @@ export default function(system) {
|
||||
},
|
||||
|
||||
[CLEAR]: (state, { payload }) => {
|
||||
if(!payload) {
|
||||
return
|
||||
if(!payload || !state.get("errors")) {
|
||||
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({
|
||||
errors: newErrors
|
||||
})
|
||||
|
||||
@@ -20,7 +20,7 @@ const state = state => {
|
||||
return state || Map()
|
||||
}
|
||||
|
||||
const nullSelector = createSelector(() => null)
|
||||
const nullSelector = createSelector(() => null)
|
||||
|
||||
const OAS3NullSelector = onlyOAS3(nullSelector)
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import YAML from "js-yaml"
|
||||
import { Map } from "immutable"
|
||||
import parseUrl from "url-parse"
|
||||
import serializeError from "serialize-error"
|
||||
import isString from "lodash/isString"
|
||||
import debounce from "lodash/debounce"
|
||||
import set from "lodash/set"
|
||||
import { isJSONObject } from "core/utils"
|
||||
|
||||
// Actions conform to FSA (flux-standard-actions)
|
||||
@@ -21,6 +24,7 @@ export const CLEAR_REQUEST = "spec_clear_request"
|
||||
export const CLEAR_VALIDATE_PARAMS = "spec_clear_validate_param"
|
||||
export const UPDATE_OPERATION_META_VALUE = "spec_update_operation_meta_value"
|
||||
export const UPDATE_RESOLVED = "spec_update_resolved"
|
||||
export const UPDATE_RESOLVED_SUBTREE = "spec_update_resolved_subtree"
|
||||
export const SET_SCHEME = "set_scheme"
|
||||
|
||||
const toStr = (str) => isString(str) ? str : ""
|
||||
@@ -74,7 +78,14 @@ export const parseToJson = (str) => ({specActions, specSelectors, errActions}) =
|
||||
return {}
|
||||
}
|
||||
|
||||
let hasWarnedAboutResolveSpecDeprecation = false
|
||||
|
||||
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 {
|
||||
modelPropertyMacro,
|
||||
parameterMacro,
|
||||
@@ -124,6 +135,86 @@ export const resolveSpec = (json, url) => ({specActions, specSelectors, errActio
|
||||
})
|
||||
}
|
||||
|
||||
let requestBatch = []
|
||||
|
||||
const debResolveSubtrees = debounce(async () => {
|
||||
const system = requestBatch.system // Just a reference to the "latest" system
|
||||
|
||||
if(!system) {
|
||||
console.error("debResolveSubtrees: don't have a system to operate on, aborting.")
|
||||
return
|
||||
}
|
||||
const {
|
||||
errActions,
|
||||
errSelectors,
|
||||
fn: {
|
||||
resolveSubtree,
|
||||
AST: { getLineNumberForPath }
|
||||
},
|
||||
specSelectors,
|
||||
specActions,
|
||||
} = system
|
||||
|
||||
if(!resolveSubtree) {
|
||||
console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing.")
|
||||
return
|
||||
}
|
||||
|
||||
const specStr = specSelectors.specStr()
|
||||
|
||||
try {
|
||||
var batchResult = await requestBatch.reduce(async (prev, path) => {
|
||||
const { resultMap, specWithCurrentSubtrees } = await prev
|
||||
|
||||
const { errors, spec } = await resolveSubtree(specWithCurrentSubtrees, path)
|
||||
|
||||
if(errSelectors.allErrors().size) {
|
||||
errActions.clear({
|
||||
type: "thrown"
|
||||
})
|
||||
}
|
||||
|
||||
if(Array.isArray(errors) && errors.length > 0) {
|
||||
let preparedErrors = errors
|
||||
.map(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)
|
||||
}
|
||||
|
||||
set(resultMap, path, spec)
|
||||
set(specWithCurrentSubtrees, path, spec)
|
||||
|
||||
return {
|
||||
resultMap,
|
||||
specWithCurrentSubtrees
|
||||
}
|
||||
}, Promise.resolve({
|
||||
resultMap: (specSelectors.specResolvedSubtree([]) || Map()).toJS(),
|
||||
specWithCurrentSubtrees: specSelectors.specJson().toJS()
|
||||
}))
|
||||
|
||||
delete requestBatch.system
|
||||
requestBatch = [] // Clear stack
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
specActions.updateResolvedSubtree([], batchResult.resultMap)
|
||||
}, 35)
|
||||
|
||||
export const requestResolvedSubtree = path => system => {
|
||||
requestBatch.push(path)
|
||||
requestBatch.system = system
|
||||
debResolveSubtrees()
|
||||
}
|
||||
|
||||
export function changeParam( path, paramName, paramIn, value, isXml ){
|
||||
return {
|
||||
type: UPDATE_PARAM,
|
||||
@@ -131,6 +222,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 ) =>{
|
||||
return {
|
||||
type: VALIDATE_PARAMS,
|
||||
@@ -251,6 +359,7 @@ export const executeRequest = (req) =>
|
||||
// track duration of request
|
||||
const startTime = Date.now()
|
||||
|
||||
|
||||
return fn.execute(req)
|
||||
.then( res => {
|
||||
res.duration = Date.now() - startTime
|
||||
@@ -267,13 +376,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
|
||||
export const execute = ( { path, method, ...extras }={} ) => (system) => {
|
||||
let { fn:{fetch}, specSelectors, specActions } = system
|
||||
let spec = specSelectors.spec().toJS()
|
||||
let spec = specSelectors.specJsonWithResolvedSubtrees().toJS()
|
||||
let scheme = specSelectors.operationScheme(path, method)
|
||||
let { requestContentType, responseContentType } = specSelectors.contentTypeValues([path, method]).toJS()
|
||||
let isXml = /xml/i.test(requestContentType)
|
||||
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) {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { fromJS } from "immutable"
|
||||
import { fromJS, List } from "immutable"
|
||||
import { fromJSOrdered, validateParam } from "core/utils"
|
||||
import win from "../../window"
|
||||
|
||||
// selector-in-reducer is suboptimal, but `operationWithMeta` is more of a helper
|
||||
import {
|
||||
operationWithMeta
|
||||
} from "./selectors"
|
||||
|
||||
import {
|
||||
UPDATE_SPEC,
|
||||
UPDATE_URL,
|
||||
@@ -12,6 +17,7 @@ import {
|
||||
SET_REQUEST,
|
||||
SET_MUTATED_REQUEST,
|
||||
UPDATE_RESOLVED,
|
||||
UPDATE_RESOLVED_SUBTREE,
|
||||
UPDATE_OPERATION_META_VALUE,
|
||||
CLEAR_RESPONSE,
|
||||
CLEAR_REQUEST,
|
||||
@@ -39,38 +45,38 @@ export default {
|
||||
return state.setIn(["resolved"], fromJSOrdered(action.payload))
|
||||
},
|
||||
|
||||
[UPDATE_PARAM]: ( state, {payload} ) => {
|
||||
let { path, paramName, paramIn, value, isXml } = payload
|
||||
[UPDATE_RESOLVED_SUBTREE]: (state, action) => {
|
||||
const { value, path } = action.payload
|
||||
return state.setIn(["resolvedSubtrees", ...path], fromJSOrdered(value))
|
||||
},
|
||||
|
||||
return state.updateIn( [ "resolved", "paths", ...path, "parameters" ], fromJS([]), parameters => {
|
||||
const index = parameters.findIndex(p => p.get( "name" ) === paramName && p.get("in") === paramIn )
|
||||
if (!(value instanceof win.File)) {
|
||||
value = fromJSOrdered( value )
|
||||
}
|
||||
return parameters.setIn( [ index, isXml ? "value_xml" : "value" ], value)
|
||||
})
|
||||
[UPDATE_PARAM]: ( state, {payload} ) => {
|
||||
let { path: pathMethod, paramName, paramIn, value, isXml } = payload
|
||||
|
||||
const valueKey = isXml ? "value_xml" : "value"
|
||||
|
||||
return state.setIn(
|
||||
["meta", "paths", ...pathMethod, "parameters", `${paramName}.${paramIn}`, valueKey],
|
||||
value
|
||||
)
|
||||
},
|
||||
|
||||
[VALIDATE_PARAMS]: ( state, { payload: { pathMethod, isOAS3 } } ) => {
|
||||
let meta = state.getIn( [ "meta", "paths", ...pathMethod ], fromJS({}) )
|
||||
let isXml = /xml/i.test(meta.get("consumes_value"))
|
||||
|
||||
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
||||
return parameters.withMutations( parameters => {
|
||||
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
|
||||
let errors = validateParam(parameters.get(i), isXml, isOAS3)
|
||||
parameters.setIn([i, "errors"], fromJS(errors))
|
||||
}
|
||||
})
|
||||
const op = operationWithMeta(state, ...pathMethod)
|
||||
|
||||
return state.updateIn(["meta", "paths", ...pathMethod, "parameters"], fromJS({}), paramMeta => {
|
||||
return op.get("parameters", List()).reduce((res, param) => {
|
||||
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 } } ) => {
|
||||
return state.updateIn( [ "resolved", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
||||
return parameters.withMutations( parameters => {
|
||||
for ( let i = 0, len = parameters.count(); i < len; i++ ) {
|
||||
parameters.setIn([i, "errors"], fromJS([]))
|
||||
}
|
||||
})
|
||||
return state.updateIn( [ "meta", "paths", ...pathMethod, "parameters" ], fromJS([]), parameters => {
|
||||
return parameters.map(param => param.set("errors", fromJS([])))
|
||||
})
|
||||
},
|
||||
|
||||
@@ -109,10 +115,10 @@ export default {
|
||||
|
||||
[UPDATE_OPERATION_META_VALUE]: (state, { payload: { path, value, key } }) => {
|
||||
// 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]
|
||||
|
||||
if(!state.getIn(operationPath)) {
|
||||
if(!state.getIn(["json", ...operationPath]) && !state.getIn(["resolved", ...operationPath])) {
|
||||
// do nothing if the operation does not exist
|
||||
return state
|
||||
}
|
||||
|
||||
@@ -42,9 +42,18 @@ export const specResolved = createSelector(
|
||||
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 )
|
||||
export const spec = state => {
|
||||
let res = specResolved(state)
|
||||
let res = specJson(state)
|
||||
return res
|
||||
}
|
||||
|
||||
@@ -137,7 +146,9 @@ export const securityDefinitions = createSelector(
|
||||
|
||||
|
||||
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(
|
||||
@@ -261,10 +272,40 @@ export const allowTryItOutFor = () => {
|
||||
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
|
||||
export function getParameter(state, pathMethod, name, inType) {
|
||||
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 Map.isMap(p) && p.get("name") === name && p.get("in") === inType
|
||||
}) || Map() // Always return a map
|
||||
@@ -281,8 +322,9 @@ export const hasHost = createSelector(
|
||||
// Get the parameter values, that the user filled out
|
||||
export function parameterValues(state, pathMethod, isXml) {
|
||||
pathMethod = pathMethod || []
|
||||
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
|
||||
return params.reduce( (hash, p) => {
|
||||
// let paramValues = state.getIn(["meta", "paths", ...pathMethod, "parameters"], fromJS([]))
|
||||
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")
|
||||
return hash.set(`${p.get("in")}.${p.get("name")}`, value)
|
||||
}, fromJS({}))
|
||||
@@ -305,7 +347,7 @@ export function parametersIncludeType(parameters, typeValue="") {
|
||||
// Get the consumes/produces value that the user selected
|
||||
export function contentTypeValues(state, 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 producesValue = currentProducesFor(state, pathMethod)
|
||||
|
||||
@@ -327,14 +369,14 @@ export function contentTypeValues(state, pathMethod) {
|
||||
// Get the consumes/produces by path
|
||||
export function operationConsumes(state, 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
|
||||
export function currentProducesFor(state, pathMethod) {
|
||||
pathMethod = pathMethod || []
|
||||
|
||||
const operation = spec(state).getIn(["paths", ...pathMethod], null)
|
||||
const operation = specJsonWithResolvedSubtrees(state).getIn([ "paths", ...pathMethod], null)
|
||||
|
||||
if(operation === null) {
|
||||
// return nothing if the operation does not exist
|
||||
@@ -362,10 +404,10 @@ export const canExecuteScheme = ( state, path, method ) => {
|
||||
|
||||
export const validateBeforeExecute = ( state, pathMethod ) => {
|
||||
pathMethod = pathMethod || []
|
||||
let params = spec(state).getIn(["paths", ...pathMethod, "parameters"], fromJS([]))
|
||||
let paramValues = state.getIn(["meta", "paths", ...pathMethod, "parameters"], fromJS([]))
|
||||
let isValid = true
|
||||
|
||||
params.forEach( (p) => {
|
||||
paramValues.forEach( (p) => {
|
||||
let errors = p.get("errors")
|
||||
if ( errors && errors.count() ) {
|
||||
isValid = false
|
||||
|
||||
@@ -5,7 +5,7 @@ export const updateSpec = (ori, {specActions}) => (...args) => {
|
||||
|
||||
export const updateJsonSpec = (ori, {specActions}) => (...args) => {
|
||||
ori(...args)
|
||||
specActions.resolveSpec(...args)
|
||||
specActions.invalidateResolvedSubtreeCache()
|
||||
}
|
||||
|
||||
// Log the request ( just for debugging, shouldn't affect prod )
|
||||
@@ -16,4 +16,4 @@ export const executeRequest = (ori, { specActions }) => (req) => {
|
||||
|
||||
export const validateParams = (ori, { specSelectors }) => (req) => {
|
||||
return ori(req, specSelectors.isOAS3())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ module.exports = function({ configs }) {
|
||||
buildRequest: Swagger.buildRequest,
|
||||
execute: Swagger.execute,
|
||||
resolve: Swagger.resolve,
|
||||
resolveSubtree: Swagger.resolveSubtree,
|
||||
serializeRes: Swagger.serializeRes,
|
||||
opId: Swagger.helpers.opId
|
||||
}
|
||||
|
||||
+1
-1
@@ -445,7 +445,7 @@ export const validateDateTime = (val) => {
|
||||
|
||||
export const validateGuid = (val) => {
|
||||
val = val.toString().toLowerCase()
|
||||
if (!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}[)}]?$/.test(val)) {
|
||||
if (!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[)}]?$/.test(val)) {
|
||||
return "Value must be a Guid"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-rolling" style="background-image: none; background-position: initial initial; background-repeat: initial initial;"><circle cx="50" cy="50" fill="none" ng-attr-stroke="{{config.color}}" ng-attr-stroke-width="{{config.width}}" ng-attr-r="{{config.radius}}" ng-attr-stroke-dasharray="{{config.dasharray}}" stroke="#555555" stroke-width="10" r="35" stroke-dasharray="164.93361431346415 56.97787143782138"><animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform></circle></svg>
|
||||
|
After Width: | Height: | Size: 734 B |
@@ -586,6 +586,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
.opblock-body
|
||||
{
|
||||
.opblock-loading-animation
|
||||
{
|
||||
display: block;
|
||||
margin: 3em;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.opblock-body pre
|
||||
{
|
||||
|
||||
@@ -198,6 +198,7 @@ section.models
|
||||
.model-box
|
||||
{
|
||||
padding: 10px;
|
||||
display: inline-block;
|
||||
|
||||
border-radius: 4px;
|
||||
background: rgba($section-models-model-box-background-color,.1);
|
||||
|
||||
@@ -24,7 +24,8 @@ describe("<Models/>", function(){
|
||||
def1: {},
|
||||
def2: {}
|
||||
})
|
||||
}
|
||||
},
|
||||
specResolvedSubtree: () => {}
|
||||
},
|
||||
layoutSelectors: {
|
||||
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(){
|
||||
})
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ describe("spec plugin - selectors", function(){
|
||||
|
||||
// Given
|
||||
const spec = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
@@ -55,7 +55,7 @@ describe("spec plugin - selectors", function(){
|
||||
it("should return { requestContentType, responseContentType } from an operation", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
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(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
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(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {}
|
||||
@@ -151,7 +151,7 @@ describe("spec plugin - selectors", function(){
|
||||
it("should prioritize consumes value first from an operation", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
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(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
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(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
@@ -244,7 +244,7 @@ describe("spec plugin - selectors", function(){
|
||||
// Given
|
||||
let state = fromJS({
|
||||
url: "https://generator.swagger.io/api/swagger.json",
|
||||
resolved: {
|
||||
json: {
|
||||
paths: {
|
||||
"/one": {
|
||||
get: {
|
||||
|
||||
@@ -260,6 +260,9 @@ describe("utils", function() {
|
||||
expect(validateGuid("8ce4811e-cec5-4a29-891a-15d1917153c1")).toBeFalsy()
|
||||
expect(validateGuid("{8ce4811e-cec5-4a29-891a-15d1917153c1}")).toBeFalsy()
|
||||
expect(validateGuid("8CE4811E-CEC5-4A29-891A-15D1917153C1")).toBeFalsy()
|
||||
expect(validateGuid("6ffefd8e-a018-e811-bbf9-60f67727d806")).toBeFalsy()
|
||||
expect(validateGuid("6FFEFD8E-A018-E811-BBF9-60F67727D806")).toBeFalsy()
|
||||
expect(validateGuid("00000000-0000-0000-0000-000000000000")).toBeFalsy()
|
||||
})
|
||||
|
||||
it("returns a message for invalid input'", function() {
|
||||
|
||||
Reference in New Issue
Block a user