diff --git a/.generator/src/generator/templates/model_utils.j2 b/.generator/src/generator/templates/model_utils.j2 index b0d701eaab..ad1e3b0758 100644 --- a/.generator/src/generator/templates/model_utils.j2 +++ b/.generator/src/generator/templates/model_utils.j2 @@ -565,6 +565,8 @@ class ModelComposed(OpenApiModel): def get_oneof_instance(self): """Returns the oneOf instance""" + if not self._composed_instances: + return self return self._composed_instances[0] def __eq__(self, other): @@ -1481,7 +1483,7 @@ def model_to_dict(model_instance, serialize=True): model_instances = [model_instance] model = model_instance - while model._composed_schemas: + while model._composed_schemas and model._composed_instances: model_instances.extend(model._composed_instances) model = model.get_oneof_instance() diff --git a/src/datadog_api_client/model_utils.py b/src/datadog_api_client/model_utils.py index 9f508e1f57..57f159de59 100644 --- a/src/datadog_api_client/model_utils.py +++ b/src/datadog_api_client/model_utils.py @@ -578,6 +578,8 @@ def to_str(self): def get_oneof_instance(self): """Returns the oneOf instance""" + if not self._composed_instances: + return self return self._composed_instances[0] def __eq__(self, other): @@ -1497,7 +1499,7 @@ def model_to_dict(model_instance, serialize=True): model_instances = [model_instance] model = model_instance - while model._composed_schemas: + while model._composed_schemas and model._composed_instances: model_instances.extend(model._composed_instances) model = model.get_oneof_instance() diff --git a/tests/test_deserialization.py b/tests/test_deserialization.py index 503bc0bdc4..9bf1b93033 100644 --- a/tests/test_deserialization.py +++ b/tests/test_deserialization.py @@ -12,6 +12,7 @@ from datadog_api_client.v2.model.logs_aggregate_response import LogsAggregateResponse from datadog_api_client.v2.model.logs_archive import LogsArchive from datadog_api_client.v2.model.logs_archive_destination import LogsArchiveDestination +from datadog_api_client.v1.model.dashboard import Dashboard from datadog_api_client.v2.model.user_response import UserResponse @@ -334,6 +335,55 @@ def test_additional_properties_preserve_integer_precision(): ), f"expected int {realistic}, got {type(v_real).__name__} {v_real!r}" +def test_empty_group_by_list_does_not_raise_on_serialization(): + """Regression guard: a query_value widget with group_by: [] must not raise + IndexError when the response is printed or serialized via to_dict(). + + The oneOf matcher for FormulaAndFunctionEventQueryGroupByConfig cannot + resolve an empty list to a variant, leaving _composed_instances empty. + model_to_dict() and get_oneof_instance() must handle this gracefully.""" + body = """{ + "id": "abc-def-ghi", + "title": "Test Dashboard", + "layout_type": "ordered", + "widgets": [ + { + "id": 1234567890, + "definition": { + "type": "query_value", + "requests": [ + { + "response_format": "scalar", + "queries": [ + { + "data_source": "logs", + "name": "query1", + "search": {"query": ""}, + "indexes": ["*"], + "compute": {"aggregation": "count"}, + "group_by": [], + "storage": "hot" + } + ] + } + ], + "autoscale": true, + "precision": 2 + }, + "layout": {"x": 0, "y": 0, "width": 2, "height": 2} + } + ] + }""" + config = Configuration() + deserialized_data = validate_and_convert_types( + json.loads(body), (Dashboard,), ["received_data"], True, True, config + ) + assert isinstance(deserialized_data, Dashboard) + result = model_to_dict(deserialized_data) + assert result["title"] == "Test Dashboard" + assert str(deserialized_data) != "" + + def test_schema_declared_float_still_upconverts_int_input(): """Regression guard: when the schema explicitly declares a field as float, an integer JSON value must still upconvert to float."""