Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: multiple files upload endpoints are broken in the Swagger UI #941

Closed
danielgafni opened this issue Dec 15, 2022 · 4 comments
Closed
Assignees
Labels
Bug 🐛 This is something that is not working as expected wontfix This will not be worked on

Comments

@danielgafni
Copy link

danielgafni commented Dec 15, 2022

Describe the bug
It's asking me to specify a list of strings in the request body instead of providing file upload button(s).

To Reproduce

@post(path="/handle_file_upload")
async def handle_file_upload(
    data: list[UploadFile] = Body(media_type=RequestEncodingType.MULTI_PART),
) -> str:
    return "success"
@danielgafni danielgafni added Bug 🐛 This is something that is not working as expected Triage Required 🏥 This requires triage labels Dec 15, 2022
@cofin
Copy link
Member

cofin commented Dec 15, 2022

Interesting, FWIW, I have multi-file working within the swagger interface with the following setup:

class UploadCreate(CamelizedBaseSchema):
    """Stores upload."""

    collections: list[UploadFile]

    @validator("collections", pre=True, allow_reuse=True)
    def parse_collections(cls, value: UploadFile | list[UploadFile]) -> list[UploadFile]:
        """Parses a list of Upload Files."""
        if isinstance(value, list):
            return value
        if isinstance(value, UploadFile):
            return [value]
        raise ValueError(value)
@post(
    name="collection_upload",
    path="/collections/upload",
    cache=False,
    tags=["Collections"],
    opt={"exclude_from_auth": True},
)
async def upload_file(
    data: schemas.UploadCreate = Body(media_type=RequestEncodingType.MULTI_PART),
) -> schemas.Upload:
    """Upload a file."""
    uploaded_files = await services.storage.collections.upload(files=data.collections)
    logger.info("Uploaded files to: %s", uploaded_files)

    return schemas.Upload(
        collections=uploaded_files,
        message="Assessment queued for processing",
    )
  • Note - CameilizedBaseSchema has allow_arbitrary_types enabled on the Pydantic config.

the list[UploadFile] vs UploadCreate appears to be the primary difference between your example and the above.

@cofin cofin removed the Triage Required 🏥 This requires triage label Dec 15, 2022
@Goldziher
Copy link
Contributor

I don't know how much we should invest in this. @cofin - can you handle this please?

@TorbranTundra
Copy link

TorbranTundra commented Dec 22, 2022

edit: cofins solution works, my bad! forgot to add self as a parameter in the controller function

I tried cofins solution and it works if the route handler is linked directly. If I add it to a controller it doesn't work anymore.
Within a controller I get the following error:

image
{
"status_code": 500,
"detail": "TypeError("FileController.doesnt_work() got multiple values for argument 'data'")"
}

Here the code:


class UploadCreate(BaseModel):
    """Stores upload."""

    class Config:
        arbitrary_types_allowed = True
        
    file: UploadFile

    @validator("file", pre=True, allow_reuse=True)
    def parse_file(cls, value: UploadFile ) -> UploadFile:
        
        if isinstance(value, UploadFile):
            return value
        raise ValueError(value)

        
class FileController(Controller):
    path = "/file"

    @post()
    async def doesnt_work(
        data: UploadCreate = Body(media_type=RequestEncodingType.MULTI_PART),
    ) -> str:
        """Upload a file."""
        return "success"


@post()
async def works(
    data: UploadCreate = Body(media_type=RequestEncodingType.MULTI_PART),
) -> str:
    """Upload a file."""
    return "success"

app = Starlite(route_handlers=[FileController,works])

@cofin
Copy link
Member

cofin commented Jan 22, 2023

I've re-reviewed this one and I don't think there's a great way to implement a fix here. This is due to an incompatibility between the OpenAPI 3.0 and 3.1 specifications. Currently, SwaggerUI only supports OpenAPI 3.0 specification.

Here's an example of an OpenAPI schema that works and one that doesn't for reference:

working:

{ "schema": {
                  "properties": {
                    "files": {
                      "items": {
                        "type": "string",
                        "format": "binary",
                        "contentMediaType": "application/octet-stream"
                      },
                      "type": "array"
                    }
                  },
                  "type": "object"
                }
}

not working:

{
                "schema": {
                  "items": {
                    "type": "string",
                    "format": "binary",
                    "contentMediaType": "application/octet-stream"
                  },
                  "type": "array"
                }
              }

Nesting all response schemas under an type of object isn't feasible, so for now, we should continue using the workaround above until Swagger implements OpenAPI 3.1

Since we have a work-around, I'm marking this one as closed. When/If the the feature above is implemented, this should resolve itself.

@cofin cofin closed this as completed Jan 22, 2023
@cofin cofin added the wontfix This will not be worked on label Jan 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug 🐛 This is something that is not working as expected wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

4 participants