feature: support for Parameter.content (#5571)

* add `getParameterSchema` OAS helper

* use `Parameter.content.[firstKey].schema` as schema value when present

* `newValue` -> `initialValue`

* make `paramWithMeta` a const

* add trailing comma to `swagger2SchemaKeys`

* refactor `helpers` to a folder

* deprecate `src/core/utils.js` in favor of `src/core/helpers/`

* support `Parameter.content.[mediaType].schema` in validateParam

* reject `null` as an OAS3 object value

* expose Fetch errors in the browser console

* generate ParameterRow default values based on `content` values

* add tests for `getParameterSchema`

* remove debugger statement

* remove debugger statement

* don't apply `generatedSampleValue`s to parameters with `examples`

* remove extra semi

* disable JSON check in parameter runtime validation

* stringify JsonSchema_object textarea values

* add Cypress tests

* swagger-client@3.9.4
This commit is contained in:
kyle
2019-08-31 16:37:43 -07:00
committed by GitHub
parent 24c6473990
commit c9c3b2338e
11 changed files with 400 additions and 52 deletions

View File

@@ -3,7 +3,8 @@ import { Map, List } from "immutable"
import PropTypes from "prop-types"
import ImPropTypes from "react-immutable-proptypes"
import win from "core/window"
import { getExtensions, getCommonExtensions, numberToString, stringify } from "core/utils"
import { getSampleSchema, getExtensions, getCommonExtensions, numberToString, stringify } from "core/utils"
import getParameterSchema from "../../helpers/get-parameter-schema.js"
export default class ParameterRow extends Component {
static propTypes = {
@@ -40,7 +41,7 @@ export default class ParameterRow extends Component {
let enumValue
if(isOAS3) {
let schema = parameterWithMeta.get("schema") || Map()
let schema = getParameterSchema(parameterWithMeta, { isOAS3 })
enumValue = schema.get("enum")
} else {
enumValue = parameterWithMeta ? parameterWithMeta.get("enum") : undefined
@@ -95,30 +96,68 @@ export default class ParameterRow extends Component {
setDefaultValue = () => {
let { specSelectors, pathMethod, rawParam, oas3Selectors } = this.props
let paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || Map()
const paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || Map()
const schema = getParameterSchema(paramWithMeta, { isOAS3: specSelectors.isOAS3() })
const parameterMediaType = paramWithMeta
.get("content", Map())
.keySeq()
.first()
const generatedSampleValue = getSampleSchema(schema.toJS(), parameterMediaType, {
includeWriteOnly: true
})
if (!paramWithMeta || paramWithMeta.get("value") !== undefined) {
return
}
if( paramWithMeta.get("in") !== "body" ) {
let newValue
let initialValue
//// Find an initial value
if (specSelectors.isSwagger2()) {
newValue = paramWithMeta.get("x-example")
|| paramWithMeta.getIn(["default"])
initialValue = paramWithMeta.get("x-example")
|| paramWithMeta.getIn(["schema", "example"])
|| paramWithMeta.getIn(["schema", "default"])
|| schema.getIn(["default"])
} else if (specSelectors.isOAS3()) {
const currentExampleKey = oas3Selectors.activeExamplesMember(...pathMethod, "parameters", this.getParamKey())
newValue = paramWithMeta.getIn(["examples", currentExampleKey, "value"])
initialValue = paramWithMeta.getIn(["examples", currentExampleKey, "value"])
|| paramWithMeta.getIn(["content", parameterMediaType, "example"])
|| paramWithMeta.get("example")
|| paramWithMeta.getIn(["schema", "example"])
|| paramWithMeta.getIn(["schema", "default"])
|| schema.get("example")
|| schema.get("default")
}
if(newValue !== undefined) {
//// Process the initial value
if(initialValue !== undefined && !List.isList(initialValue)) {
// Stringify if it isn't a List
initialValue = stringify(initialValue)
}
//// Dispatch the initial value
if(initialValue !== undefined) {
this.onChangeWrapper(initialValue)
} else if(
schema.get("type") === "object"
&& generatedSampleValue
&& !paramWithMeta.get("examples")
) {
// Object parameters get special treatment.. if the user doesn't set any
// default or example values, we'll provide initial values generated from
// the schema.
// However, if `examples` exist for the parameter, we won't do anything,
// so that the appropriate `examples` logic can take over.
this.onChangeWrapper(
List.isList(newValue) ? newValue : stringify(newValue)
List.isList(generatedSampleValue) ? (
generatedSampleValue
) : (
stringify(generatedSampleValue)
)
)
}
}
@@ -171,7 +210,7 @@ export default class ParameterRow extends Component {
let paramWithMeta = specSelectors.parameterWithMetaByIdentity(pathMethod, rawParam) || Map()
let format = param.get("format")
let schema = isOAS3 ? param.get("schema") : param
let schema = getParameterSchema(param, { isOAS3 })
let type = schema.get("type")
let isFormData = inType === "formData"
let isFormDataSupported = "FormData" in win
@@ -285,7 +324,7 @@ export default class ParameterRow extends Component {
getConfigs={ getConfigs }
isExecute={ isExecute }
specSelectors={ specSelectors }
schema={ param.get("schema") }
schema={ schema }
example={ bodyParam }/>
: null
}

View File

@@ -4,6 +4,7 @@ import { List, fromJS } from "immutable"
import cx from "classnames"
import ImPropTypes from "react-immutable-proptypes"
import DebounceInput from "react-debounce-input"
import { stringify } from "core/utils"
//import "less/json-schema-form"
const noop = ()=> {}
@@ -269,7 +270,7 @@ export class JsonSchema_object extends PureComponent {
<TextArea
className={cx({ invalid: errors.size })}
title={ errors.size ? errors.join(", ") : ""}
value={value}
value={stringify(value)}
disabled={disabled}
onChange={ this.handleOnChange }/>
</div>

View File

@@ -436,9 +436,12 @@ export const executeRequest = (req) =>
specActions.setResponse(req.pathName, req.method, res)
} )
.catch(
err => specActions.setResponse(req.pathName, req.method, {
error: true, err: serializeError(err)
})
err => {
console.error(err)
specActions.setResponse(req.pathName, req.method, {
error: true, err: serializeError(err)
})
}
)
}

View File

@@ -1,3 +1,15 @@
/*
ATTENTION! This file (but not the functions within) is deprecated.
You should probably add a new file to `./helpers/` instead of adding a new
function here.
One-function-per-file is a better pattern than what we have here.
If you're refactoring something in here, feel free to break it out to a file
in `./helpers` if you have the time.
*/
import Im from "immutable"
import { sanitizeUrl as braintreeSanitizeUrl } from "@braintree/sanitize-url"
import camelCase from "lodash/camelCase"
@@ -9,6 +21,7 @@ import eq from "lodash/eq"
import { memoizedSampleFromSchema, memoizedCreateXMLExample } from "core/plugins/samples/fn"
import win from "./window"
import cssEscape from "css.escape"
import getParameterSchema from "../helpers/get-parameter-schema"
const DEFAULT_RESPONSE_KEY = "default"
@@ -488,7 +501,7 @@ export const validateParam = (param, value, { isOAS3 = false, bypassRequiredChec
let errors = []
let required = param.get("required")
let paramDetails = isOAS3 ? param.get("schema") : param
let paramDetails = getParameterSchema(param, { isOAS3 })
if(!paramDetails) return errors
@@ -517,18 +530,23 @@ export const validateParam = (param, value, { isOAS3 = false, bypassRequiredChec
let oas3ObjectCheck = false
if(false || isOAS3 && type === "object") {
if(typeof value === "object") {
if(isOAS3 && type === "object") {
if(typeof value === "object" && value !== null) {
oas3ObjectCheck = true
} else if(typeof value === "string") {
try {
JSON.parse(value)
oas3ObjectCheck = true
} catch(e) {
errors.push("Parameter string value must be valid JSON")
return errors
}
oas3ObjectCheck = true
}
// Disabled because `validateParam` doesn't consider the MediaType of the
// `Parameter.content` hint correctly.
// } else if(typeof value === "string") {
// try {
// JSON.parse(value)
// oas3ObjectCheck = true
// } catch(e) {
// errors.push("Parameter string value must be valid JSON")
// return errors
// }
// }
}
const allChecks = [

View File

@@ -0,0 +1,67 @@
/**
* @prettier
*/
import Im from "immutable"
const swagger2SchemaKeys = Im.Set.of(
"type",
"format",
"items",
"default",
"maximum",
"exclusiveMaximum",
"minimum",
"exclusiveMinimum",
"maxLength",
"minLength",
"pattern",
"maxItems",
"minItems",
"uniqueItems",
"enum",
"multipleOf"
)
/**
* Get the effective schema value for a parameter, or an empty Immutable.Map if
* no suitable schema can be found.
*
* Supports OpenAPI 3.0 `Parameter.content` priority -- since a Parameter Object
* cannot have both `schema` and `content`, this function ignores `schema` when
* `content` is present.
*
* @param {Immutable.Map} parameter The parameter to identify a schema for
* @param {object} config
* @param {boolean} config.isOAS3 Whether the parameter is from an OpenAPI 2.0
* or OpenAPI 3.0 definition
* @return {Immutable.Map} The desired schema
*/
export default function getParameterSchema(parameter, { isOAS3 } = {}) {
// Return empty Map if `parameter` isn't a Map
if (!Im.Map.isMap(parameter)) return Im.Map()
if (!isOAS3) {
// Swagger 2.0
if (parameter.get("in") === "body") {
return parameter.get("schema", Im.Map())
} else {
return parameter.filter((v, k) => swagger2SchemaKeys.includes(k))
}
}
// If we've reached here, the parameter is OpenAPI 3.0
if (parameter.get("content")) {
const parameterContentMediaTypes = parameter
.get("content", Im.Map({}))
.keySeq()
return parameter.getIn(
["content", parameterContentMediaTypes.first(), "schema"],
Im.Map()
)
}
return parameter.get("schema", Im.Map())
}