feat: OAS3 binary media type support (#4592)
* fix(validator-badge): resolve definition URLs against browser location * use param as meta parameter if not found * convert request body from Immutable if necessary * show file upload for `format: binary` and `format: base64` jsonschema strings * add `dispatchInitialValue` prop to JsonSchemaForm * add optional subkey parameter to onChange * add binary media type support to request body
This commit is contained in:
@@ -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.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) : fromJS({})
|
let parameter = (specSelectors ? specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) : fromJS({})) || param
|
||||||
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")
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default class ParameterRow extends Component {
|
|||||||
let { isOAS3 } = specSelectors
|
let { isOAS3 } = specSelectors
|
||||||
|
|
||||||
let example = param.get("example")
|
let example = param.get("example")
|
||||||
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
|
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) || param
|
||||||
let enumValue
|
let enumValue
|
||||||
|
|
||||||
if(isOAS3()) {
|
if(isOAS3()) {
|
||||||
@@ -156,7 +156,7 @@ export default class ParameterRow extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr>
|
<tr className="parameters">
|
||||||
<td className="col parameters-col_name">
|
<td className="col parameters-col_name">
|
||||||
<div className={required ? "parameter__name required" : "parameter__name"}>
|
<div className={required ? "parameter__name required" : "parameter__name"}>
|
||||||
{ param.get("name") }
|
{ param.get("name") }
|
||||||
|
|||||||
@@ -34,6 +34,13 @@ export class JsonSchemaForm extends Component {
|
|||||||
static propTypes = JsonSchemaPropShape
|
static propTypes = JsonSchemaPropShape
|
||||||
static defaultProps = JsonSchemaDefaultProps
|
static defaultProps = JsonSchemaDefaultProps
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { dispatchInitialValue, value, onChange } = this.props
|
||||||
|
if(dispatchInitialValue) {
|
||||||
|
onChange(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let { schema, errors, value, onChange, getComponent, fn } = this.props
|
let { schema, errors, value, onChange, getComponent, fn } = this.props
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import PropTypes from "prop-types"
|
import PropTypes from "prop-types"
|
||||||
import ImPropTypes from "react-immutable-proptypes"
|
import ImPropTypes from "react-immutable-proptypes"
|
||||||
import { OrderedMap } from "immutable"
|
import { getSampleSchema } from "core/utils"
|
||||||
|
import Im, { Map, OrderedMap, List } from "immutable"
|
||||||
|
|
||||||
const RequestBody = ({
|
const RequestBody = ({
|
||||||
requestBody,
|
requestBody,
|
||||||
|
requestBodyValue,
|
||||||
getComponent,
|
getComponent,
|
||||||
getConfigs,
|
getConfigs,
|
||||||
specSelectors,
|
specSelectors,
|
||||||
@@ -13,6 +15,10 @@ const RequestBody = ({
|
|||||||
specPath,
|
specPath,
|
||||||
onChange
|
onChange
|
||||||
}) => {
|
}) => {
|
||||||
|
const handleFile = (e) => {
|
||||||
|
onChange(e.target.files[0])
|
||||||
|
}
|
||||||
|
|
||||||
const Markdown = getComponent("Markdown")
|
const Markdown = getComponent("Markdown")
|
||||||
const ModelExample = getComponent("modelExample")
|
const ModelExample = getComponent("modelExample")
|
||||||
const RequestBodyEditor = getComponent("RequestBodyEditor")
|
const RequestBodyEditor = getComponent("RequestBodyEditor")
|
||||||
@@ -23,10 +29,80 @@ const RequestBody = ({
|
|||||||
|
|
||||||
const mediaTypeValue = requestBodyContent.get(contentType)
|
const mediaTypeValue = requestBodyContent.get(contentType)
|
||||||
|
|
||||||
|
const isObjectContent = mediaTypeValue.getIn(["schema", "type"]) === "object"
|
||||||
|
|
||||||
if(!mediaTypeValue) {
|
if(!mediaTypeValue) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(contentType === "application/octet-stream") {
|
||||||
|
const Input = getComponent("Input")
|
||||||
|
|
||||||
|
if(!isExecute) {
|
||||||
|
return <i>
|
||||||
|
Example values are not available for <code>application/octet-stream</code> media types.
|
||||||
|
</i>
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Input type={"file"} onChange={handleFile} />
|
||||||
|
}
|
||||||
|
|
||||||
|
if(
|
||||||
|
isObjectContent &&
|
||||||
|
(contentType === "application/x-www-form-urlencoded"
|
||||||
|
|| contentType.indexOf("multipart/") === 0))
|
||||||
|
{
|
||||||
|
const JsonSchemaForm = getComponent("JsonSchemaForm")
|
||||||
|
const HighlightCode = getComponent("highlightCode")
|
||||||
|
const bodyProperties = requestBody.getIn(["content", contentType, "schema", "properties"], OrderedMap())
|
||||||
|
requestBodyValue = Map.isMap(requestBodyValue) ? requestBodyValue : OrderedMap()
|
||||||
|
|
||||||
|
return <div className="table-container">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{
|
||||||
|
bodyProperties.map((prop, key) => {
|
||||||
|
const required = prop.get("required")
|
||||||
|
const type = prop.get("type")
|
||||||
|
const format = prop.get("format")
|
||||||
|
|
||||||
|
const isFile = type === "string" && (format === "binary" || format === "base64")
|
||||||
|
|
||||||
|
return <tr key={key} className="parameters">
|
||||||
|
<td className="col parameters-col_name">
|
||||||
|
<div className={required ? "parameter__name required" : "parameter__name"}>
|
||||||
|
{ key }
|
||||||
|
{ !required ? null : <span style={{color: "red"}}> *</span> }
|
||||||
|
</div>
|
||||||
|
<div className="parameter__type">
|
||||||
|
{ type }
|
||||||
|
{ format && <span className="prop-format">(${format})</span>}
|
||||||
|
</div>
|
||||||
|
<div className="parameter__deprecated">
|
||||||
|
{ prop.get("deprecated") ? "deprecated": null }
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td className="col parameters-col_description">
|
||||||
|
{isExecute ?
|
||||||
|
<JsonSchemaForm
|
||||||
|
dispatchInitialValue={!isFile}
|
||||||
|
schema={prop}
|
||||||
|
getComponent={getComponent}
|
||||||
|
value={requestBodyValue.get(key) || getSampleSchema(prop)}
|
||||||
|
onChange={(value) => {
|
||||||
|
onChange(value, [key])
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
: <HighlightCode className="example" value={ getSampleSchema(prop) } />}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
{ requestBodyDescription &&
|
{ requestBodyDescription &&
|
||||||
<Markdown source={requestBodyDescription} />
|
<Markdown source={requestBodyDescription} />
|
||||||
@@ -53,6 +129,7 @@ const RequestBody = ({
|
|||||||
|
|
||||||
RequestBody.propTypes = {
|
RequestBody.propTypes = {
|
||||||
requestBody: ImPropTypes.orderedMap.isRequired,
|
requestBody: ImPropTypes.orderedMap.isRequired,
|
||||||
|
requestBodyValue: ImPropTypes.orderedMap.isRequired,
|
||||||
getComponent: PropTypes.func.isRequired,
|
getComponent: PropTypes.func.isRequired,
|
||||||
getConfigs: PropTypes.func.isRequired,
|
getConfigs: PropTypes.func.isRequired,
|
||||||
specSelectors: PropTypes.object.isRequired,
|
specSelectors: PropTypes.object.isRequired,
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import parameters from "./parameters"
|
|||||||
import VersionStamp from "./version-stamp"
|
import VersionStamp from "./version-stamp"
|
||||||
import OnlineValidatorBadge from "./online-validator-badge"
|
import OnlineValidatorBadge from "./online-validator-badge"
|
||||||
import Model from "./model"
|
import Model from "./model"
|
||||||
|
import JsonSchema_string from "./json-schema-string"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
Markdown,
|
Markdown,
|
||||||
AuthItem,
|
AuthItem,
|
||||||
parameters,
|
parameters,
|
||||||
|
JsonSchema_string,
|
||||||
VersionStamp,
|
VersionStamp,
|
||||||
model: Model,
|
model: Model,
|
||||||
onlineValidatorBadge: OnlineValidatorBadge,
|
onlineValidatorBadge: OnlineValidatorBadge,
|
||||||
|
|||||||
26
src/core/plugins/oas3/wrap-components/json-schema-string.js
Normal file
26
src/core/plugins/oas3/wrap-components/json-schema-string.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import React from "react"
|
||||||
|
import { OAS3ComponentWrapFactory } from "../helpers"
|
||||||
|
|
||||||
|
export default OAS3ComponentWrapFactory(({ Ori, ...props }) => {
|
||||||
|
const {
|
||||||
|
schema,
|
||||||
|
getComponent,
|
||||||
|
errors,
|
||||||
|
onChange
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const { type, format } = schema
|
||||||
|
const Input = getComponent("Input")
|
||||||
|
|
||||||
|
if(type === "string" && (format === "binary" || format === "base64")) {
|
||||||
|
return <Input type="file"
|
||||||
|
className={ errors.length ? "invalid" : ""}
|
||||||
|
title={ errors.length ? errors : ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
onChange(e.target.files[0])
|
||||||
|
}}
|
||||||
|
disabled={Ori.isDisabled}/>
|
||||||
|
} else {
|
||||||
|
return <Ori {...props} />
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -185,8 +185,17 @@ class Parameters extends Component {
|
|||||||
<RequestBody
|
<RequestBody
|
||||||
specPath={requestBodySpecPath}
|
specPath={requestBodySpecPath}
|
||||||
requestBody={requestBody}
|
requestBody={requestBody}
|
||||||
|
requestBodyValue={oas3Selectors.requestBodyValue(...pathMethod) || Map()}
|
||||||
isExecute={isExecute}
|
isExecute={isExecute}
|
||||||
onChange={(value) => {
|
onChange={(value, path) => {
|
||||||
|
if(path) {
|
||||||
|
const lastValue = oas3Selectors.requestBodyValue(...pathMethod)
|
||||||
|
const usableValue = Map.isMap(lastValue) ? lastValue : Map()
|
||||||
|
return oas3Actions.setRequestBodyValue({
|
||||||
|
pathMethod,
|
||||||
|
value: usableValue.setIn(path, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
oas3Actions.setRequestBodyValue({ value, pathMethod })
|
oas3Actions.setRequestBodyValue({ value, pathMethod })
|
||||||
}}
|
}}
|
||||||
contentType={oas3Selectors.requestContentType(...pathMethod)}/>
|
contentType={oas3Selectors.requestContentType(...pathMethod)}/>
|
||||||
|
|||||||
@@ -348,6 +348,8 @@ export const executeRequest = (req) =>
|
|||||||
|
|
||||||
if(isJSONObject(requestBody)) {
|
if(isJSONObject(requestBody)) {
|
||||||
req.requestBody = JSON.parse(requestBody)
|
req.requestBody = JSON.parse(requestBody)
|
||||||
|
} else if(requestBody && requestBody.toJS) {
|
||||||
|
req.requestBody = requestBody.toJS()
|
||||||
} else{
|
} else{
|
||||||
req.requestBody = requestBody
|
req.requestBody = requestBody
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user