fix: align OpenAPI 3.x.y file uploads with specification (#10409)
Refs #9278
This commit is contained in:
@@ -87,6 +87,13 @@ const defaultOptions = Object.freeze({
|
||||
onComplete: null,
|
||||
modelPropertyMacro: null,
|
||||
parameterMacro: null,
|
||||
|
||||
fileUploadMediaTypes: [
|
||||
"application/octet-stream",
|
||||
"image/",
|
||||
"audio/",
|
||||
"video/",
|
||||
],
|
||||
})
|
||||
|
||||
export default defaultOptions
|
||||
|
||||
@@ -45,6 +45,10 @@ const mappings = {
|
||||
docExpansion: { typeCaster: stringTypeCaster },
|
||||
dom_id: { typeCaster: nullableStringTypeCaster },
|
||||
domNode: { typeCaster: domNodeTypeCaster },
|
||||
fileUploadMediaTypes: {
|
||||
typeCaster: arrayTypeCaster,
|
||||
defaultValue: defaultOptions.fileUploadMediaTypes,
|
||||
},
|
||||
filter: { typeCaster: filterTypeCaster },
|
||||
fn: { typeCaster: objectTypeCaster },
|
||||
initialState: { typeCaster: objectTypeCaster },
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import { List, Map } from "immutable"
|
||||
|
||||
export const upperFirst = (value) => {
|
||||
if (typeof value === "string") {
|
||||
return `${value.charAt(0).toUpperCase()}${value.slice(1)}`
|
||||
@@ -504,3 +506,22 @@ export const makeGetExtensionKeywords = (fnAccessor) => {
|
||||
|
||||
return getExtensionKeywords
|
||||
}
|
||||
|
||||
export const hasSchemaType = (schema, type) => {
|
||||
const isSchemaImmutable = Map.isMap(schema)
|
||||
|
||||
if (!isSchemaImmutable && !isPlainObject(schema)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const hasType = (schemaType) =>
|
||||
type === schemaType || (Array.isArray(type) && type.includes(schemaType))
|
||||
|
||||
const schemaType = isSchemaImmutable ? schema.get("type") : schema.type
|
||||
|
||||
if (List.isList(schemaType) || Array.isArray(schemaType)) {
|
||||
return schemaType.some((t) => hasType(t))
|
||||
}
|
||||
|
||||
return hasType(schemaType)
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
isBooleanJSONSchema,
|
||||
getSchemaKeywords,
|
||||
makeGetExtensionKeywords,
|
||||
hasSchemaType,
|
||||
} from "./fn"
|
||||
import { JSONSchemaPathContext, JSONSchemaLevelContext } from "./context"
|
||||
import {
|
||||
@@ -143,6 +144,7 @@ const JSONSchema202012Plugin = ({ getSystem, fn }) => {
|
||||
useLevel,
|
||||
getSchemaKeywords,
|
||||
getExtensionKeywords: makeGetExtensionKeywords(fnAccessor),
|
||||
hasSchemaType,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ export class JsonSchemaForm extends Component {
|
||||
const format = schema && schema.get ? schema.get("format") : null
|
||||
const type = schema && schema.get ? schema.get("type") : null
|
||||
const foldedType = fn.jsonSchema202012.foldType(immutableToJS(type))
|
||||
const isFileUploadIntended = fn.isFileUploadIntended(schema)
|
||||
|
||||
let getComponentSilently = (name) => getComponent(name, false, { failSilently: true })
|
||||
let Comp = type ? format ?
|
||||
@@ -58,7 +59,7 @@ export class JsonSchemaForm extends Component {
|
||||
getComponentSilently(`JsonSchema_${type}`) :
|
||||
getComponent("JsonSchema_string")
|
||||
|
||||
if (List.isList(type) && (foldedType === "array" || foldedType === "object")) {
|
||||
if (!isFileUploadIntended && List.isList(type) && (foldedType === "array" || foldedType === "object")) {
|
||||
Comp = getComponent("JsonSchema_object")
|
||||
}
|
||||
|
||||
|
||||
19
src/core/plugins/json-schema-5/fn.js
Normal file
19
src/core/plugins/json-schema-5/fn.js
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import { Map } from "immutable"
|
||||
import isPlainObject from "lodash/isPlainObject"
|
||||
|
||||
export const hasSchemaType = (schema, type) => {
|
||||
const isSchemaImmutable = Map.isMap(schema)
|
||||
|
||||
if (!isSchemaImmutable && !isPlainObject(schema)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const schemaType = isSchemaImmutable ? schema.get("type") : schema.type
|
||||
|
||||
return (
|
||||
type === schemaType || (Array.isArray(type) && type.includes(schemaType))
|
||||
)
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import Schemes from "./components/schemes"
|
||||
import SchemesContainer from "./containers/schemes"
|
||||
import * as JSONSchemaComponents from "./components/json-schema-components"
|
||||
import { ModelExtensions } from "./components/model-extensions"
|
||||
import { hasSchemaType } from "./fn"
|
||||
|
||||
const JSONSchema5Plugin = () => ({
|
||||
components: {
|
||||
@@ -31,6 +32,9 @@ const JSONSchema5Plugin = () => ({
|
||||
SchemesContainer,
|
||||
...JSONSchemaComponents,
|
||||
},
|
||||
fn: {
|
||||
hasSchemaType,
|
||||
},
|
||||
})
|
||||
|
||||
export default JSONSchema5Plugin
|
||||
|
||||
@@ -105,21 +105,13 @@ const RequestBody = ({
|
||||
}
|
||||
requestBodyErrors = List.isList(requestBodyErrors) ? requestBodyErrors : List()
|
||||
|
||||
if(!mediaTypeValue.size) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isObjectContent = mediaTypeValue.getIn(["schema", "type"]) === "object"
|
||||
const isBinaryFormat = mediaTypeValue.getIn(["schema", "format"]) === "binary"
|
||||
const isBase64Format = mediaTypeValue.getIn(["schema", "format"]) === "base64"
|
||||
const isFileUploadIntended = fn.isFileUploadIntended(
|
||||
mediaTypeValue?.get("schema"),
|
||||
contentType
|
||||
)
|
||||
|
||||
if(
|
||||
contentType === "application/octet-stream"
|
||||
|| contentType.indexOf("image/") === 0
|
||||
|| contentType.indexOf("audio/") === 0
|
||||
|| contentType.indexOf("video/") === 0
|
||||
|| isBinaryFormat
|
||||
|| isBase64Format
|
||||
isFileUploadIntended
|
||||
) {
|
||||
const Input = getComponent("Input")
|
||||
|
||||
@@ -132,6 +124,13 @@ const RequestBody = ({
|
||||
return <Input type={"file"} onChange={handleFile} />
|
||||
}
|
||||
|
||||
|
||||
if (!mediaTypeValue.size) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isObjectContent = fn.hasSchemaType(mediaTypeValue.get("schema"), "object")
|
||||
|
||||
if (
|
||||
isObjectContent &&
|
||||
(
|
||||
@@ -190,11 +189,11 @@ const RequestBody = ({
|
||||
initialValue = JSON.parse(initialValue)
|
||||
}
|
||||
|
||||
const isFile = type === "string" && (format === "binary" || format === "base64")
|
||||
const isFileUploadIntended = fn.isFileUploadIntended(schema)
|
||||
|
||||
const jsonSchemaForm = <JsonSchemaForm
|
||||
fn={fn}
|
||||
dispatchInitialValue={!isFile}
|
||||
dispatchInitialValue={!isFileUploadIntended}
|
||||
schema={schema}
|
||||
description={key}
|
||||
getComponent={getComponent}
|
||||
|
||||
35
src/core/plugins/oas3/fn.js
Normal file
35
src/core/plugins/oas3/fn.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
import { Map } from "immutable"
|
||||
import isPlainObject from "lodash/isPlainObject"
|
||||
|
||||
export const makeIsFileUploadIntended = (getSystem) => {
|
||||
const isFileUploadIntended = (schema, mediaType = null) => {
|
||||
const { getConfigs, fn } = getSystem()
|
||||
const { fileUploadMediaTypes } = getConfigs()
|
||||
const isFileUploadMediaType =
|
||||
typeof mediaType === "string" &&
|
||||
fileUploadMediaTypes.some((fileUploadMediaType) =>
|
||||
mediaType.startsWith(fileUploadMediaType)
|
||||
)
|
||||
|
||||
if (isFileUploadMediaType) {
|
||||
return true
|
||||
}
|
||||
|
||||
const isSchemaImmutable = Map.isMap(schema)
|
||||
|
||||
if (!isSchemaImmutable && !isPlainObject(schema)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const format = isSchemaImmutable ? schema.get("format") : schema.format
|
||||
|
||||
return (
|
||||
fn.hasSchemaType(schema, "string") && ["binary", "byte"].includes(format)
|
||||
)
|
||||
}
|
||||
|
||||
return isFileUploadIntended
|
||||
}
|
||||
@@ -9,8 +9,11 @@ import wrapComponents from "./wrap-components"
|
||||
import * as actions from "./actions"
|
||||
import * as selectors from "./selectors"
|
||||
import reducers from "./reducers"
|
||||
import { makeIsFileUploadIntended } from "./fn"
|
||||
|
||||
export default function ({ getSystem }) {
|
||||
const isFileUploadIntended = makeIsFileUploadIntended(getSystem)
|
||||
|
||||
export default function () {
|
||||
return {
|
||||
components,
|
||||
wrapComponents,
|
||||
@@ -28,5 +31,9 @@ export default function () {
|
||||
selectors: { ...selectors },
|
||||
},
|
||||
},
|
||||
fn: {
|
||||
isFileUploadIntended,
|
||||
isFileUploadIntendedOAS30: isFileUploadIntended,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@ export default OAS3ComponentWrapFactory(({ Ori, ...props }) => {
|
||||
schema,
|
||||
getComponent,
|
||||
errors,
|
||||
onChange
|
||||
onChange,
|
||||
fn
|
||||
} = props
|
||||
|
||||
const format = schema && schema.get ? schema.get("format") : null
|
||||
const type = schema && schema.get ? schema.get("type") : null
|
||||
const isFileUploadIntended = fn.isFileUploadIntended(schema)
|
||||
const Input = getComponent("Input")
|
||||
|
||||
if(type && type === "string" && (format && (format === "binary" || format === "base64"))) {
|
||||
if (isFileUploadIntended) {
|
||||
return <Input type="file"
|
||||
className={ errors.length ? "invalid" : ""}
|
||||
title={ errors.length ? errors : ""}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
getProperties,
|
||||
} from "./json-schema-2020-12-extensions/fn"
|
||||
import { wrapOAS31Fn } from "./fn"
|
||||
import { makeIsFileUploadIntended } from "./oas3-extensions/fn"
|
||||
|
||||
function afterLoad({ fn, getSystem }) {
|
||||
// overrides for fn.jsonSchema202012
|
||||
@@ -38,6 +39,29 @@ function afterLoad({ fn, getSystem }) {
|
||||
|
||||
Object.assign(this.fn, wrappedFns)
|
||||
}
|
||||
|
||||
// overrides behavior in OpenAPI 3.1.x, recognizes more intentions
|
||||
const isFileUploadIntended = makeIsFileUploadIntended(getSystem)
|
||||
const { isFileUploadIntended: isFileUploadIntendedWrap } = wrapOAS31Fn(
|
||||
{
|
||||
isFileUploadIntended,
|
||||
},
|
||||
getSystem()
|
||||
)
|
||||
|
||||
this.fn.isFileUploadIntended = isFileUploadIntendedWrap
|
||||
this.fn.isFileUploadIntendedOAS31 = isFileUploadIntended
|
||||
|
||||
if (fn.jsonSchema202012) {
|
||||
const { hasSchemaType } = wrapOAS31Fn(
|
||||
{
|
||||
hasSchemaType: fn.jsonSchema202012.hasSchemaType,
|
||||
},
|
||||
getSystem()
|
||||
)
|
||||
|
||||
this.fn.hasSchemaType = hasSchemaType
|
||||
}
|
||||
}
|
||||
|
||||
export default afterLoad
|
||||
|
||||
13
src/core/plugins/oas31/oas3-extensions/fn.js
Normal file
13
src/core/plugins/oas31/oas3-extensions/fn.js
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @prettier
|
||||
*/
|
||||
|
||||
export const makeIsFileUploadIntended = (getSystem) => {
|
||||
const isFileUploadIntended = (schema, mediaType = null) => {
|
||||
const { fn } = getSystem()
|
||||
|
||||
return fn.isFileUploadIntendedOAS30(schema, mediaType)
|
||||
}
|
||||
|
||||
return isFileUploadIntended
|
||||
}
|
||||
Reference in New Issue
Block a user