From 60dcf8613f6b75b606757c4a991fe018ad10ee99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Herv=C3=A9?= Date: Wed, 14 Apr 2021 20:43:28 +0200 Subject: [PATCH] Don't include read-only properties in Python examples. (#9252) * Don't include read-only properties in Python examples. When using a schema with read-only fields as API inputs, Python generates examples for those: this excludes them. * Fix tests --- .../languages/PythonClientCodegen.java | 17 ++ ...odels-for-testing-with-http-signature.yaml | 8 + .../petstore/python/.openapi-generator/FILES | 2 + .../openapi3/client/petstore/python/README.md | 1 + .../python/docs/ObjectModelWithRefProps.md | 1 + .../client/petstore/python/docs/Readonly.md | 11 ++ .../model/object_model_with_ref_props.py | 5 + .../python/petstore_api/model/readonly.py | 166 ++++++++++++++++++ .../python/petstore_api/models/__init__.py | 1 + .../petstore/python/test/test_readonly.py | 35 ++++ .../test_object_model_with_ref_props.py | 2 + 11 files changed, 249 insertions(+) create mode 100644 samples/openapi3/client/petstore/python/docs/Readonly.md create mode 100644 samples/openapi3/client/petstore/python/petstore_api/model/readonly.py create mode 100644 samples/openapi3/client/petstore/python/test/test_readonly.py diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java index 3b0b5deeb81..87811995525 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java @@ -1223,6 +1223,23 @@ public class PythonClientCodegen extends PythonLegacyClientCodegen { for (Map.Entry entry : requiredAndOptionalProps.entrySet()) { String propName = entry.getKey(); Schema propSchema = entry.getValue(); + boolean readOnly = false; + if (propSchema.getReadOnly() != null) { + readOnly = propSchema.getReadOnly(); + } + if (readOnly) { + continue; + } + String ref = propSchema.get$ref(); + if (ref != null) { + Schema refSchema = ModelUtils.getSchema(this.openAPI, ModelUtils.getSimpleRef(ref)); + if (refSchema.getReadOnly() != null) { + readOnly = refSchema.getReadOnly(); + } + if (readOnly) { + continue; + } + } propName = toVarName(propName); String propModelName = null; Object propExample = null; diff --git a/modules/openapi-generator/src/test/resources/3_0/python/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml b/modules/openapi-generator/src/test/resources/3_0/python/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml index 9e6502ca756..3c77711d37c 100644 --- a/modules/openapi-generator/src/test/resources/3_0/python/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/python/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml @@ -2002,6 +2002,8 @@ components: properties: my_number: $ref: '#/definitions/NumberWithValidations' + my_readonly: + $ref: '#/definitions/Readonly' my_string: $ref: '#/definitions/String' my_boolean: @@ -2010,6 +2012,12 @@ components: type: number minimum: 10 maximum: 20 + Readonly: + type: object + readOnly: true + properties: + name: + type: string ComposedOneOfNumberWithValidations: description: this is a model that allows payloads of type object or number oneOf: diff --git a/samples/openapi3/client/petstore/python/.openapi-generator/FILES b/samples/openapi3/client/petstore/python/.openapi-generator/FILES index 6a324e4e94a..e3b9c0feabb 100644 --- a/samples/openapi3/client/petstore/python/.openapi-generator/FILES +++ b/samples/openapi3/client/petstore/python/.openapi-generator/FILES @@ -79,6 +79,7 @@ docs/Pig.md docs/Quadrilateral.md docs/QuadrilateralInterface.md docs/ReadOnlyFirst.md +docs/Readonly.md docs/ScaleneTriangle.md docs/Shape.md docs/ShapeInterface.md @@ -185,6 +186,7 @@ petstore_api/model/pig.py petstore_api/model/quadrilateral.py petstore_api/model/quadrilateral_interface.py petstore_api/model/read_only_first.py +petstore_api/model/readonly.py petstore_api/model/scalene_triangle.py petstore_api/model/shape.py petstore_api/model/shape_interface.py diff --git a/samples/openapi3/client/petstore/python/README.md b/samples/openapi3/client/petstore/python/README.md index afe16c9ab23..2fc4c6b58bd 100644 --- a/samples/openapi3/client/petstore/python/README.md +++ b/samples/openapi3/client/petstore/python/README.md @@ -206,6 +206,7 @@ Class | Method | HTTP request | Description - [Quadrilateral](docs/Quadrilateral.md) - [QuadrilateralInterface](docs/QuadrilateralInterface.md) - [ReadOnlyFirst](docs/ReadOnlyFirst.md) + - [Readonly](docs/Readonly.md) - [ScaleneTriangle](docs/ScaleneTriangle.md) - [Shape](docs/Shape.md) - [ShapeInterface](docs/ShapeInterface.md) diff --git a/samples/openapi3/client/petstore/python/docs/ObjectModelWithRefProps.md b/samples/openapi3/client/petstore/python/docs/ObjectModelWithRefProps.md index 5ff4e52033d..a046561314c 100644 --- a/samples/openapi3/client/petstore/python/docs/ObjectModelWithRefProps.md +++ b/samples/openapi3/client/petstore/python/docs/ObjectModelWithRefProps.md @@ -6,6 +6,7 @@ a model that includes properties which should stay primitive (String + Boolean) Name | Type | Description | Notes ------------ | ------------- | ------------- | ------------- **my_number** | [**NumberWithValidations**](NumberWithValidations.md) | | [optional] +**my_readonly** | [**Readonly**](Readonly.md) | | [optional] **my_string** | **str** | | [optional] **my_boolean** | **bool** | | [optional] diff --git a/samples/openapi3/client/petstore/python/docs/Readonly.md b/samples/openapi3/client/petstore/python/docs/Readonly.md new file mode 100644 index 00000000000..4f4ba8ede25 --- /dev/null +++ b/samples/openapi3/client/petstore/python/docs/Readonly.md @@ -0,0 +1,11 @@ +# Readonly + + +## Properties +Name | Type | Description | Notes +------------ | ------------- | ------------- | ------------- +**name** | **str** | | [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) + + diff --git a/samples/openapi3/client/petstore/python/petstore_api/model/object_model_with_ref_props.py b/samples/openapi3/client/petstore/python/petstore_api/model/object_model_with_ref_props.py index b1dc4bf82e2..ee3613c4444 100644 --- a/samples/openapi3/client/petstore/python/petstore_api/model/object_model_with_ref_props.py +++ b/samples/openapi3/client/petstore/python/petstore_api/model/object_model_with_ref_props.py @@ -28,7 +28,9 @@ from petstore_api.model_utils import ( # noqa: F401 def lazy_import(): from petstore_api.model.number_with_validations import NumberWithValidations + from petstore_api.model.readonly import Readonly globals()['NumberWithValidations'] = NumberWithValidations + globals()['Readonly'] = Readonly class ObjectModelWithRefProps(ModelNormal): @@ -78,6 +80,7 @@ class ObjectModelWithRefProps(ModelNormal): lazy_import() return { 'my_number': (NumberWithValidations,), # noqa: E501 + 'my_readonly': (Readonly,), # noqa: E501 'my_string': (str,), # noqa: E501 'my_boolean': (bool,), # noqa: E501 } @@ -89,6 +92,7 @@ class ObjectModelWithRefProps(ModelNormal): attribute_map = { 'my_number': 'my_number', # noqa: E501 + 'my_readonly': 'my_readonly', # noqa: E501 'my_string': 'my_string', # noqa: E501 'my_boolean': 'my_boolean', # noqa: E501 } @@ -140,6 +144,7 @@ class ObjectModelWithRefProps(ModelNormal): through its discriminator because we passed in _visited_composed_classes = (Animal,) my_number (NumberWithValidations): [optional] # noqa: E501 + my_readonly (Readonly): [optional] # noqa: E501 my_string (str): [optional] # noqa: E501 my_boolean (bool): [optional] # noqa: E501 """ diff --git a/samples/openapi3/client/petstore/python/petstore_api/model/readonly.py b/samples/openapi3/client/petstore/python/petstore_api/model/readonly.py new file mode 100644 index 00000000000..b5d137b467d --- /dev/null +++ b/samples/openapi3/client/petstore/python/petstore_api/model/readonly.py @@ -0,0 +1,166 @@ +""" + 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 +""" + + +import re # noqa: F401 +import sys # noqa: F401 + +from petstore_api.model_utils import ( # noqa: F401 + ApiTypeError, + ModelComposed, + ModelNormal, + ModelSimple, + cached_property, + change_keys_js_to_python, + convert_js_args_to_python_args, + date, + datetime, + file_type, + none_type, + validate_get_composed_info, +) + + +class Readonly(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 + + _nullable = False + + @cached_property + def openapi_types(): + """ + This must be a method because a model may have properties that are + of type self, this must run after the class is loaded + + Returns + openapi_types (dict): The key is attribute name + and the value is attribute type. + """ + return { + 'name': (str,), # noqa: E501 + } + + @cached_property + def discriminator(): + return None + + + attribute_map = { + 'name': 'name', # noqa: E501 + } + + _composed_schemas = {} + + required_properties = set([ + '_data_store', + '_check_type', + '_spec_property_naming', + '_path_to_item', + '_configuration', + '_visited_composed_classes', + ]) + + @convert_js_args_to_python_args + def __init__(self, *args, **kwargs): # noqa: E501 + """Readonly - 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 + _spec_property_naming (bool): True if the variable names in the input data + are serialized names, as specified in the OpenAPI document. + False if the variable names in the input data + are pythonic names, e.g. snake case (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,) + name (str): [optional] # noqa: E501 + """ + + _check_type = kwargs.pop('_check_type', True) + _spec_property_naming = kwargs.pop('_spec_property_naming', False) + _path_to_item = kwargs.pop('_path_to_item', ()) + _configuration = kwargs.pop('_configuration', None) + _visited_composed_classes = kwargs.pop('_visited_composed_classes', ()) + + if args: + raise ApiTypeError( + "Invalid positional arguments=%s passed to %s. Remove those invalid positional arguments." % ( + args, + self.__class__.__name__, + ), + path_to_item=_path_to_item, + valid_classes=(self.__class__,), + ) + + self._data_store = {} + self._check_type = _check_type + self._spec_property_naming = _spec_property_naming + 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 kwargs.items(): + 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) diff --git a/samples/openapi3/client/petstore/python/petstore_api/models/__init__.py b/samples/openapi3/client/petstore/python/petstore_api/models/__init__.py index 5875dbb55cf..12f8d86da03 100644 --- a/samples/openapi3/client/petstore/python/petstore_api/models/__init__.py +++ b/samples/openapi3/client/petstore/python/petstore_api/models/__init__.py @@ -81,6 +81,7 @@ from petstore_api.model.pig import Pig from petstore_api.model.quadrilateral import Quadrilateral from petstore_api.model.quadrilateral_interface import QuadrilateralInterface from petstore_api.model.read_only_first import ReadOnlyFirst +from petstore_api.model.readonly import Readonly from petstore_api.model.scalene_triangle import ScaleneTriangle from petstore_api.model.shape import Shape from petstore_api.model.shape_interface import ShapeInterface diff --git a/samples/openapi3/client/petstore/python/test/test_readonly.py b/samples/openapi3/client/petstore/python/test/test_readonly.py new file mode 100644 index 00000000000..50be60d4e44 --- /dev/null +++ b/samples/openapi3/client/petstore/python/test/test_readonly.py @@ -0,0 +1,35 @@ +""" + 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 +""" + + +import sys +import unittest + +import petstore_api +from petstore_api.model.readonly import Readonly + + +class TestReadonly(unittest.TestCase): + """Readonly unit test stubs""" + + def setUp(self): + pass + + def tearDown(self): + pass + + def testReadonly(self): + """Test Readonly""" + # FIXME: construct object with mandatory attributes with example values + # model = Readonly() # noqa: E501 + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/samples/openapi3/client/petstore/python/tests_manual/test_object_model_with_ref_props.py b/samples/openapi3/client/petstore/python/tests_manual/test_object_model_with_ref_props.py index 9ce55c75bb8..c708237acb4 100644 --- a/samples/openapi3/client/petstore/python/tests_manual/test_object_model_with_ref_props.py +++ b/samples/openapi3/client/petstore/python/tests_manual/test_object_model_with_ref_props.py @@ -20,6 +20,7 @@ except ImportError: number_with_validations = sys.modules[ 'petstore_api.model.number_with_validations'] from petstore_api.model.object_model_with_ref_props import ObjectModelWithRefProps +from petstore_api.model.readonly import Readonly class TestObjectModelWithRefProps(unittest.TestCase): @@ -38,6 +39,7 @@ class TestObjectModelWithRefProps(unittest.TestCase): ObjectModelWithRefProps.openapi_types, { 'my_number': (NumberWithValidations,), + 'my_readonly': (Readonly,), 'my_string': (str,), 'my_boolean': (bool,), }