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

Incorrect parsing of null as nullable enum value serialized as strings

Open kunom opened this issue 9 months ago • 2 comments

Consider the following schema definition (which passes validation with the openapitools/openapi-generator-cli container image):

// [...]
          "deviceEncryptionKeyScheme": {
            "$ref": "#/components/schemas/DeviceEncryptionKeyScheme",
            "nullable": true  // <-- nullability declared
          },

// [...]
      "DeviceEncryptionKeyScheme": {
        "enum": ["Default8"],
        "type": "string"
      },

It looks as if openapi-python-client version 0.23.1 supports deserialization with the deviceEncryptionKeyScheme property being unset, but not it being null:

        _device_encryption_key_scheme = d.pop("deviceEncryptionKeyScheme", UNSET)
        device_encryption_key_scheme: Union[Unset, DeviceEncryptionKeyScheme]
        if isinstance(_device_encryption_key_scheme, Unset):
            device_encryption_key_scheme = UNSET
        else:
            device_encryption_key_scheme = DeviceEncryptionKeyScheme(_device_encryption_key_scheme)

This looks like a bug to me.

kunom avatar Apr 09 '25 13:04 kunom

It looks like you're using OpenAPI 3.0, which explicitly states

This object cannot be extended with additional properties, and any properties added SHALL be ignored.

I believe the solution is to use allOf containing the ref and null.

dbanty avatar Apr 09 '25 15:04 dbanty

Not sure if exactly the same, but i'm encountering a similar issue when updating from 0.24.0 to 0.24.1 or higher. The specification property is indicated as nullable, but the code cannot handle this and will crash on d = dict(src_dict) in SubVariantDto when src_dict is None .

{
  "x-generator": "NSwag v14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))",
  "openapi": "3.0.0",
  ...
        "properties": {
          ...
          "specification": {
            "nullable": true,
            "$ref": "#/components/schemas/SubVariantDto"
          },
         ...
        }
    @classmethod
    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
        ...

        d = dict(src_dict)

        ...

        _specification = d.pop("specification", UNSET) # <-- can be None
        specification: Union[Unset, SubVariantDto]
        if isinstance(_specification, Unset):
            specification = UNSET
        else:
            specification = SubVariantDto.from_dict(_specification)

        node_dto = cls(
            ...,
            specification=specification,
            ...
        )

        return node_dto 
@_attrs_define
class SubVariantDto:

    ...

    @classmethod
    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
        d = dict(src_dict) # <-- crashes on None is not iterable

        ...

In 0.24.0 or earlier the generated code would give the block below, which would not trigger the issue.

    @classmethod
    def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T:
        ...

        d = src_dict.copy()
        
        ...

        specification = d.pop("specification", UNSET)

        ...

        node_dto = cls(
            ...
            specification=specification,
            ...
        )

        return node_dto 

alexg-of avatar Apr 10 '25 12:04 alexg-of