diff --git a/src/core/config/defaults.js b/src/core/config/defaults.js
index b7daa8fd624..12ce768b11e 100644
--- a/src/core/config/defaults.js
+++ b/src/core/config/defaults.js
@@ -87,6 +87,13 @@ const defaultOptions = Object.freeze({
onComplete: null,
modelPropertyMacro: null,
parameterMacro: null,
+
+ fileUploadMediaTypes: [
+ "application/octet-stream",
+ "image/",
+ "audio/",
+ "video/",
+ ],
})
export default defaultOptions
diff --git a/src/core/config/type-cast/mappings.js b/src/core/config/type-cast/mappings.js
index b025fb1295e..559f35025c2 100644
--- a/src/core/config/type-cast/mappings.js
+++ b/src/core/config/type-cast/mappings.js
@@ -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 },
diff --git a/src/core/plugins/json-schema-2020-12/fn.js b/src/core/plugins/json-schema-2020-12/fn.js
index 1e6626e4c56..d07f7acd4e0 100644
--- a/src/core/plugins/json-schema-2020-12/fn.js
+++ b/src/core/plugins/json-schema-2020-12/fn.js
@@ -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)
+}
diff --git a/src/core/plugins/json-schema-2020-12/index.js b/src/core/plugins/json-schema-2020-12/index.js
index e6c929d9fd9..c78d89dc7d2 100644
--- a/src/core/plugins/json-schema-2020-12/index.js
+++ b/src/core/plugins/json-schema-2020-12/index.js
@@ -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,
},
},
}
diff --git a/src/core/plugins/json-schema-5/components/json-schema-components.jsx b/src/core/plugins/json-schema-5/components/json-schema-components.jsx
index 1899ffcdf68..6575a067dcd 100644
--- a/src/core/plugins/json-schema-5/components/json-schema-components.jsx
+++ b/src/core/plugins/json-schema-5/components/json-schema-components.jsx
@@ -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")
}
diff --git a/src/core/plugins/json-schema-5/fn.js b/src/core/plugins/json-schema-5/fn.js
new file mode 100644
index 00000000000..c4844b4bcba
--- /dev/null
+++ b/src/core/plugins/json-schema-5/fn.js
@@ -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))
+ )
+}
diff --git a/src/core/plugins/json-schema-5/index.js b/src/core/plugins/json-schema-5/index.js
index 3aa3e375d78..f3e394a1699 100644
--- a/src/core/plugins/json-schema-5/index.js
+++ b/src/core/plugins/json-schema-5/index.js
@@ -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
diff --git a/src/core/plugins/oas3/components/request-body.jsx b/src/core/plugins/oas3/components/request-body.jsx
index 7c80040be50..d7065ee04d0 100644
--- a/src/core/plugins/oas3/components/request-body.jsx
+++ b/src/core/plugins/oas3/components/request-body.jsx
@@ -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
}
+
+ 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 = {
+ 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
+}
diff --git a/src/core/plugins/oas3/index.js b/src/core/plugins/oas3/index.js
index 7d63ce0e6a0..9a42d9e2ae1 100644
--- a/src/core/plugins/oas3/index.js
+++ b/src/core/plugins/oas3/index.js
@@ -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,
+ },
}
}
diff --git a/src/core/plugins/oas3/wrap-components/json-schema-string.jsx b/src/core/plugins/oas3/wrap-components/json-schema-string.jsx
index 96534b0f20b..86469396e83 100644
--- a/src/core/plugins/oas3/wrap-components/json-schema-string.jsx
+++ b/src/core/plugins/oas3/wrap-components/json-schema-string.jsx
@@ -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 {
+ const isFileUploadIntended = (schema, mediaType = null) => {
+ const { fn } = getSystem()
+
+ return fn.isFileUploadIntendedOAS30(schema, mediaType)
+ }
+
+ return isFileUploadIntended
+}
diff --git a/test/e2e-cypress/e2e/features/plugins/oas3/request-body-upload-file.cy.js b/test/e2e-cypress/e2e/features/plugins/oas3/request-body-upload-file.cy.js
new file mode 100644
index 00000000000..c5827b59432
--- /dev/null
+++ b/test/e2e-cypress/e2e/features/plugins/oas3/request-body-upload-file.cy.js
@@ -0,0 +1,189 @@
+/**
+ * @prettier
+ */
+
+describe("OpenAPI 3.0 Request Body upload file button", () => {
+ beforeEach(() => {
+ cy.visit("/?url=/documents/features/oas3-request-body-upload-file.yaml")
+ })
+
+ describe("application/octet-stream", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadApplicationOctetStream").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for application/octet-stream media types."
+ )
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("image/png", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadImagePng").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for image/png media types."
+ )
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("audio/wav", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadAudioWav").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for audio/wav media types."
+ )
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("video/mpeg", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadVideoMpeg").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for video/mpeg media types."
+ )
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("application/octet-stream with empty Media Type Object", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadApplicationOctetStreamEmpty").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for application/octet-stream media types."
+ )
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("schema type string and format binary", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadSchemaFormatBinary").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for application/x-custom media types."
+ )
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("schema type string and format byte", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadSchemaFormatByte").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for application/x-custom media types."
+ )
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("multipart/form-data object property with schema type string and format binary", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadPropertySchemaFormatBinary").click()
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("multipart/form-data object property with schema type string and format byte", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadPropertySchemaFormatByte").click()
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+})
diff --git a/test/e2e-cypress/e2e/features/plugins/oas31/oas31-request-body-upload-file.cy.js b/test/e2e-cypress/e2e/features/plugins/oas31/oas31-request-body-upload-file.cy.js
new file mode 100644
index 00000000000..4d1f9a254aa
--- /dev/null
+++ b/test/e2e-cypress/e2e/features/plugins/oas31/oas31-request-body-upload-file.cy.js
@@ -0,0 +1,257 @@
+/**
+ * @prettier
+ */
+
+describe("OpenAPI 3.1 Request Body upload file button", () => {
+ beforeEach(() => {
+ cy.visit("/?url=/documents/features/oas31-request-body-upload-file.yaml")
+ })
+
+ describe("application/octet-stream", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadApplicationOctetStream").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for application/octet-stream media types."
+ )
+ })
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("image/png", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadImagePng").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for image/png media types."
+ )
+ })
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("audio/wav", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadAudioWav").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for audio/wav media types."
+ )
+ })
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("video/mpeg", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadVideoMpeg").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for video/mpeg media types."
+ )
+ })
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("application/octet-stream with empty Media Type Object", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadApplicationOctetStreamEmpty").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for application/octet-stream media types."
+ )
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("schema type string and format binary", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadSchemaTypeFormatBinary").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for application/x-custom media types."
+ )
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("schema type string and format byte", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadSchemaTypeFormatByte").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for application/x-custom media types."
+ )
+ })
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("schema union type includes string and format binary", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadSchemaUnionTypeFormatBinary").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for application/x-custom media types."
+ )
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("schema union type includes string and format byte", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadSchemaUnionTypeFormatByte").click()
+ })
+
+ it("should display description with the correct content type", () => {
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper i"
+ ).should(
+ "have.text",
+ "Example values are not available for application/x-custom media types."
+ )
+ })
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("multipart/form-data object property with schema type string and format binary", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadPropertySchemaFormatBinary").click()
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("multipart/form-data object property with schema type string and format byte", () => {
+ beforeEach(() => {
+ cy.get("#operations-default-uploadPropertySchemaFormatByte").click()
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("multipart/form-data object property with schema union type including string and format binary", () => {
+ beforeEach(() => {
+ cy.get(
+ "#operations-default-uploadPropertySchemaUnionTypeFormatBinary"
+ ).click()
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+
+ describe("multipart/form-data object property with schema union type including string and format byte", () => {
+ beforeEach(() => {
+ cy.get(
+ "#operations-default-uploadPropertySchemaUnionTypeFormatByte"
+ ).click()
+ })
+
+ it("should display a file upload button", () => {
+ cy.get(".try-out__btn").click()
+ cy.get(
+ ".opblock-section-request-body .opblock-description-wrapper input"
+ ).should("have.prop", "type", "file")
+ })
+ })
+})
diff --git a/test/e2e-cypress/e2e/features/request-body-upload-file.cy.js b/test/e2e-cypress/e2e/features/request-body-upload-file.cy.js
deleted file mode 100644
index bb0819f92dc..00000000000
--- a/test/e2e-cypress/e2e/features/request-body-upload-file.cy.js
+++ /dev/null
@@ -1,132 +0,0 @@
-/**
- * @prettier
- */
-
-describe("OpenAPI 3.0 Request Body upload file button", () => {
- describe("application/octet-stream", () => {
- it("should display description with the correct content type", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadApplicationOctetStream")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper i")
- .should(
- "have.text",
- "Example values are not available for application/octet-stream media types."
- )
- })
- it("should display a file upload button", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadApplicationOctetStream")
- .click()
- .get(".try-out__btn")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper input")
- .should("have.prop", "type", "file")
- })
- })
- describe("image/png", () => {
- it("should display description with the correct content type", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadImagePng")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper i")
- .should(
- "have.text",
- "Example values are not available for image/png media types."
- )
- })
- it("should display a file upload button", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadApplicationOctetStream")
- .click()
- .get(".try-out__btn")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper input")
- .should("have.prop", "type", "file")
- })
- })
- describe("audio/wav", () => {
- it("should display description with the correct content type", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadAudioWav")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper i")
- .should(
- "have.text",
- "Example values are not available for audio/wav media types."
- )
- })
- it("should display a file upload button", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadApplicationOctetStream")
- .click()
- .get(".try-out__btn")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper input")
- .should("have.prop", "type", "file")
- })
- })
- describe("video/mpeg", () => {
- it("should display description with the correct content type", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadVideoMpeg")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper i")
- .should(
- "have.text",
- "Example values are not available for video/mpeg media types."
- )
- })
- it("should display a file upload button", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadApplicationOctetStream")
- .click()
- .get(".try-out__btn")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper input")
- .should("have.prop", "type", "file")
- })
- })
- describe("schema format binary", () => {
- it("should display description with the correct content type", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadSchemaFormatBinary")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper i")
- .should(
- "have.text",
- "Example values are not available for application/x-custom media types."
- )
- })
- it("should display a file upload button", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadSchemaFormatBinary")
- .click()
- .get(".try-out__btn")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper input")
- .should("have.prop", "type", "file")
- })
- })
- describe("schema format base64", () => {
- it("should display description with the correct content type", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadSchemaFormatBase64")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper i")
- .should(
- "have.text",
- "Example values are not available for application/x-custom media types."
- )
- })
- it("should display a file upload button", () => {
- cy.visit("/?url=/documents/features/request-body-upload-file.yaml")
- .get("#operations-default-uploadSchemaFormatBinary")
- .click()
- .get(".try-out__btn")
- .click()
- .get(".opblock-section-request-body .opblock-description-wrapper input")
- .should("have.prop", "type", "file")
- })
- })
-})
diff --git a/test/e2e-cypress/static/documents/features/request-body-upload-file.yaml b/test/e2e-cypress/static/documents/features/oas3-request-body-upload-file.yaml
similarity index 53%
rename from test/e2e-cypress/static/documents/features/request-body-upload-file.yaml
rename to test/e2e-cypress/static/documents/features/oas3-request-body-upload-file.yaml
index 2c487da298c..619a5095a14 100644
--- a/test/e2e-cypress/static/documents/features/request-body-upload-file.yaml
+++ b/test/e2e-cypress/static/documents/features/oas3-request-body-upload-file.yaml
@@ -7,8 +7,11 @@ info:
* `audio/*` content type (no matter what schema format)
* `image/*` content type (no matter what schema format)
* `video/*` content type (no matter what schema format)
- * schema format is `base64` (no matter what content type)
- * schema format is `binary` (no matter what content type)
+ * `application/octect-stream` content type with empty Media Type Object
+ * schema type is `string` and format is `byte` (no matter what content type)
+ * schema type is `string` and format is `binary` (no matter what content type)
+ * multipart/form-data object property schema type is `string` and format is `byte`
+ * multipart/form-data object property schema type is `string` and format is `binary`
version: "1.0.0"
paths:
/upload-application-octet-stream:
@@ -19,13 +22,6 @@ paths:
application/octet-stream:
schema:
type: string
- responses:
- '200':
- description: successful operation
- content:
- text/plain:
- schema:
- type: string
/upload-image-png:
post:
operationId: uploadImagePng
@@ -34,13 +30,6 @@ paths:
image/png:
schema:
type: string
- responses:
- '200':
- description: successful operation
- content:
- text/plain:
- schema:
- type: string
/upload-audio-wav:
post:
operationId: uploadAudioWav
@@ -49,13 +38,6 @@ paths:
audio/wav:
schema:
type: string
- responses:
- '200':
- description: successful operation
- content:
- text/plain:
- schema:
- type: string
/upload-video-mpeg:
post:
operationId: uploadVideoMpeg
@@ -64,13 +46,12 @@ paths:
video/mpeg:
schema:
type: string
- responses:
- '200':
- description: successful operation
- content:
- text/plain:
- schema:
- type: string
+ /upload-application-octet-stream-empty:
+ post:
+ operationId: uploadApplicationOctetStreamEmpty
+ requestBody:
+ content:
+ application/octet-stream: {}
/upload-schema-format-binary:
post:
operationId: uploadSchemaFormatBinary
@@ -80,26 +61,36 @@ paths:
schema:
type: string
format: binary
- responses:
- '200':
- description: successful operation
- content:
- text/plain:
- schema:
- type: string
- /upload-schema-format-base64:
+ /upload-schema-format-byte:
post:
- operationId: uploadSchemaFormatBase64
+ operationId: uploadSchemaFormatByte
requestBody:
content:
application/x-custom:
schema:
type: string
- format: base64
- responses:
- '200':
- description: successful operation
- content:
- text/plain:
- schema:
- type: string
+ format: byte
+ /upload-property-schema-format-binary:
+ post:
+ operationId: uploadPropertySchemaFormatBinary
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ file:
+ type: string
+ format: binary
+ /upload-property-schema-format-byte:
+ post:
+ operationId: uploadPropertySchemaFormatByte
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ file:
+ type: string
+ format: byte
diff --git a/test/e2e-cypress/static/documents/features/oas31-request-body-upload-file.yaml b/test/e2e-cypress/static/documents/features/oas31-request-body-upload-file.yaml
new file mode 100644
index 00000000000..85c04608eef
--- /dev/null
+++ b/test/e2e-cypress/static/documents/features/oas31-request-body-upload-file.yaml
@@ -0,0 +1,150 @@
+openapi: 3.1.0
+info:
+ title: "Request body file upload"
+ description: |-
+ This document has examples for examining the `schema` or content type for request bodies requiring a file upload
+ * `application/octect-stream` content type (no matter what schema format)
+ * `audio/*` content type (no matter what schema format)
+ * `image/*` content type (no matter what schema format)
+ * `video/*` content type (no matter what schema format)
+ * `application/octect-stream` content type with empty Media Type Object
+ * schema type is `string` and format is `byte` (no matter what content type)
+ * schema type is `string` and format is `binary` (no matter what content type)
+ * schema union type includes `string` and format is `byte` (no matter what content type)
+ * schema union type includes `string` and format is `binary` (no matter what content type)
+ * multipart/form-data object property schema type is `string` and format is `byte`
+ * multipart/form-data object property schema type is `string` and format is `binary`
+ * multipart/form-data object property schema union type includes `string` and format is `byte`
+ * multipart/form-data object property schema union type includes `string` and format is `binary`
+ version: "1.0.0"
+paths:
+ /upload-application-octet-stream:
+ post:
+ operationId: uploadApplicationOctetStream
+ requestBody:
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ /upload-image-png:
+ post:
+ operationId: uploadImagePng
+ requestBody:
+ content:
+ image/png:
+ schema:
+ type: string
+ /upload-audio-wav:
+ post:
+ operationId: uploadAudioWav
+ requestBody:
+ content:
+ audio/wav:
+ schema:
+ type: string
+ /upload-video-mpeg:
+ post:
+ operationId: uploadVideoMpeg
+ requestBody:
+ content:
+ video/mpeg:
+ schema:
+ type: string
+ /upload-application-octet-stream-empty:
+ post:
+ operationId: uploadApplicationOctetStreamEmpty
+ requestBody:
+ content:
+ application/octet-stream: {}
+ /upload-schema-type-format-binary:
+ post:
+ operationId: uploadSchemaTypeFormatBinary
+ requestBody:
+ content:
+ application/x-custom:
+ schema:
+ type: string
+ format: binary
+ /upload-schema-type-format-byte:
+ post:
+ operationId: uploadSchemaTypeFormatByte
+ requestBody:
+ content:
+ application/x-custom:
+ schema:
+ type: string
+ format: byte
+ /upload-schema-union-type-format-binary:
+ post:
+ operationId: uploadSchemaUnionTypeFormatBinary
+ requestBody:
+ content:
+ application/x-custom:
+ schema:
+ type:
+ - object
+ - string
+ format: binary
+ /upload-schema-union-type-format-byte:
+ post:
+ operationId: uploadSchemaUnionTypeFormatByte
+ requestBody:
+ content:
+ application/x-custom:
+ schema:
+ type:
+ - object
+ - string
+ format: byte
+ /upload-property-schema-format-binary:
+ post:
+ operationId: uploadPropertySchemaFormatBinary
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ file:
+ type: string
+ format: binary
+ /upload-property-schema-format-byte:
+ post:
+ operationId: uploadPropertySchemaFormatByte
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ file:
+ type: string
+ format: binary
+ /upload-property-schema-union-type-format-binary:
+ post:
+ operationId: uploadPropertySchemaUnionTypeFormatBinary
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ file:
+ type:
+ - object
+ - string
+ format: binary
+ /upload-property-schema-union-type-format-byte:
+ post:
+ operationId: uploadPropertySchemaUnionTypeFormatByte
+ requestBody:
+ content:
+ multipart/form-data:
+ schema:
+ type: object
+ properties:
+ file:
+ type:
+ - object
+ - string
+ format: byte
diff --git a/test/unit/core/plugins/json-schema-5/components/json-schema-form.jsx b/test/unit/core/plugins/json-schema-5/components/json-schema-form.jsx
index 73d0d2292a3..4600beb0784 100644
--- a/test/unit/core/plugins/json-schema-5/components/json-schema-form.jsx
+++ b/test/unit/core/plugins/json-schema-5/components/json-schema-form.jsx
@@ -4,6 +4,7 @@ import { Select, Input, TextArea } from "core/components/layout-utils"
import { mount, render } from "enzyme"
import * as JsonSchemaComponents from "core/plugins/json-schema-5/components/json-schema-components"
import { foldType } from "core/plugins/json-schema-2020-12-samples/fn/index"
+import { makeIsFileUploadIntended } from "core/plugins/oas3/fn"
const components = {...JsonSchemaComponents, Select, Input, TextArea}
@@ -13,6 +14,16 @@ const getComponentStub = (name) => {
return null
}
+const getSystemStub = () => ({
+ getConfigs: () => ({
+ fileUploadMediaTypes: [],
+ }),
+ fn: {
+ hasSchemaType: () => {},
+ isFileUploadIntendedOAS30: () => {},
+ },
+})
+
describe("", function(){
describe("strings", function() {
it("should render the correct options for a string enum parameter", function(){
@@ -26,6 +37,7 @@ describe("", function(){
jsonSchema202012: {
foldType,
},
+ isFileUploadIntended: makeIsFileUploadIntended(getSystemStub)
},
schema: Immutable.fromJS({
type: "string",
@@ -53,6 +65,7 @@ describe("", function(){
jsonSchema202012: {
foldType,
},
+ isFileUploadIntended: makeIsFileUploadIntended(getSystemStub)
},
schema: Immutable.fromJS({
type: "string",
@@ -78,6 +91,7 @@ describe("", function(){
jsonSchema202012: {
foldType,
},
+ isFileUploadIntended: makeIsFileUploadIntended(getSystemStub)
},
required: true,
schema: Immutable.fromJS({
@@ -106,6 +120,7 @@ describe("", function(){
jsonSchema202012: {
foldType,
},
+ isFileUploadIntended: makeIsFileUploadIntended(getSystemStub)
},
schema: Immutable.fromJS({
type: "boolean"
@@ -133,6 +148,7 @@ describe("", function(){
jsonSchema202012: {
foldType,
},
+ isFileUploadIntended: makeIsFileUploadIntended(getSystemStub)
},
schema: Immutable.fromJS({
type: "boolean",
@@ -160,6 +176,7 @@ describe("", function(){
jsonSchema202012: {
foldType,
},
+ isFileUploadIntended: makeIsFileUploadIntended(getSystemStub)
},
schema: Immutable.fromJS({
type: "boolean",
@@ -188,6 +205,7 @@ describe("", function(){
jsonSchema202012: {
foldType,
},
+ isFileUploadIntended: makeIsFileUploadIntended(getSystemStub)
},
required: true,
schema: Immutable.fromJS({
@@ -219,6 +237,7 @@ describe("", function(){
jsonSchema202012: {
foldType,
},
+ isFileUploadIntended: makeIsFileUploadIntended(getSystemStub)
},
errors: List(),
schema: Immutable.fromJS({
@@ -252,6 +271,7 @@ describe("", function(){
jsonSchema202012: {
foldType,
},
+ isFileUploadIntended: makeIsFileUploadIntended(getSystemStub)
},
schema: Immutable.fromJS({
type: "NotARealType"
@@ -278,6 +298,7 @@ describe("", function(){
jsonSchema202012: {
foldType,
},
+ isFileUploadIntended: makeIsFileUploadIntended(getSystemStub)
},
schema: Immutable.fromJS({
type: "NotARealType",