OpenAPITools / OpenAPITools/openapi-generator

[PYTHON]: Bug or not bug ? Using allOf for inheritance generate bad imports

Open
#18,966 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Enhancement: Feature
Dominant language
Java
Stars
26.8k
Forks
7.7k
PR merge metrics
PR metrics pending

Description

Hi,
I'm not sure what i will describe is a bug; I prefer to give you some clues and have a little chat before openning a bug.

Context:
I fetch a swagger generated from one of my colleagues who is using Swashbuckle and the inheritance for the Dog schema use the following syntax:

openapi: 3.0.1
info:
  title: title
  version: 1.0.0
paths: {}
components:
  schemas:
    Dog:
      type: object
      allOf:
        - $ref: '#/components/schemas/Animal'
      properties:
        food_eaten:
          type: array
          items:
            oneOf:
              - $ref: '#/components/schemas/Treat'

    Animal:
      type: object

    Treat:
      type: object

When generate python flask server, the imports are kinda broken.
docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli:v7.6.0 generate -i /local/test.yaml -g python-flask -o /local/dist

from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model import Model
from openapi_server.models.one_ofobject import OneOfobject
from openapi_server import util

from openapi_server.models.one_ofobject import OneOfobject  # noqa: E501

class Dog(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, food_eaten=None):  # noqa: E501
        """Dog - a model defined in OpenAPI

        :param food_eaten: The food_eaten of this Dog.  # noqa: E501
        :type food_eaten: List[OneOfobject]
        """
        self.openapi_types = {
            'food_eaten': List[OneOfobject]
        }

        self.attribute_map = {
            'food_eaten': 'food_eaten'
        }

        self._food_eaten = food_eaten

    @classmethod
    def from_dict(cls, dikt) -> 'Dog':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Dog of this Dog.  # noqa: E501
        :rtype: Dog
        """
        return util.deserialize_model(dikt, cls)

    @property
    def food_eaten(self) -> List[OneOfobject]:
        """Gets the food_eaten of this Dog.


        :return: The food_eaten of this Dog.
        :rtype: List[OneOfobject]
        """
        return self._food_eaten

    @food_eaten.setter
    def food_eaten(self, food_eaten: List[OneOfobject]):
        """Sets the food_eaten of this Dog.


        :param food_eaten: The food_eaten of this Dog.
        :type food_eaten: List[OneOfobject]
        """

        self._food_eaten = food_eaten

I saw on swagger.io that the syntax of allOf should be the following one:

openapi: 3.0.1
info:
  title: title
  version: 1.0.0
paths: {}
components:
  schemas:
    Dog:
      type: object
      allOf:
        - $ref: '#/components/schemas/Animal'
        - type: object
          properties:
            food_eaten:
              type: array
              items:
                oneOf:
                  - $ref: '#/components/schemas/Treat'

    Animal:
      type: object

    Treat:
      type: object

When i'm using this one, the imports are correct (working):

from datetime import date, datetime  # noqa: F401

from typing import List, Dict  # noqa: F401

from openapi_server.models.base_model import Model
from openapi_server.models.dog_all_of_food_eaten import DogAllOfFoodEaten
from openapi_server import util

from openapi_server.models.dog_all_of_food_eaten import DogAllOfFoodEaten  # noqa: E501

class Dog(Model):
    """NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).

    Do not edit the class manually.
    """

    def __init__(self, food_eaten=None):  # noqa: E501
        """Dog - a model defined in OpenAPI

        :param food_eaten: The food_eaten of this Dog.  # noqa: E501
        :type food_eaten: List[DogAllOfFoodEaten]
        """
        self.openapi_types = {
            'food_eaten': List[DogAllOfFoodEaten]
        }

        self.attribute_map = {
            'food_eaten': 'food_eaten'
        }

        self._food_eaten = food_eaten

    @classmethod
    def from_dict(cls, dikt) -> 'Dog':
        """Returns the dict as a model

        :param dikt: A dict.
        :type: dict
        :return: The Dog of this Dog.  # noqa: E501
        :rtype: Dog
        """
        return util.deserialize_model(dikt, cls)

    @property
    def food_eaten(self) -> List[DogAllOfFoodEaten]:
        """Gets the food_eaten of this Dog.


        :return: The food_eaten of this Dog.
        :rtype: List[DogAllOfFoodEaten]
        """
        return self._food_eaten

    @food_eaten.setter
    def food_eaten(self, food_eaten: List[DogAllOfFoodEaten]):
        """Sets the food_eaten of this Dog.


        :param food_eaten: The food_eaten of this Dog.
        :type food_eaten: List[DogAllOfFoodEaten]
        """

        self._food_eaten = food_eaten
  1. Why the first syntax break the imports ? Is the syntax wrong ?
  2. When im using the second syntax, i dont understand why it is generating a DogAllOfFoodEaten.

Actual:

        self.openapi_types = {
            'food_eaten': List[DogAllOfFoodEaten]
        }

Expected:

        self.openapi_types = {
            'food_eaten': List[Treat | DogBiscuit | SomethingElse]
        }

Thanks for all you have done so far. I really appreciate it.
Have a good day.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the test.yaml schema and reproduce the output using the documented openapi-generator-cli python-flask command. Compare generated imports and model types for the two allOf forms, then inspect the generator behavior that resolves oneOf inside array items. Done means the valid schema form produces resolvable imports and the generated type reflects the referenced schemas without invalid names.

Written by the indexing model from the issue text.

Assessment

Tech stack
flask, openapi, python
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.