From 1b65c5dffc7e946cdca5fae29548e8f87ff96236 Mon Sep 17 00:00:00 2001 From: Herbert Buurman Date: Thu, 6 Feb 2025 17:20:19 +0100 Subject: [PATCH 1/4] Add test model to baseline OpenAPI specs --- end_to_end_tests/baseline_openapi_3.0.json | 9 +++++++++ end_to_end_tests/baseline_openapi_3.1.yaml | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/end_to_end_tests/baseline_openapi_3.0.json b/end_to_end_tests/baseline_openapi_3.0.json index 4afeabc1d..6d700d820 100644 --- a/end_to_end_tests/baseline_openapi_3.0.json +++ b/end_to_end_tests/baseline_openapi_3.0.json @@ -2563,6 +2563,15 @@ } } }, + "AModelWithOptionalPropertyRefAndDefaultValue": { + "type": "object", + "properties": { + "enum_property_ref": { + "$ref": "#/components/schemas/AnEnum", + "default": "FIRST_VALUE" + } + } + }, "ModelWithDateTimeProperty": { "type": "object", "properties": { diff --git a/end_to_end_tests/baseline_openapi_3.1.yaml b/end_to_end_tests/baseline_openapi_3.1.yaml index caddda8eb..2df31f326 100644 --- a/end_to_end_tests/baseline_openapi_3.1.yaml +++ b/end_to_end_tests/baseline_openapi_3.1.yaml @@ -2555,6 +2555,15 @@ info: } } }, + "AModelWithOptionalPropertyRefAndDefaultValue": { + "type": "object", + "properties": { + "enum_property_ref": { + "$ref": "#/components/schemas/AnEnum", + "default": "FIRST_VALUE" + } + } + }, "ModelWithDateTimeProperty": { "type": "object", "properties": { From b013093c585995498a1f1eb13e8aa7e9752c6885 Mon Sep 17 00:00:00 2001 From: Herbert Buurman Date: Thu, 6 Feb 2025 17:20:46 +0100 Subject: [PATCH 2/4] Add desired/correct generated code to golden-record --- .../my_test_api_client/models/__init__.py | 2 + ...optional_property_ref_and_default_value.py | 67 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_optional_property_ref_and_default_value.py diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py index f354c31c7..1e02445bb 100644 --- a/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py +++ b/end_to_end_tests/golden-record/my_test_api_client/models/__init__.py @@ -4,6 +4,7 @@ from .a_discriminated_union_type_2 import ADiscriminatedUnionType2 from .a_form_data import AFormData from .a_model import AModel +from .a_model_with_optional_property_ref_and_default_value import AModelWithOptionalPropertyRefAndDefaultValue from .a_model_with_properties_reference_that_are_not_object import AModelWithPropertiesReferenceThatAreNotObject from .all_of_has_properties_but_no_type import AllOfHasPropertiesButNoType from .all_of_has_properties_but_no_type_type_enum import AllOfHasPropertiesButNoTypeTypeEnum @@ -98,6 +99,7 @@ "AllOfSubModel", "AllOfSubModelTypeEnum", "AModel", + "AModelWithOptionalPropertyRefAndDefaultValue", "AModelWithPropertiesReferenceThatAreNotObject", "AnAllOfEnum", "AnArrayWithACircularRefInItemsObjectAdditionalPropertiesAItem", diff --git a/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_optional_property_ref_and_default_value.py b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_optional_property_ref_and_default_value.py new file mode 100644 index 000000000..2fc078143 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/models/a_model_with_optional_property_ref_and_default_value.py @@ -0,0 +1,67 @@ +from typing import Any, TypeVar, Union + +from attrs import define as _attrs_define +from attrs import field as _attrs_field + +from ..models.an_enum import AnEnum +from ..types import UNSET, Unset + +T = TypeVar("T", bound="AModelWithOptionalPropertyRefAndDefaultValue") + + +@_attrs_define +class AModelWithOptionalPropertyRefAndDefaultValue: + """ + Attributes: + enum_property_ref (Union[Unset, AnEnum]): For testing Enums in all the ways they can be used Default: + AnEnum.FIRST_VALUE. + """ + + enum_property_ref: Union[Unset, AnEnum] = AnEnum.FIRST_VALUE + additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + + def to_dict(self) -> dict[str, Any]: + enum_property_ref: Union[Unset, str] = UNSET + if not isinstance(self.enum_property_ref, Unset): + enum_property_ref = self.enum_property_ref.value + + field_dict: dict[str, Any] = {} + field_dict.update(self.additional_properties) + field_dict.update({}) + if enum_property_ref is not UNSET: + field_dict["enum_property_ref"] = enum_property_ref + + return field_dict + + @classmethod + def from_dict(cls: type[T], src_dict: dict[str, Any]) -> T: + d = src_dict.copy() + _enum_property_ref = d.pop("enum_property_ref", UNSET) + enum_property_ref: Union[Unset, AnEnum] + if isinstance(_enum_property_ref, Unset): + enum_property_ref = UNSET + else: + enum_property_ref = AnEnum(_enum_property_ref) + + a_model_with_optional_property_ref_and_default_value = cls( + enum_property_ref=enum_property_ref, + ) + + a_model_with_optional_property_ref_and_default_value.additional_properties = d + return a_model_with_optional_property_ref_and_default_value + + @property + def additional_keys(self) -> list[str]: + return list(self.additional_properties.keys()) + + def __getitem__(self, key: str) -> Any: + return self.additional_properties[key] + + def __setitem__(self, key: str, value: Any) -> None: + self.additional_properties[key] = value + + def __delitem__(self, key: str) -> None: + del self.additional_properties[key] + + def __contains__(self, key: str) -> bool: + return key in self.additional_properties From 1dd1efebb9486b17ac393e11450307d50319570e Mon Sep 17 00:00:00 2001 From: Herbert Buurman Date: Fri, 7 Feb 2025 10:45:58 +0100 Subject: [PATCH 3/4] Add processing of object default if present, fallback to old behavior if absent --- .../parser/properties/__init__.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index ba667347b..e156a183b 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -120,10 +120,18 @@ def _property_from_ref( schemas, ) - default = existing.convert_value(parent.default) if parent is not None else None - if isinstance(default, PropertyError): - default.data = parent or data - return default, schemas + if hasattr(data, 'default'): + default = existing.convert_value(data.default) + if isinstance(default, PropertyError): + default.data = data + return default, schemas + elif parent is not None: + default = existing.convert_value(parent.default) + if isinstance(default, PropertyError): + default.data = parent + return default, schemas + else: + default = None prop = evolve( existing, From 5d3440371db20503cf4980a789c6f013ba057da4 Mon Sep 17 00:00:00 2001 From: Herbert Buurman Date: Fri, 7 Feb 2025 11:13:26 +0100 Subject: [PATCH 4/4] Add changeset documentation --- .changeset/use_default_value_for_ref_property.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/use_default_value_for_ref_property.md diff --git a/.changeset/use_default_value_for_ref_property.md b/.changeset/use_default_value_for_ref_property.md new file mode 100644 index 000000000..6acb5659d --- /dev/null +++ b/.changeset/use_default_value_for_ref_property.md @@ -0,0 +1,9 @@ +--- +default: patch +--- + +# Use default value for ref property + +#1202 by @github-abcde + +Use defined default value for referenced properties of a model instead of always defaulting to `UNSET` in the generated code.