Skip to content

Commit 43304aa

Browse files
authored
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
1 parent cdbd120 commit 43304aa

File tree

8 files changed

+129
-6
lines changed

8 files changed

+129
-6
lines changed

src/core/components/param-body.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default class ParamBody extends PureComponent {
4747

4848
updateValues = (props) => {
4949
let { specSelectors, pathMethod, param, isExecute, consumesValue="" } = props
50-
let parameter = specSelectors ? specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) : fromJS({})
50+
let parameter = (specSelectors ? specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) : fromJS({})) || param
5151
let isXml = /xml/i.test(consumesValue)
5252
let isJson = /json/i.test(consumesValue)
5353
let paramValue = isXml ? parameter.get("value_xml") : parameter.get("value")

src/core/components/parameter-row.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default class ParameterRow extends Component {
3030
let { isOAS3 } = specSelectors
3131

3232
let example = param.get("example")
33-
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in"))
33+
let parameter = specSelectors.parameterWithMeta(pathMethod, param.get("name"), param.get("in")) || param
3434
let enumValue
3535

3636
if(isOAS3()) {
@@ -156,7 +156,7 @@ export default class ParameterRow extends Component {
156156
}
157157

158158
return (
159-
<tr>
159+
<tr className="parameters">
160160
<td className="col parameters-col_name">
161161
<div className={required ? "parameter__name required" : "parameter__name"}>
162162
{ param.get("name") }

src/core/json-schema-components.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,13 @@ export class JsonSchemaForm extends Component {
3434
static propTypes = JsonSchemaPropShape
3535
static defaultProps = JsonSchemaDefaultProps
3636

37+
componentDidMount() {
38+
const { dispatchInitialValue, value, onChange } = this.props
39+
if(dispatchInitialValue) {
40+
onChange(value)
41+
}
42+
}
43+
3744
render() {
3845
let { schema, errors, value, onChange, getComponent, fn } = this.props
3946

src/core/plugins/oas3/components/request-body.jsx

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import React from "react"
22
import PropTypes from "prop-types"
33
import ImPropTypes from "react-immutable-proptypes"
4-
import { OrderedMap } from "immutable"
4+
import { getSampleSchema } from "core/utils"
5+
import Im, { Map, OrderedMap, List } from "immutable"
56

67
const RequestBody = ({
78
requestBody,
9+
requestBodyValue,
810
getComponent,
911
getConfigs,
1012
specSelectors,
@@ -13,6 +15,10 @@ const RequestBody = ({
1315
specPath,
1416
onChange
1517
}) => {
18+
const handleFile = (e) => {
19+
onChange(e.target.files[0])
20+
}
21+
1622
const Markdown = getComponent("Markdown")
1723
const ModelExample = getComponent("modelExample")
1824
const RequestBodyEditor = getComponent("RequestBodyEditor")
@@ -23,10 +29,80 @@ const RequestBody = ({
2329

2430
const mediaTypeValue = requestBodyContent.get(contentType)
2531

32+
const isObjectContent = mediaTypeValue.getIn(["schema", "type"]) === "object"
33+
2634
if(!mediaTypeValue) {
2735
return null
2836
}
2937

38+
if(contentType === "application/octet-stream") {
39+
const Input = getComponent("Input")
40+
41+
if(!isExecute) {
42+
return <i>
43+
Example values are not available for <code>application/octet-stream</code> media types.
44+
</i>
45+
}
46+
47+
return <Input type={"file"} onChange={handleFile} />
48+
}
49+
50+
if(
51+
isObjectContent &&
52+
(contentType === "application/x-www-form-urlencoded"
53+
|| contentType.indexOf("multipart/") === 0))
54+
{
55+
const JsonSchemaForm = getComponent("JsonSchemaForm")
56+
const HighlightCode = getComponent("highlightCode")
57+
const bodyProperties = requestBody.getIn(["content", contentType, "schema", "properties"], OrderedMap())
58+
requestBodyValue = Map.isMap(requestBodyValue) ? requestBodyValue : OrderedMap()
59+
60+
return <div className="table-container">
61+
<table>
62+
<tbody>
63+
{
64+
bodyProperties.map((prop, key) => {
65+
const required = prop.get("required")
66+
const type = prop.get("type")
67+
const format = prop.get("format")
68+
69+
const isFile = type === "string" && (format === "binary" || format === "base64")
70+
71+
return <tr key={key} className="parameters">
72+
<td className="col parameters-col_name">
73+
<div className={required ? "parameter__name required" : "parameter__name"}>
74+
{ key }
75+
{ !required ? null : <span style={{color: "red"}}>&nbsp;*</span> }
76+
</div>
77+
<div className="parameter__type">
78+
{ type }
79+
{ format && <span className="prop-format">(${format})</span>}
80+
</div>
81+
<div className="parameter__deprecated">
82+
{ prop.get("deprecated") ? "deprecated": null }
83+
</div>
84+
</td>
85+
<td className="col parameters-col_description">
86+
{isExecute ?
87+
<JsonSchemaForm
88+
dispatchInitialValue={!isFile}
89+
schema={prop}
90+
getComponent={getComponent}
91+
value={requestBodyValue.get(key) || getSampleSchema(prop)}
92+
onChange={(value) => {
93+
onChange(value, [key])
94+
}}
95+
/>
96+
: <HighlightCode className="example" value={ getSampleSchema(prop) } />}
97+
</td>
98+
</tr>
99+
})
100+
}
101+
</tbody>
102+
</table>
103+
</div>
104+
}
105+
30106
return <div>
31107
{ requestBodyDescription &&
32108
<Markdown source={requestBodyDescription} />
@@ -53,6 +129,7 @@ const RequestBody = ({
53129

54130
RequestBody.propTypes = {
55131
requestBody: ImPropTypes.orderedMap.isRequired,
132+
requestBodyValue: ImPropTypes.orderedMap.isRequired,
56133
getComponent: PropTypes.func.isRequired,
57134
getConfigs: PropTypes.func.isRequired,
58135
specSelectors: PropTypes.object.isRequired,

src/core/plugins/oas3/wrap-components/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import parameters from "./parameters"
44
import VersionStamp from "./version-stamp"
55
import OnlineValidatorBadge from "./online-validator-badge"
66
import Model from "./model"
7+
import JsonSchema_string from "./json-schema-string"
78

89
export default {
910
Markdown,
1011
AuthItem,
1112
parameters,
13+
JsonSchema_string,
1214
VersionStamp,
1315
model: Model,
1416
onlineValidatorBadge: OnlineValidatorBadge,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import React from "react"
2+
import { OAS3ComponentWrapFactory } from "../helpers"
3+
4+
export default OAS3ComponentWrapFactory(({ Ori, ...props }) => {
5+
const {
6+
schema,
7+
getComponent,
8+
errors,
9+
onChange
10+
} = props
11+
12+
const { type, format } = schema
13+
const Input = getComponent("Input")
14+
15+
if(type === "string" && (format === "binary" || format === "base64")) {
16+
return <Input type="file"
17+
className={ errors.length ? "invalid" : ""}
18+
title={ errors.length ? errors : ""}
19+
onChange={(e) => {
20+
onChange(e.target.files[0])
21+
}}
22+
disabled={Ori.isDisabled}/>
23+
} else {
24+
return <Ori {...props} />
25+
}
26+
})

src/core/plugins/oas3/wrap-components/parameters.jsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,17 @@ class Parameters extends Component {
185185
<RequestBody
186186
specPath={requestBodySpecPath}
187187
requestBody={requestBody}
188+
requestBodyValue={oas3Selectors.requestBodyValue(...pathMethod) || Map()}
188189
isExecute={isExecute}
189-
onChange={(value) => {
190+
onChange={(value, path) => {
191+
if(path) {
192+
const lastValue = oas3Selectors.requestBodyValue(...pathMethod)
193+
const usableValue = Map.isMap(lastValue) ? lastValue : Map()
194+
return oas3Actions.setRequestBodyValue({
195+
pathMethod,
196+
value: usableValue.setIn(path, value)
197+
})
198+
}
190199
oas3Actions.setRequestBodyValue({ value, pathMethod })
191200
}}
192201
contentType={oas3Selectors.requestContentType(...pathMethod)}/>

src/core/plugins/spec/actions.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,9 @@ export const executeRequest = (req) =>
348348

349349
if(isJSONObject(requestBody)) {
350350
req.requestBody = JSON.parse(requestBody)
351-
} else {
351+
} else if(requestBody && requestBody.toJS) {
352+
req.requestBody = requestBody.toJS()
353+
} else{
352354
req.requestBody = requestBody
353355
}
354356
}

0 commit comments

Comments
 (0)