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.83k stars 6.58k forks source link

[BUG][Python] Client expects any object to be a dict #16110

Open Santobert opened 1 year ago

Santobert commented 1 year ago

Bug Report Checklist

Description

The Python client expects a property of type object to be a dict, which seems to be compliant with the JSON schema specification.

object: An unordered set of properties mapping a string to an instance, from the JSON "object" value

However, this causes problems in combination with e.g. a Java server, where everything can be an object. For example, the API might allow an attribute to be either a string, a boolean, or a number, which can't be handled by the Python client.

The provided example generates an error value is not a valid dict (type=type_error.dict). Even if the object is explicitly annotated with anyOf and the corresponding data types, the error is the same.

openapi-generator version

7.0.0-beta

OpenAPI declaration file content or url

openapi.yaml

or with anyOf

...
    properties:
      name:
        type: object
        description: "A string, a number or a boolean"
        anyOf:
        - type: string
        - type: integer
          format: int64
        - type: number
          format: double
        - type: boolean
...
Generation Details

java -jar .\openapi-generator-cli-7.0.0-beta.jar generate -i .\openapi.yaml -g python -o .\python\

Steps to reproduce
  1. Create a virtual environment python -m venv venv
  2. Install the newly generated api client pip install .\python\
  3. Run the following code:
from openapi_client.api.default_api import DefaultApi
from openapi_client.models.pet import Pet

api = DefaultApi()

if __name__=="__main__":
    pet = Pet(id=123, name="Test")
    api.add_pet(pet)
  1. The following error occurs:
    pydantic.error_wrappers.ValidationError: 1 validation error for Pet
    name
    value is not a valid dict (type=type_error.dict)
Related issues/PRs
Suggest a fix

It seems to me that the client is generated according to the JSON schema specification, however this is useless in the scenario shown above. The old python client python-prior allowed an object to be anything. Personally, I think this should be the desired behavior with the new client as well.

spacether commented 1 year ago

the older generator is still being worked on in a new repo here: https://github.com/openapi-json-schema-tools/openapi-json-schema-generator and it allows AnyType schemas + complex composition

Fyi, you openapi document looks wrong:

    properties:
      name:
        type: object
        description: "A string, a number or a boolean"
        anyOf:
        - type: string
        - type: integer
          format: int64
        - type: number
          format: double
        - type: boolean

--> in a map there is a property called name with a value that is also a map that value must also meet these requirements. One or more of these schemas must validate the map payload:

because the value of name is already type object (which is a map or dict in python) it will never be a string/int/number/boolean and validation of this schema will never pass. If you need the values in the map to be string/int/number/boolean then move your anyOf schema definition like so:

name:
  type: object
  additionalProperties:
    anyOf:
        - type: string
        - type: integer
          format: int64
        - type: number
          format: double
        - type: boolean

If you need name to be those values then remove the type object constraint like so:

name:
  anyOf:
    - type: string
    - type: integer
      format: int64
    - type: number
      format: double
    - type: boolean
Santobert commented 1 year ago

Thank you very much for your answer. Unfortunately, the openapi.yaml is generated by swagger itself (https://github.com/swagger-api/swagger-core). The underlying Java code is as follows:

public class Pet{
    private Long id;

    @Schema(description = "A string, a number or a boolean")
    // anyOf = { String.class, Long.class, Double.class, Boolean.class }
    private Object name;
}

In Java, String, Long, Double, and Boolean are also objects of type object. So the problem boils down to the difference between primitives and objects in Java and Python. Is this something that the Python generator can solve, or is this a swagger issue?

Santobert commented 1 year ago

Additionally, I tested your suggestion with my minimal example: https://gist.github.com/Santobert/0b93ef7233e0dfbbba580203fad5c9ce#file-openapi_suggestion-yaml

It still throws an error:

pydantic.error_wrappers.ValidationError: 1 validation error for Pet
name
  value is not a valid dict (type=type_error.dict)

The generated python code is the same as before:

class PetName(BaseModel):
    """
    A string, a number or a boolean
    """

    # data type: str
    anyof_schema_1_validator: Optional[StrictStr] = None
    # data type: int
    anyof_schema_2_validator: Optional[StrictInt] = None
    # data type: float
    anyof_schema_3_validator: Optional[Union[StrictFloat, StrictInt]] = None
    # data type: bool
    anyof_schema_4_validator: Optional[StrictBool] = None
    actual_instance: Any
    any_of_schemas: List[str] = Field(PETNAME_ANY_OF_SCHEMAS, const=True)
spacether commented 1 year ago

There are multiple issues at play here

How about trying my suggested spec (name is anyOf only with no object constraint) with this generator? https://github.com/openapi-json-schema-tools/openapi-json-schema-generator It will work.

Santobert commented 1 year ago

I've submitted the issue here: https://github.com/swagger-api/swagger-core/issues/4457

Thanks for your help, I'll try the openapi-json-schema-generator soon.