Skip to content

[BUG] [Python] OneOf objects can't be created if they don't have required properties #22104

@fbpo23

Description

@fbpo23

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

When using a oneOf, the class generated for that property will have methods to create an instance from a dict or json. When using it, it will try to create an object for each class in the oneOf, and will give an error if either no objects could be generated or if two or more objects were generated.
My issue is that this causes a problem when inside the oneOf there are objects with no required properties, because any single dictionary will pass the validations of each oneOf object, resulting in an error since two or more objects were generated. I have additionalProperties set as True for both oneOf objects, but the current implementation of the generated classes still allows the validation to pass even if the dict object has fields that have no correspondence in that class.

openapi-generator version

7.16.0

OpenAPI declaration file content or url
components:
  schemas:
    foo:
      oneOf:
        - title: Bar1
          additionalProperties: false
          properties:
            bar1:
              type: string
          type: object
        - title: Bar2
          additionalProperties: false
          properties:
            bar2:
              type: string
          type: object
info:
  title: Title
  version: 1.0.0
openapi: 3.0.4
paths: {}
Generation Details

Command:
openapi-generator-cli generate -i api.yaml -g python --additional-properties=generateSourceCodeOnly=true --package-name generated-api

Steps to reproduce
  • Save the yaml above in a file called api.yaml
  • Run the command above to generate the code
  • Run the following python code to observe the issues (you might need to install pydantic):
from generated_api.models.bar2 import Bar2
from generated_api.models.foo import Foo

try:
    Bar2.from_json('{"bar1": "bar"}')
    print("BUG: Bar2 accepted unknown field!")
except Exception as e:
    print(f"Expected: {e}")

try:
    foo = Foo.from_json('{"bar2": "bar"}')
    print(f"Success: Foo is of type {type(foo)}")
except Exception as e:
    print(f"BUG: {e}")

And the output should be something like:

BUG: FooOneOf accepted unknown field!
BUG: Multiple matches found when deserializing the JSON string into Foo with oneOf schemas: Bar1, Bar2. Details: 
Suggest a fix

The code for the oneOf classes to create an object from a dict is the following:

@classmethod
    def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
        """Create an instance of Bar2 from a dict"""
        if obj is None:
            return None

        if not isinstance(obj, dict):
            return cls.model_validate(obj)

        _obj = cls.model_validate({
            "bar2": obj.get("bar2")
        })
        return _obj

I tested changing it to

@classmethod
    def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
        """Create an instance of Bar2 from a dict"""
        if obj is None:
            return None

        return cls.model_validate(obj)

As well as adding extra="forbid" to the model_config.

The two changes together fixed my issues. The problem with the original function is that it only fetches the correct fields from each object, which would ignore the extra fields, guaranteeing the creation of a correct object, but removing its function to validate the object.
Adding extra="forbid" here is essential, especially because I have additionalProperties as True in the api.yaml

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions