Skip to content

FormulaAndFunctionQueryDefinition group_by is list of objects, not dicts#3768

Open
ddadamhooper wants to merge 4 commits into
masterfrom
adam.hooper/propagate-unparsed
Open

FormulaAndFunctionQueryDefinition group_by is list of objects, not dicts#3768
ddadamhooper wants to merge 4 commits into
masterfrom
adam.hooper/propagate-unparsed

Conversation

@ddadamhooper

@ddadamhooper ddadamhooper commented Jul 13, 2026

Copy link
Copy Markdown

Fixes bugs uncovered by DataDog/datadog-api-spec#5974 Test branch datadog-api-spec/test/adam.hooper/topology-widget-freshen-2

Behavior change

Solved two small parsing bugs:

oneOf matched both branches

Spotted during TopologyMapWidgetDefinition change.

After the schema change, TopologyMapWidgetDefinitionDataStreams has an attribute requests with each child a TopologyRequestDataStreams. And TopologyMapWidgetDefinitionServiceMap has an attribute requests with each child TopologyRequestServiceMap.

When parsing a service-map widget, we test both branches:

  • TopologyMapWidgetDefinitionServiceMap => succeeds, because its requests contains a valid TopologyRequestServiceMap
  • TopologyMapWidgetDefinitionDataStreams => succeeds in error, because its requests contains an invalid TopologyRequestDataStreams but that _unparsed wasn't propagated to the list.

Fix: propagate _unparsed so the list is marked unparsed.

Formula Events group-by lists failed to parse

Existing bug -- this is a breaking client-API change! It fixes another breaking client-API changed introduced in #3230.

#3230 (March 2026) introduced a regression: Formula Events group-by lists failed to parse.

What we wanted:

query.get_oneof_instance()          # -> FormulaAndFunctionEventQueryDefinition
query._unparsed                     # -> False

gb = query.get_oneof_instance().group_by
type(gb)                            # -> list
gb[0].facet                         # -> '@geo.country_iso_code'
gb[0].sort.order.value              # -> 'desc'

What we shipped:

query = <FormulaAndFunctionQueryDefinition>
query.get_oneof_instance()      # -> UnparsedObject   (not the real variant!)
query._unparsed                 # -> True             (but never surfaced)

gb = query.get_oneof_instance().group_by
type(gb)                        # -> list
gb                              # -> [{'facet': ..., 'limit': 250, 'sort': {...}}]  # raw dicts
gb[0].facet                     # -> AttributeError: 'dict' object has no attribute 'facet'

This is because the parser wouldn't accept oneOf where one branch is a list.

But we didn't see this error because we weren't propagating _unparsed correctly.

Fix: deserialize_model(): accept a list as a valid oneOf child.

TL;DR

Customer-facing change in this PR: parsing Dashboards API responses, formula group-by clauses WERE list-of-dicts, and are NOW list-of-objects. This fixes a regression.

Two related deserialization bugs surfaced when parsing the topology_map
and geomap dashboard widgets:

- ModelNormal.set_attribute only propagated _unparsed from list elements
  that were UnparsedObject instances, missing nested OpenApiModel
  instances that were themselves _unparsed. A topology_map request whose
  query carried a mismatched data_source enum could ambiguously satisfy
  both oneOf variants without being flagged.

- allows_single_value_input skipped list oneOf branches, returning False
  for a ModelComposed such as FormulaAndFunctionEventQueryGroupByConfig
  (oneOf of a group-by list or a flat fields object). deserialize_model
  then splatted a list input into positional args, crashing
  ModelComposed.__init__; the TypeError was swallowed during matching and
  masked the failure once _unparsed propagation was fixed.

Propagate _unparsed from nested models in lists, and treat a list oneOf
branch as allowing single-value input so list inputs route through
get_oneof_instance. Applied to both the generated model_utils.py and the
generator template.
@ddadamhooper ddadamhooper requested review from a team as code owners July 13, 2026 13:35
@ddadamhooper

Copy link
Copy Markdown
Author

@codex review

@ddadamhooper ddadamhooper changed the title Fix oneOf deserialization for composed models with a list branch FormulaAndFunctionQueryDefinition group_by is list of objects, not dicts Jul 13, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e0bad42583

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/datadog_api_client/model_utils.py
@ddadamhooper

Copy link
Copy Markdown
Author

/merge

@gh-worker-devflow-routing-ef8351

gh-worker-devflow-routing-ef8351 Bot commented Jul 13, 2026

Copy link
Copy Markdown

View all feedbacks in Devflow UI.

2026-07-13 17:49:58 UTC ℹ️ Start processing command /merge


2026-07-13 17:50:05 UTC ℹ️ MergeQueue: waiting for PR to be ready

This pull request is not mergeable according to GitHub. Common reasons include pending required checks, missing approvals, or merge conflicts — but it could also be blocked by other repository rules or settings.
It will be added to the queue as soon as checks pass and/or get approvals. View in MergeQueue UI.
Note: if you pushed new commits since the last approval, you may need additional approval.
You can remove it from the waiting list with /remove command.


2026-07-13 18:12:51 UTC ⚠️ MergeQueue: This merge request was unqueued

adam.hooper@datadoghq.com unqueued this merge request

@ddadamhooper

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 326bb385fd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/datadog_api_client/model_utils.py Outdated
@ddadamhooper

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85501f8615

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

]
else:
list_oneof_instance = [oneof_class(**arg, **constant_kwargs) for arg in model_arg]
if not any(item._unparsed for item in list_oneof_instance):

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve empty oneOf arrays

When model_arg is an empty array, list_oneof_instance is also empty and any(...) is false, so this records the array branch even though non-list branches are still evaluated with empty model_kwargs. For datadog_api_client.v2.model.any_value.AnyValue (AnyValueObject plus [AnyValueItem]), deserializing a valid JSON value [] now records both the object and array matches and falls through to UnparsedObject, marking fields that use AnyValue as unparsed. Skip non-list branches for list payloads or special-case empty arrays so the array branch can be returned without ambiguity.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants