OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.63k stars 6.53k forks source link

[BUG] [Python] client - response fails to deserialize due to incorrect type for field in model code #8227

Closed SteveHespelt closed 3 years ago

SteveHespelt commented 3 years ago

Bug Report Checklist

Description

The generated model code for type Elements for the response schema uses a type 'object' which causes an ApiValueError exception to be raised when the model_utils.py deserialize_model() is invoked - object is not a supported type. I was expecting that for a freeform property of type object in the spec would result in a dict type being used.

I would expect a model/element.py to be generated (based on the schema) and for the Elements.openapi_types method to return a dictionary using 'elements' : ([Element],)
rather than the generated code:

        return {
            'elements': ([object],),  # noqa: E501
            'comment': (str,),  # noqa: E501
            'source': (str,),  # noqa: E501
        }
openapi-generator version

5.0.0-beta3, api-latest-master.openapi-generator.tech

OpenAPI declaration file content or url

a complete but minimal spec available here: minimal-spec.txt

Generation Details

openapi-generator-cli generate -g python -o ./client -i minimal-spec.yaml
also used the online generator at api-latest-master.openapi-generator.tech to try with a SNAPSHOT deployment of master

Steps to reproduce

Of some potential interest, if I submit the same spec to the online swagger generator generator3.swagger.io, the generated code includes the expected Element type and elements.py makes use of it in the swagger_types dictionary.

Related issues/PRs
Suggest a fix
SteveHespelt commented 3 years ago

If I remove the use of the ELement type in the Elements model (ie.

    Elements:
      type: "object"
      required:
        - "elements"
      properties:
        elements:
          type: "array"
          items:
            type: object  <- No more use of the type Element
            #$ref: "#/components/schemas/Element"
        comment:
          type: "string"
        source:
          type: "string"

The python client generated code's model/elements.py now has openapi_types returning dictionary

        return {
            'elements': ([{str: (bool, date, datetime, dict, float, int, list, str, none_type)}],),  # noqa: E501
            'comment': (str,),  # noqa: E501
            'source': (str,),  # noqa: E501
        }

which is as one would expect.
Using python-flask as the generator value, the server code's models/elements.py Elements.init uses an openapi_types dictionary:

        self.openapi_types = {
            'elements': List[object],  # <- a 'bit' different from that used by the python client code.
            'comment': str,
            'source': str
        }

Hopefully this helps?

spacether commented 3 years ago

Our sample spec includes a similar-to example in the Pet model. Pet.tag definition: https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/test/resources/3_0/python/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml#L1500

        tags:
          type: array
          items:
            $ref: '#/components/schemas/Tag'

Generated model: https://github.com/OpenAPITools/openapi-generator/blob/master/samples/openapi3/client/petstore/python/petstore_api/model/pet.py#L93 Please see Pet.tags defined as ([Tag],) which is an array of Tag

    def openapi_types():
        """
        This must be a method because a model may have properties that are
        of type self, this must run after the class is loaded
        Returns
            openapi_types (dict): The key is attribute name
                and the value is attribute type.
        """
        lazy_import()
        return {
            'name': (str,),  # noqa: E501
            'photo_urls': ([str],),  # noqa: E501
            'id': (int,),  # noqa: E501
            'category': (Category,),  # noqa: E501
            'tags': ([Tag],),  # noqa: E501
            'status': (str,),  # noqa: E501
        }
spacether commented 3 years ago

Trying this with the latest 5.0.1 release I see these types being generated with the python generator:

    @cached_property
    def openapi_types():
        """
        This must be a method because a model may have properties that are
        of type self, this must run after the class is loaded

        Returns
            openapi_types (dict): The key is attribute name
                and the value is attribute type.
        """
        lazy_import()
        return {
            'elements': ([Element],),  # noqa: E501
            'comment': (str,),  # noqa: E501
            'source': (str,),  # noqa: E501
        }

So it looks like this is working. Also I reverted to 5.0.3 beta3 version at commit 2715f1371a1bd438159e4aa79bd8f1fe172b03d9 and I get the same results. @SteveHespelt

spacether commented 3 years ago

@SteveHespelt I'm having trouble understanding what you want here.

  1. Do you want elements to be a list of dicts containing values of any type?
  2. Do you want elements to be a list of Element? # this is the one that I think you want and this is what the code does
  3. Do you want elements to be a list containing values of any type?

Are you asking for changes to the python client or the python server?

spacether commented 3 years ago

Closing this issue because it is unreproducible. Per tests in https://github.com/OpenAPITools/openapi-generator/issues/8227#issuecomment-774189117 the type generation here is correct, and is the desired 'elements': ([Element],),