openapi-python-client icon indicating copy to clipboard operation
openapi-python-client copied to clipboard

String Binary File is just expecting BytesIO and not filename and mime-type

Open b-pison opened this issue 3 years ago • 1 comments

I have Java OpenAPI server which provides an upload endpoint (POST) that expect as schema a FileRequest ( schema = @Schema(implementation = FileRequest.class))

The FileRequest looks like:

@Value.Immutable
@JsonDeserialize(builder = com.mydata.request.FileRequest.Builder.class)
public abstract class FileRequest implements FileFields {
    @Schema(type = "string", format = "binary", description = "File")
    @JsonProperty
    public abstract String file();

    public static class Builder extends ImmutableFileRequest.Builder {

    }
}

Looking at the generated code, the models looks good. This is the relevant part on types.py:

@attr.s(auto_attribs=True)
class File:
    """ Contains information for file uploads """

    payload: BinaryIO
    file_name: Optional[str] = None
    mime_type: Optional[str] = None

    def to_tuple(self) -> FileJsonType:
        """ Return a tuple representation that httpx will accept for multipart/form-data """
        return self.file_name, self.payload, self.mime_type


T = TypeVar("T")

However, looking at the upload endpoint request, it doesn't expect the Optional values above:


    @classmethod
    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
        d = src_dict.copy()

        _file = d.pop("file", UNSET)
        file: Union[Unset, File]
        if isinstance(_file,  Unset):
            file = UNSET
        else:
            file = File(
             payload = BytesIO(_file)
        )

Notice that the File expects only the binary data, losing the filename and mimetype.

Expected behavior

I would expect that the generated code would look something like below, which I patched and am using currently:

        else:
            file = File(
             payload = BytesIO(_file['payload']),
                file_name=_file['filename']
        )

Desktop (please complete the following information):

  • OS: Linux, but irrelevant
  • openapi-python-client version: 0.11.1
  • Python 3.8.10
  • openapi: 3.0.1

b-pison avatar May 04 '22 16:05 b-pison

Hi All, I too have the same requirement of uploading a pdf file via POST and I am running into a issue perhaps because of the above issue.

The relevant part from the OpenAPI.json is:

"requestBody": {
    "content": {
      "multipart/form-data": {
        "schema": {
          "required": [
            "pdfDocument"
          ],
          "type": "object",
          "properties": {
            "pdfDocument": {
              "type": "string",
              "format": "binary"
            }
          }
        }
      }
    }
  }

The relevant and important python code snippet generated via this json file : in types.py:

@define
class File:
    """Contains information for file uploads"""

    payload: BinaryIO
    file_name: Optional[str] = None
    mime_type: Optional[str] = None

    def to_tuple(self) -> FileJsonType:
        """Return a tuple representation that httpx will accept for multipart/form-data"""
        return self.file_name, self.payload, self.mime_type

in models/post_document_multipart_data.py:

@define
class PostDocumentMultipartData:
    """
    Attributes:
        pdf_document (File):
    """

    pdf_document: File
    additional_properties: Dict[str, Any] = field(init=False, factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        pdf_document = self.pdf_document.to_tuple()

        field_dict: Dict[str, Any] = {}
        field_dict.update(self.additional_properties)
        field_dict.update(
            {
                "pdfDocument": pdf_document,
            }
        )

        return field_dict

And finally below is my python code that tests the API:

client = AuthenticatedClient(base_url=url, token=token)
payload = open('/path/to/test.pdf', 'rb')
file: File = File(payload=payload)
multipart_data: PostDocumentMultipartData = PostDocumentMultipartData(pdf_document=file)

with client as client:
    response = sync_detailed(client=client, multipart_data=multipart_data)
    print(response)

But when I run this code, I always receive the error 405 (method not allowed) from the server. I have also tried setting the optional parameters of file_name and mime_type but sadly, same behavior.

NOTE: The server is implemented in Java.

Any suggestion what can be improved/corrected here ?

Regards, GSN

govindsinghnegi avatar Jul 24 '23 14:07 govindsinghnegi