Dorthu / openapi3

A Python3 OpenAPI 3 Spec Parser
BSD 3-Clause "New" or "Revised" License
117 stars 47 forks source link

Multipart support for the requests to server #96

Open miAndreev opened 1 year ago

miAndreev commented 1 year ago

Hi, I need to make multipart requests to server and I'm trying to add this feature to the code. For now I think to add files to _request.files and add them to _request.files and the rest will add to the fields.

At the moment it's work in progress.

Do you have any tips or any requirement to the implementation? I would love to see this feature in this library.

PS: Also I think that it would be great when a NotImplementedError is raised there would be also a message.

Dorthu commented 1 year ago

It sounds like you're on the right track; I'd add a new condition in here to hande multipart/form-data requestBody types; the incoming data type will also matter, as a valid spec might include multiple request types, and defining a new class for it will help disambiguate those cases. Once you decide that the operation supports multipart/form-data and that the given data looks like multipart form data, setting self._request.files should do the trick - the requests documentation shows that being set as a tuple.

Off the top of my head, it might look something like this:

def _request_handle_body(self, data):
    # this is assuming a new class, MultipartFormData, is defined that wraps the values expected by requests
    # and can return the correctly-formatted tuple
    if isinstance(data, MultipartFormData) and "multipart/form-data" in self.requestBody.content:
        # some validation to ensure that incoming field names match those in the spec
        self._request.files = data.serialized
    elif "application/json" in self.requestBody.content:
        # existing code

I'd expect to use it like this:

spec.call_multipartOperation(
    data=MultipartFormData(
        MultipartFormDataFile("field-name", "filename", file_object, "content/type"),
        MultipartFormDataFile("field-name", "filename2", file_object_2, "content/type"),
    ),
)

Those aren't hard requirements though, just how I'd do it; if you have other ideas, I'd be happy to hear them. Feel free to open a draft PR if you'd like to collaborate more directly.

miAndreev commented 1 year ago

Thank you for the quick response! I started also with addition in _request_handle_body.

For now I check the properties in the schema in the self.requestBody.content['multipart/form-data'] for the format field. When it is "binary" I add it to dict with the files, the rest goes to data. At the end I set the dicts to the request object.

Other variant is to pass one more parameter to _request_handle_body - with files.

When I have something working I will make a PR and I will iterate from there,

miAndreev commented 1 year ago

Hi! I have something that seams to work. https://github.com/Dorthu/openapi3/pull/97

For now there it supports to pass only dictionary with the format - { "field name": file_obj }. In Requests documentation they say that the file can also be a tuple. (https://requests.readthedocs.io/en/latest/api/#requests.request)

For the implementation - I choose a separate structure files as so there is no need to decide what field is a file and what not.

I will be happy to become a feedback for the implementation! The tests are not ready yet.