🦖 Fix multiple upload file schema to be compatible with OAS 3.0 to make it work in Swagger UI#15069
🦖 Fix multiple upload file schema to be compatible with OAS 3.0 to make it work in Swagger UI#15069YuriiMotov wants to merge 5 commits into
Conversation
|
This pull request has a merge conflict that needs to be resolved. |
|
I was thinking we could just include the |
My bad, I didn't describe properly why we need to handle Specifying |
|
Hi, will it be reviewed soon? I would like to contribute to this PR if more work is needed. |
This comment was marked as resolved.
This comment was marked as resolved.
|
I ran into this issue having multiple files in my upload schema, but the docs having weird text input boxes. If I understand correctly, this PR should resolve it, but seems to be stale? Any chance to fix this issue, as the docs with ability to test the api are really great, if it works. Thanks! :-) |
|
While we are waiting for the issue to be fixed, you can use workaround described here: #14975 (comment) |
#14953 updated schema of
bytefields to be in line with OAS 3.1.But Swagger UI doesn't seem to fully support it and we have text fields instead of file pickers for multiple file upload fields (see #14975).
The idea of this PR is to make the schema of file upload fields backward compatible with OAS 3.0 (which is handled well by Swagger UI) by adding
"format": "binary":{ "type": "string", + "format": "binary", "contentMediaType": "application/octet-stream", }OAS 3.1+ will just ignore it (see spec) and OAS 3.0 - compatible tools will just ignore
contentMediaType(as it's not defined in OAS 3.0).We have to handle separately situation when
bytesare inside JSON (not file upload). To do this we need to somehow pass context (whether it's a File field or not) tobytes_schema.Cloude Code suggested to do it by adding
_FileUploadMarkermarker. I reviewed and it looks good to me (after several iterations :))Tested manually with the following app:
Details
Produced OpenAPI schema:
Details
{ "openapi": "3.1.0", "info": { "title": "FastAPI", "version": "0.1.0" }, "paths": { "/upload-files-bytes": { "post": { "summary": "Upload Files Bytes", "operationId": "upload_files_bytes_upload_files_bytes_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_upload_files_bytes_upload_files_bytes_post" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/upload-files-uploadfile": { "post": { "summary": "Upload Files Uploadfile", "operationId": "upload_files_uploadfile_upload_files_uploadfile_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_upload_files_uploadfile_upload_files_uploadfile_post" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/upload-file-bytes": { "post": { "summary": "Upload File Bytes", "operationId": "upload_file_bytes_upload_file_bytes_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_upload_file_bytes_upload_file_bytes_post" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/upload-file-uploadfile": { "post": { "summary": "Upload File Uploadfile", "operationId": "upload_file_uploadfile_upload_file_uploadfile_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_upload_file_uploadfile_upload_file_uploadfile_post" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/opt-upload-files-bytes": { "post": { "summary": "Opt Upload Files Bytes", "operationId": "opt_upload_files_bytes_opt_upload_files_bytes_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_opt_upload_files_bytes_opt_upload_files_bytes_post" } } } }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/opt-upload-files-uploadfile": { "post": { "summary": "Opt Upload Files Uploadfile", "operationId": "opt_upload_files_uploadfile_opt_upload_files_uploadfile_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_opt_upload_files_uploadfile_opt_upload_files_uploadfile_post" } } } }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/opt-upload-file-bytes": { "post": { "summary": "Opt Upload File Bytes", "operationId": "opt_upload_file_bytes_opt_upload_file_bytes_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_opt_upload_file_bytes_opt_upload_file_bytes_post" } } } }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/opt-upload-file-uploadfile": { "post": { "summary": "Opt Upload File Uploadfile", "operationId": "opt_upload_file_uploadfile_opt_upload_file_uploadfile_post", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/Body_opt_upload_file_uploadfile_opt_upload_file_uploadfile_post" } } } }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/data": { "post": { "summary": "Post Data", "operationId": "post_data_data_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DataInput" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } }, "/data-base64": { "post": { "summary": "Post Data Base64", "operationId": "post_data_base64_data_base64_post", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DataInputBase64" } } }, "required": true }, "responses": { "200": { "description": "Successful Response", "content": { "application/json": { "schema": { } } } }, "422": { "description": "Validation Error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HTTPValidationError" } } } } } } } }, "components": { "schemas": { "Body_opt_upload_file_bytes_opt_upload_file_bytes_post": { "properties": { "p": { "anyOf": [ { "type": "string", "format": "binary", <= Added "contentMediaType": "application/octet-stream" }, { "type": "null" } ], "title": "P" } }, "type": "object", "title": "Body_opt_upload_file_bytes_opt_upload_file_bytes_post" }, "Body_opt_upload_file_uploadfile_opt_upload_file_uploadfile_post": { "properties": { "p": { "anyOf": [ { "type": "string", "format": "binary", <= Added "contentMediaType": "application/octet-stream" }, { "type": "null" } ], "title": "P" } }, "type": "object", "title": "Body_opt_upload_file_uploadfile_opt_upload_file_uploadfile_post" }, "Body_opt_upload_files_bytes_opt_upload_files_bytes_post": { "properties": { "p": { "anyOf": [ { "items": { "type": "string", "format": "binary", <= Added "contentMediaType": "application/octet-stream" }, "type": "array" }, { "type": "null" } ], "title": "P" } }, "type": "object", "title": "Body_opt_upload_files_bytes_opt_upload_files_bytes_post" }, "Body_opt_upload_files_uploadfile_opt_upload_files_uploadfile_post": { "properties": { "p": { "anyOf": [ { "items": { "type": "string", "format": "binary", <= Added "contentMediaType": "application/octet-stream" }, "type": "array" }, { "type": "null" } ], "title": "P" } }, "type": "object", "title": "Body_opt_upload_files_uploadfile_opt_upload_files_uploadfile_post" }, "Body_upload_file_bytes_upload_file_bytes_post": { "properties": { "p": { "type": "string", "format": "binary", <= Added "contentMediaType": "application/octet-stream", "title": "P" } }, "type": "object", "required": [ "p" ], "title": "Body_upload_file_bytes_upload_file_bytes_post" }, "Body_upload_file_uploadfile_upload_file_uploadfile_post": { "properties": { "p": { "type": "string", "format": "binary", <= Added "contentMediaType": "application/octet-stream", "title": "P" } }, "type": "object", "required": [ "p" ], "title": "Body_upload_file_uploadfile_upload_file_uploadfile_post" }, "Body_upload_files_bytes_upload_files_bytes_post": { "properties": { "p": { "items": { "type": "string", "format": "binary", <= Added "contentMediaType": "application/octet-stream" }, "type": "array", "title": "P" } }, "type": "object", "required": [ "p" ], "title": "Body_upload_files_bytes_upload_files_bytes_post" }, "Body_upload_files_uploadfile_upload_files_uploadfile_post": { "properties": { "p": { "items": { "type": "string", "format": "binary", <= Added "contentMediaType": "application/octet-stream" }, "type": "array", "title": "P" } }, "type": "object", "required": [ "p" ], "title": "Body_upload_files_uploadfile_upload_files_uploadfile_post" }, "DataInput": { "properties": { "description": { "type": "string", "title": "Description" }, "data": { "type": "string", <= No 'format: binary' for JSON "contentMediaType": "application/octet-stream", "title": "Data" } }, "type": "object", "required": [ "description", "data" ], "title": "DataInput" }, "DataInputBase64": { "properties": { "description": { "type": "string", "title": "Description" }, "data": { "type": "string", <= No 'format: binary' for JSON "contentEncoding": "base64", "contentMediaType": "application/octet-stream", "title": "Data" } }, "type": "object", "required": [ "description", "data" ], "title": "DataInputBase64" }, "HTTPValidationError": { "properties": { "detail": { "items": { "$ref": "#/components/schemas/ValidationError" }, "type": "array", "title": "Detail" } }, "type": "object", "title": "HTTPValidationError" }, "ValidationError": { "properties": { "loc": { "items": { "anyOf": [ { "type": "string" }, { "type": "integer" } ] }, "type": "array", "title": "Location" }, "msg": { "type": "string", "title": "Message" }, "type": { "type": "string", "title": "Error Type" }, "input": { "title": "Input" }, "ctx": { "type": "object", "title": "Context" } }, "type": "object", "required": [ "loc", "msg", "type" ], "title": "ValidationError" } } } }All endpoints work in Swagger UI