forked from loafle/openapi-generator-original
[python-experimental] Support schema property which has $ref to 'oneOf' schema (#6262)
* Add reference to oneOf schema * Add model showing unit test failure with ref to oneOf schema * Updates get_discriminator_class to return visited_composed_classes * Fixes broken test, adds is_valid_type * move unit test to test_drawing.py file * Add more unit tests * invoke git pull from spacether fork * invoke git pull from spacether fork * Improve unit tests Co-authored-by: Justin Black <justin.a.black@gmail.com>
This commit is contained in:
parent
a5b410993b
commit
1a6cc67fcc
@ -872,6 +872,31 @@ def attempt_convert_item(input_value, valid_classes, path_to_item,
|
|||||||
return input_value
|
return input_value
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_type(input_class_simple, valid_classes):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
input_class_simple (class): the class of the input_value that we are
|
||||||
|
checking
|
||||||
|
valid_classes (tuple): the valid classes that the current item
|
||||||
|
should be
|
||||||
|
Returns:
|
||||||
|
bool
|
||||||
|
"""
|
||||||
|
valid_type = input_class_simple in valid_classes
|
||||||
|
if not valid_type and issubclass(input_class_simple, OpenApiModel):
|
||||||
|
for valid_class in valid_classes:
|
||||||
|
if not valid_class.discriminator:
|
||||||
|
continue
|
||||||
|
discr_propertyname_py = list(valid_class.discriminator.keys())[0]
|
||||||
|
discriminator_classes = (
|
||||||
|
valid_class.discriminator[discr_propertyname_py].values()
|
||||||
|
)
|
||||||
|
valid_type = is_valid_type(input_class_simple, discriminator_classes)
|
||||||
|
if valid_type:
|
||||||
|
return True
|
||||||
|
return valid_type
|
||||||
|
|
||||||
|
|
||||||
def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
|
def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
|
||||||
from_server, _check_type, configuration=None):
|
from_server, _check_type, configuration=None):
|
||||||
"""Raises a TypeError is there is a problem, otherwise returns value
|
"""Raises a TypeError is there is a problem, otherwise returns value
|
||||||
@ -904,7 +929,7 @@ def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
|
|||||||
valid_classes, child_req_types_by_current_type = results
|
valid_classes, child_req_types_by_current_type = results
|
||||||
|
|
||||||
input_class_simple = get_simple_class(input_value)
|
input_class_simple = get_simple_class(input_value)
|
||||||
valid_type = input_class_simple in set(valid_classes)
|
valid_type = is_valid_type(input_class_simple, valid_classes)
|
||||||
if not valid_type:
|
if not valid_type:
|
||||||
if configuration:
|
if configuration:
|
||||||
# if input_value is not valid_type try to convert it
|
# if input_value is not valid_type try to convert it
|
||||||
|
@ -1892,6 +1892,17 @@ components:
|
|||||||
- lengthCm
|
- lengthCm
|
||||||
# go-experimental is unable to make Triangle and Quadrilateral models
|
# go-experimental is unable to make Triangle and Quadrilateral models
|
||||||
# correctly https://github.com/OpenAPITools/openapi-generator/issues/6149
|
# correctly https://github.com/OpenAPITools/openapi-generator/issues/6149
|
||||||
|
Drawing:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
mainShape:
|
||||||
|
# A property whose value is a 'oneOf' type, and the type is referenced instead
|
||||||
|
# of being define inline.
|
||||||
|
$ref: '#/components/schemas/Shape'
|
||||||
|
shapes:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/Shape'
|
||||||
Shape:
|
Shape:
|
||||||
oneOf:
|
oneOf:
|
||||||
- $ref: '#/components/schemas/Triangle'
|
- $ref: '#/components/schemas/Triangle'
|
||||||
|
@ -1138,6 +1138,31 @@ def attempt_convert_item(input_value, valid_classes, path_to_item,
|
|||||||
return input_value
|
return input_value
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_type(input_class_simple, valid_classes):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
input_class_simple (class): the class of the input_value that we are
|
||||||
|
checking
|
||||||
|
valid_classes (tuple): the valid classes that the current item
|
||||||
|
should be
|
||||||
|
Returns:
|
||||||
|
bool
|
||||||
|
"""
|
||||||
|
valid_type = input_class_simple in valid_classes
|
||||||
|
if not valid_type and issubclass(input_class_simple, OpenApiModel):
|
||||||
|
for valid_class in valid_classes:
|
||||||
|
if not valid_class.discriminator:
|
||||||
|
continue
|
||||||
|
discr_propertyname_py = list(valid_class.discriminator.keys())[0]
|
||||||
|
discriminator_classes = (
|
||||||
|
valid_class.discriminator[discr_propertyname_py].values()
|
||||||
|
)
|
||||||
|
valid_type = is_valid_type(input_class_simple, discriminator_classes)
|
||||||
|
if valid_type:
|
||||||
|
return True
|
||||||
|
return valid_type
|
||||||
|
|
||||||
|
|
||||||
def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
|
def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
|
||||||
from_server, _check_type, configuration=None):
|
from_server, _check_type, configuration=None):
|
||||||
"""Raises a TypeError is there is a problem, otherwise returns value
|
"""Raises a TypeError is there is a problem, otherwise returns value
|
||||||
@ -1170,7 +1195,7 @@ def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
|
|||||||
valid_classes, child_req_types_by_current_type = results
|
valid_classes, child_req_types_by_current_type = results
|
||||||
|
|
||||||
input_class_simple = get_simple_class(input_value)
|
input_class_simple = get_simple_class(input_value)
|
||||||
valid_type = input_class_simple in set(valid_classes)
|
valid_type = is_valid_type(input_class_simple, valid_classes)
|
||||||
if not valid_type:
|
if not valid_type:
|
||||||
if configuration:
|
if configuration:
|
||||||
# if input_value is not valid_type try to convert it
|
# if input_value is not valid_type try to convert it
|
||||||
|
@ -147,6 +147,7 @@ Class | Method | HTTP request | Description
|
|||||||
- [complex_quadrilateral.ComplexQuadrilateral](docs/ComplexQuadrilateral.md)
|
- [complex_quadrilateral.ComplexQuadrilateral](docs/ComplexQuadrilateral.md)
|
||||||
- [dog.Dog](docs/Dog.md)
|
- [dog.Dog](docs/Dog.md)
|
||||||
- [dog_all_of.DogAllOf](docs/DogAllOf.md)
|
- [dog_all_of.DogAllOf](docs/DogAllOf.md)
|
||||||
|
- [drawing.Drawing](docs/Drawing.md)
|
||||||
- [enum_arrays.EnumArrays](docs/EnumArrays.md)
|
- [enum_arrays.EnumArrays](docs/EnumArrays.md)
|
||||||
- [enum_class.EnumClass](docs/EnumClass.md)
|
- [enum_class.EnumClass](docs/EnumClass.md)
|
||||||
- [enum_test.EnumTest](docs/EnumTest.md)
|
- [enum_test.EnumTest](docs/EnumTest.md)
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
# drawing.Drawing
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
Name | Type | Description | Notes
|
||||||
|
------------ | ------------- | ------------- | -------------
|
||||||
|
**main_shape** | [**shape.Shape**](Shape.md) | | [optional]
|
||||||
|
**shapes** | [**[shape.Shape]**](Shape.md) | | [optional]
|
||||||
|
|
||||||
|
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||||
|
|
||||||
|
|
@ -65,6 +65,7 @@ from petstore_api.models.client import Client
|
|||||||
from petstore_api.models.complex_quadrilateral import ComplexQuadrilateral
|
from petstore_api.models.complex_quadrilateral import ComplexQuadrilateral
|
||||||
from petstore_api.models.dog import Dog
|
from petstore_api.models.dog import Dog
|
||||||
from petstore_api.models.dog_all_of import DogAllOf
|
from petstore_api.models.dog_all_of import DogAllOf
|
||||||
|
from petstore_api.models.drawing import Drawing
|
||||||
from petstore_api.models.enum_arrays import EnumArrays
|
from petstore_api.models.enum_arrays import EnumArrays
|
||||||
from petstore_api.models.enum_class import EnumClass
|
from petstore_api.models.enum_class import EnumClass
|
||||||
from petstore_api.models.enum_test import EnumTest
|
from petstore_api.models.enum_test import EnumTest
|
||||||
|
@ -1138,6 +1138,31 @@ def attempt_convert_item(input_value, valid_classes, path_to_item,
|
|||||||
return input_value
|
return input_value
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_type(input_class_simple, valid_classes):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
input_class_simple (class): the class of the input_value that we are
|
||||||
|
checking
|
||||||
|
valid_classes (tuple): the valid classes that the current item
|
||||||
|
should be
|
||||||
|
Returns:
|
||||||
|
bool
|
||||||
|
"""
|
||||||
|
valid_type = input_class_simple in valid_classes
|
||||||
|
if not valid_type and issubclass(input_class_simple, OpenApiModel):
|
||||||
|
for valid_class in valid_classes:
|
||||||
|
if not valid_class.discriminator:
|
||||||
|
continue
|
||||||
|
discr_propertyname_py = list(valid_class.discriminator.keys())[0]
|
||||||
|
discriminator_classes = (
|
||||||
|
valid_class.discriminator[discr_propertyname_py].values()
|
||||||
|
)
|
||||||
|
valid_type = is_valid_type(input_class_simple, discriminator_classes)
|
||||||
|
if valid_type:
|
||||||
|
return True
|
||||||
|
return valid_type
|
||||||
|
|
||||||
|
|
||||||
def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
|
def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
|
||||||
from_server, _check_type, configuration=None):
|
from_server, _check_type, configuration=None):
|
||||||
"""Raises a TypeError is there is a problem, otherwise returns value
|
"""Raises a TypeError is there is a problem, otherwise returns value
|
||||||
@ -1170,7 +1195,7 @@ def validate_and_convert_types(input_value, required_types_mixed, path_to_item,
|
|||||||
valid_classes, child_req_types_by_current_type = results
|
valid_classes, child_req_types_by_current_type = results
|
||||||
|
|
||||||
input_class_simple = get_simple_class(input_value)
|
input_class_simple = get_simple_class(input_value)
|
||||||
valid_type = input_class_simple in set(valid_classes)
|
valid_type = is_valid_type(input_class_simple, valid_classes)
|
||||||
if not valid_type:
|
if not valid_type:
|
||||||
if configuration:
|
if configuration:
|
||||||
# if input_value is not valid_type try to convert it
|
# if input_value is not valid_type try to convert it
|
||||||
|
@ -0,0 +1,160 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
OpenAPI Petstore
|
||||||
|
|
||||||
|
This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501
|
||||||
|
|
||||||
|
The version of the OpenAPI document: 1.0.0
|
||||||
|
Generated by: https://openapi-generator.tech
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
import re # noqa: F401
|
||||||
|
import sys # noqa: F401
|
||||||
|
|
||||||
|
import six # noqa: F401
|
||||||
|
import nulltype # noqa: F401
|
||||||
|
|
||||||
|
from petstore_api.model_utils import ( # noqa: F401
|
||||||
|
ModelComposed,
|
||||||
|
ModelNormal,
|
||||||
|
ModelSimple,
|
||||||
|
cached_property,
|
||||||
|
change_keys_js_to_python,
|
||||||
|
convert_js_args_to_python_args,
|
||||||
|
date,
|
||||||
|
datetime,
|
||||||
|
file_type,
|
||||||
|
int,
|
||||||
|
none_type,
|
||||||
|
str,
|
||||||
|
validate_get_composed_info,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
from petstore_api.models import shape
|
||||||
|
except ImportError:
|
||||||
|
shape = sys.modules[
|
||||||
|
'petstore_api.models.shape']
|
||||||
|
|
||||||
|
|
||||||
|
class Drawing(ModelNormal):
|
||||||
|
"""NOTE: This class is auto generated by OpenAPI Generator.
|
||||||
|
Ref: https://openapi-generator.tech
|
||||||
|
|
||||||
|
Do not edit the class manually.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
allowed_values (dict): The key is the tuple path to the attribute
|
||||||
|
and the for var_name this is (var_name,). The value is a dict
|
||||||
|
with a capitalized key describing the allowed value and an allowed
|
||||||
|
value. These dicts store the allowed enum values.
|
||||||
|
attribute_map (dict): The key is attribute name
|
||||||
|
and the value is json key in definition.
|
||||||
|
discriminator_value_class_map (dict): A dict to go from the discriminator
|
||||||
|
variable value to the discriminator class name.
|
||||||
|
validations (dict): The key is the tuple path to the attribute
|
||||||
|
and the for var_name this is (var_name,). The value is a dict
|
||||||
|
that stores validations for max_length, min_length, max_items,
|
||||||
|
min_items, exclusive_maximum, inclusive_maximum, exclusive_minimum,
|
||||||
|
inclusive_minimum, and regex.
|
||||||
|
additional_properties_type (tuple): A tuple of classes accepted
|
||||||
|
as additional properties values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
allowed_values = {
|
||||||
|
}
|
||||||
|
|
||||||
|
validations = {
|
||||||
|
}
|
||||||
|
|
||||||
|
additional_properties_type = None
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def openapi_types():
|
||||||
|
"""
|
||||||
|
This must be a class method so a model may have properties that are
|
||||||
|
of type self, this ensures that we don't create a cyclic import
|
||||||
|
|
||||||
|
Returns
|
||||||
|
openapi_types (dict): The key is attribute name
|
||||||
|
and the value is attribute type.
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
'main_shape': (shape.Shape,), # noqa: E501
|
||||||
|
'shapes': ([shape.Shape],), # noqa: E501
|
||||||
|
}
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def discriminator():
|
||||||
|
return None
|
||||||
|
|
||||||
|
attribute_map = {
|
||||||
|
'main_shape': 'mainShape', # noqa: E501
|
||||||
|
'shapes': 'shapes', # noqa: E501
|
||||||
|
}
|
||||||
|
|
||||||
|
_composed_schemas = {}
|
||||||
|
|
||||||
|
required_properties = set([
|
||||||
|
'_data_store',
|
||||||
|
'_check_type',
|
||||||
|
'_from_server',
|
||||||
|
'_path_to_item',
|
||||||
|
'_configuration',
|
||||||
|
'_visited_composed_classes',
|
||||||
|
])
|
||||||
|
|
||||||
|
@convert_js_args_to_python_args
|
||||||
|
def __init__(self, _check_type=True, _from_server=False, _path_to_item=(), _configuration=None, _visited_composed_classes=(), **kwargs): # noqa: E501
|
||||||
|
"""drawing.Drawing - a model defined in OpenAPI
|
||||||
|
|
||||||
|
Keyword Args:
|
||||||
|
_check_type (bool): if True, values for parameters in openapi_types
|
||||||
|
will be type checked and a TypeError will be
|
||||||
|
raised if the wrong type is input.
|
||||||
|
Defaults to True
|
||||||
|
_path_to_item (tuple/list): This is a list of keys or values to
|
||||||
|
drill down to the model in received_data
|
||||||
|
when deserializing a response
|
||||||
|
_from_server (bool): True if the data is from the server
|
||||||
|
False if the data is from the client (default)
|
||||||
|
_configuration (Configuration): the instance to use when
|
||||||
|
deserializing a file_type parameter.
|
||||||
|
If passed, type conversion is attempted
|
||||||
|
If omitted no type conversion is done.
|
||||||
|
_visited_composed_classes (tuple): This stores a tuple of
|
||||||
|
classes that we have traveled through so that
|
||||||
|
if we see that class again we will not use its
|
||||||
|
discriminator again.
|
||||||
|
When traveling through a discriminator, the
|
||||||
|
composed schema that is
|
||||||
|
is traveled through is added to this set.
|
||||||
|
For example if Animal has a discriminator
|
||||||
|
petType and we pass in "Dog", and the class Dog
|
||||||
|
allOf includes Animal, we move through Animal
|
||||||
|
once using the discriminator, and pick Dog.
|
||||||
|
Then in Dog, we will make an instance of the
|
||||||
|
Animal class but this time we won't travel
|
||||||
|
through its discriminator because we passed in
|
||||||
|
_visited_composed_classes = (Animal,)
|
||||||
|
main_shape (shape.Shape): [optional] # noqa: E501
|
||||||
|
shapes ([shape.Shape]): [optional] # noqa: E501
|
||||||
|
"""
|
||||||
|
|
||||||
|
self._data_store = {}
|
||||||
|
self._check_type = _check_type
|
||||||
|
self._from_server = _from_server
|
||||||
|
self._path_to_item = _path_to_item
|
||||||
|
self._configuration = _configuration
|
||||||
|
self._visited_composed_classes = _visited_composed_classes + (self.__class__,)
|
||||||
|
|
||||||
|
for var_name, var_value in six.iteritems(kwargs):
|
||||||
|
if var_name not in self.attribute_map and \
|
||||||
|
self._configuration is not None and \
|
||||||
|
self._configuration.discard_unknown_keys and \
|
||||||
|
self.additional_properties_type is None:
|
||||||
|
# discard variable.
|
||||||
|
continue
|
||||||
|
setattr(self, var_name, var_value)
|
@ -0,0 +1,68 @@
|
|||||||
|
# coding: utf-8
|
||||||
|
|
||||||
|
"""
|
||||||
|
OpenAPI Petstore
|
||||||
|
|
||||||
|
This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ # noqa: E501
|
||||||
|
|
||||||
|
The version of the OpenAPI document: 1.0.0
|
||||||
|
Generated by: https://openapi-generator.tech
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import petstore_api
|
||||||
|
|
||||||
|
|
||||||
|
class TestDrawing(unittest.TestCase):
|
||||||
|
"""Drawing unit test stubs"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.api_client = petstore_api.ApiClient()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_deserialize_oneof_reference(self):
|
||||||
|
isosceles_triangle = petstore_api.Shape(
|
||||||
|
shape_type="Triangle",
|
||||||
|
triangle_type="IsoscelesTriangle"
|
||||||
|
)
|
||||||
|
assert isinstance(isosceles_triangle, petstore_api.IsoscelesTriangle)
|
||||||
|
inst = petstore_api.Drawing(
|
||||||
|
# 'main_shape' has type 'Shape', which is a oneOf [triangle, quadrilateral]
|
||||||
|
# composed schema. So we should be able to assign a petstore_api.Triangle
|
||||||
|
# to a 'main_shape'.
|
||||||
|
main_shape=isosceles_triangle,
|
||||||
|
shapes=[
|
||||||
|
petstore_api.Shape(
|
||||||
|
shape_type="Triangle",
|
||||||
|
triangle_type="EquilateralTriangle"
|
||||||
|
),
|
||||||
|
petstore_api.Triangle(
|
||||||
|
shape_type="Triangle",
|
||||||
|
triangle_type="IsoscelesTriangle"
|
||||||
|
),
|
||||||
|
petstore_api.EquilateralTriangle(
|
||||||
|
shape_type="Triangle",
|
||||||
|
triangle_type="EquilateralTriangle"
|
||||||
|
),
|
||||||
|
petstore_api.Shape(
|
||||||
|
shape_type="Quadrilateral",
|
||||||
|
quadrilateral_type="ComplexQuadrilateral"
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert isinstance(inst, petstore_api.Drawing)
|
||||||
|
assert isinstance(inst.main_shape, petstore_api.IsoscelesTriangle)
|
||||||
|
self.assertEqual(len(inst.shapes), 4)
|
||||||
|
assert isinstance(inst.shapes[0], petstore_api.EquilateralTriangle)
|
||||||
|
assert isinstance(inst.shapes[1], petstore_api.IsoscelesTriangle)
|
||||||
|
assert isinstance(inst.shapes[2], petstore_api.EquilateralTriangle)
|
||||||
|
assert isinstance(inst.shapes[3], petstore_api.ComplexQuadrilateral)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
Loading…
x
Reference in New Issue
Block a user