swagger-api / swagger-codegen

swagger-codegen contains a template-driven engine to generate documentation, API clients and server stubs in different languages by parsing your OpenAPI / Swagger definition.
http://swagger.io
Apache License 2.0
16.88k stars 6.03k forks source link

[Python] parameter type 'file' not handled correctly #7935

Open shuoli84 opened 6 years ago

shuoli84 commented 6 years ago
Description

parameter type 'file' not proper handled. The generated code contains one line

from swagger_client.models.file import file
Swagger-codegen version

2.3.1

Swagger declaration file content or url

If you post the code inline, please wrap it with

  /media/web/upload:
    post:
      responses:
        default:
      parameters:
        - type: file
          name: media
          in: formData
      consumes:
        - multipart/form-data
Command line used for generation

swagger-codegen generate -i /tmp/openapi2.0.json -l python

shuoli84 commented 6 years ago

generated java client is correct. Generated type for 'file' object is java.io.File.

dobos commented 6 years ago

Also, when return type is file in body, the python client tries to parse it and, obviously, chokes on it:

"V1/Editor.svc/footprint/plot": {
      "get": {
        "summary": "Plots the footprint",
        "operationId": "PlotFootprint",
        "tags": [
          "Editor"
        ],
        "consumes": [],
        "produces": [
          "image/jpeg",
          "image/png",
          "image/gif",
          "image/bmp",
          "application/pdf",
          "application/postscript",
          "windows/metafile"
        ],
        "parameters": [],
        "responses": {
          "default": {
            "description": "unexpected error",
            "schema": {
              "$ref": "#/definitions/RestError"
            }
          },
          "200": {
            "description": "",
            "schema": {
              "type": "file"
            }
          }
        }
      },

The generated request is correct:

GET http://localhost/.../footprint/plot HTTP/1.1
Accept-Encoding: identity
Accept: image/jpeg, image/png, image/gif, image/bmp, application/pdf, application/postscript, windows/metafile
Host: localhost
User-Agent: SciServer Footprint Service Client/1.0.0/python
Cookie: ASP.NET_SessionId=m1eyobvscecgnsxwe2zt1o4y
Content-Type: application/json

While the web service correctly returns a jpeg file with the following header

HTTP/1.1 200 OK
Cache-Control: private
Transfer-Encoding: chunked
Content-Type: image/jpeg
Server: Microsoft-IIS/10.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Accept, Content-Type, Content-Range, Content-Disposition, Content-Description, X-Auth-Token
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 08 May 2018 08:04:31 GMT

The Python client throws an exception

Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\lib\unittest\case.py", line 59, in testPartExecutor
    yield
  File "C:\ProgramData\Anaconda3\lib\unittest\case.py", line 605, in run
    testMethod()
  File "C:\...\test\test_editor_api.py", line 183, in test_plot_footprint
    self.api.plot_footprint()
  File "C:\...\footprint\api\editor_api.py", line 1931, in plot_footprint
    (data) = self.plot_footprint_with_http_info(**kwargs)  # noqa: E501
  File "C:\...\footprint\api\editor_api.py", line 2030, in plot_footprint_with_http_info
    collection_formats=collection_formats)
  File "C:\...\footprint\api_client.py", line 322, in call_api
    _preload_content, _request_timeout)
  File "C:\...\footprint\api_client.py", line 153, in __call_api
    _request_timeout=_request_timeout)
  File "C:\...\footprint\sciserver_client.py", line 33, in request
    _request_timeout)
  File "C:\...\footprint\api_client.py", line 343, in request
    headers=headers)
  File "C:\...\footprint\rest.py", line 238, in GET
    query_params=query_params)
  File "C:\...\footprint\rest.py", line 222, in request
    r.data = r.data.decode('utf8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x89 in position 0: invalid start byte

The generated code says:

# In the python 3, the response.data is bytes.
            # we need to decode it to string.
            if six.PY3:
                r.data = r.data.decode('utf8')

So the solution is to check if response is xml or json and not to convert to string if binary response is expected. As far as I see, there is no way to pass information about the expected output format down to the rest api layer except the accept header without changing all the functions down to GET, POST etc.

dobos commented 6 years ago

A work-around is to pass _preload_content=False to the api calls but this will result in a streaming behavior. It's probably fine for files but yet, it would be nicer if the generated lib didn't try to parse binary resposes.

rholloway commented 5 years ago

👍 yea this is definitely a bug in the library if you ask me. We are having to patch to account for different file types. Not sure why it's even necessary to decode to unicode in snippet above...where does the lib require data to be unicode? In fact, by forcing to unicode it's going to break after decoding when you get into api_client.py and try to write to file with mode "wb". Leaving as bytes actually works better.