datamodel-code-generator icon indicating copy to clipboard operation
datamodel-code-generator copied to clipboard

allOf with a single $ref with a local json-pointer is resolved in the importing schema

Open simontaurus opened this issue 1 year ago • 0 comments

Describe the bug allOf with a single $ref with a local json-pointer in schemaA.json is not resolved correctly with schemaA.json is itself referenced from schemaB.json. It seems like schemaB.json (where no definitions exist) is passed as self.raw_obj to get_model_by_path() but it should be schemaB.json. get_model_by_path() tries to look up the path but fails. https://github.com/koxudaxi/datamodel-code-generator/blob/222f71834296e36ec1b013a65344b2adb40b7cf7/datamodel_code_generator/parser/jsonschema.py#L871

To Reproduce

schemaA

{
    "definitions": {
        "generated": {
            "type": "object",
            "title": "SchemaAGenerated",
            "properties": {
                "test": {
                    "title": "test",
                    "type": "string"
                }
            }
        }
    },
    "title": "SchemaA",
    "allOf": [
        {
            "$ref": "#/definitions/generated"
        }
    ]
}

schemaB.json

{
    "allOf": [
        {
            "$ref": "SchemaA.json"
        }
    ],
    "type": "object",
    "title": "SchemaB"
}

Used commandline:

$ datamodel-codegen --input schemaB.json --output model.py --use-title-as-name

Error:

  File ".venv\lib\site-packages\datamodel_code_generator\parser\jsonschema.py", line 1807, in _parse_file
    self.parse_obj(obj_name, root_obj, path_parts or ['#'])
  File ".venv\lib\site-packages\datamodel_code_generator\parser\jsonschema.py", line 1667, in parse_obj
    self.parse_all_of(name, obj, path)
  File ".venv\lib\site-packages\datamodel_code_generator\parser\jsonschema.py", line 871, in parse_all_of
    if get_model_by_path(self.raw_obj, single_obj.ref[2:].split('/')).get(
  File ".venv\lib\site-packages\datamodel_code_generator\parser\jsonschema.py", line 87, in get_model_by_path
    model = get_model_by_path(schema[keys[0]], keys[1:])  # type: ignore
KeyError: 'definitions'

Expected behavior No exception and the following model.py

class SchemaAGenerated(BaseModel):
    test: Optional[str] = Field(
        None, title="test"
    )

class SchemaA(SchemaAGenerated):
    pass

class SchemaB(SchemaA):
    pass

or, even better, with the empty class SchemaA merged with SchemaAGenerated

class SchemaA(BaseModel):
    test: Optional[str] = Field(
        None, title="test"
    )

class SchemaB(SchemaA):
    pass

Workarounds: replacing

    "allOf": [
        {
            "$ref": "#/definitions/generated"
        }
    ]

with

"$ref": "#/definitions/generated"

works, but produces

class SchemaAGenerated(BaseModel):
    test: Optional[str] = Field(None, title='test')

class SchemaA(BaseModel):
    __root__: SchemaAGenerated = Field(..., title='SchemaA')

class SchemaB(BaseModel):
    pass

even with --collapse-root-models.

Having an array of two $refs in allOf also works as expected:

    "allOf": [
        {
            "$ref": "#/definitions/generated"
        },
        {
            
        }
    ]

Version:

  • OS: Windows 10
  • Python version: 3.11 | 3.9
  • datamodel-code-generator version: 0.25.6 | 0.26.0

simontaurus avatar Sep 08 '24 13:09 simontaurus