Skip to content

fix(slo_corrections): strip null-valued mutually-exclusive fields before POST - #649

Merged
riyazsh merged 1 commit into
mainfrom
riyaz.shiraguppi/slo-corrections-non-nullable-fields
Jul 29, 2026
Merged

fix(slo_corrections): strip null-valued mutually-exclusive fields before POST#649
riyazsh merged 1 commit into
mainfrom
riyaz.shiraguppi/slo-corrections-non-nullable-fields

Conversation

@riyazsh

@riyazsh riyazsh commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Destination POST /api/v1/slo/correction rejects payloads that carry slo_id, slo_query, or end explicitly set to null. Error shape:

API input validation failed: {'slo_query': ['Field may not be null.']}

Source resources round-tripped from GET /api/v1/slo/correction/{id} carry both slo_id and slo_query with one of them explicitly null — they are mutually exclusive:

  • Time-based correction: slo_id set, slo_query = null
  • Query-based correction: slo_query set, slo_id = null

Similarly, recurring rrule corrections carry end = null (no fixed end time).

Change

Add attributes.slo_id, attributes.slo_query, and attributes.end to SLOCorrections.resource_config.non_nullable_attr. The existing del_null_attr helper (datadog_sync/utils/resource_utils.py:277) strips fields listed there when their value is None, via prep_resource. Same pattern already used for attributes.duration and attributes.rrule (mutually-exclusive with each other), and for downtimes.recurrence.until_date vs .until_occurrences.

Safety

  • Symmetric strip: resources_handler.py:503 (resource) + :506 (destination_copy) both pass through prep_resource before check_diff — no spurious diff on state-vs-resource comparison when one side has null and the other has the field stripped.
  • Order with connect_resources: connect_resources runs at resources_handler.py:496, before prep_resource at :503. Populated slo_id gets remapped first; only the null slo_id (the mutually-exclusive case) is stripped.
  • is None semantics: del_null_attr at resource_utils.py:285 uses resource[k_list[0]] is None, so integer 0 and empty string are preserved (matches destination API semantics, since 0 is a valid unix timestamp for end).

Tests

New file tests/unit/test_slo_corrections.py with 6 unit tests:

  1. test_time_based_correction_strips_null_slo_query — populated slo_id, null slo_queryslo_query stripped, slo_id + end preserved
  2. test_query_based_correction_strips_null_slo_id_and_null_end — populated slo_query, null slo_id + null end → both stripped
  3. test_populated_fields_are_preserved — nothing null → nothing stripped
  4. test_zero_end_is_preserved_not_strippedend: 0 (integer zero) is NOT null and must survive
  5. test_check_diff_symmetric_strip_no_spurious_diff — symmetric prep_resource on both sides of check_diff yields empty diff; regression pin against a future asymmetric-prep refactor
  6. test_null_duration_and_rrule_still_stripped_regression — existing duration + rrule behavior preserved

Local pytest run: 6/6 pass. Broader unit suite: 1067 pass, only pre-existing unrelated failures in test_custom_client_trust_env.py (present on unmodified main).

Rollback

Three-line revert of the non_nullable_attr list additions.

…ore POST

The destination `POST /api/v1/slo/correction` endpoint rejects payloads
that carry `slo_id`, `slo_query`, or `end` explicitly set to null with
'API input validation failed: {"<field>": ["Field may not be null."]}'.

Source resources round-tripped from `GET /api/v1/slo/correction/{id}`
carry both slo_id and slo_query with one explicitly null (they are
mutually exclusive — a time-based correction sets slo_id + null
slo_query, and a query-based correction sets slo_query + null slo_id).
Similarly, recurring rrule corrections carry end=null.

Add slo_id, slo_query, and end to non_nullable_attr so the existing
del_null_attr helper strips these fields when they are null. Symmetric
prep_resource application on both sides of check_diff (via
resources_handler.py:503,506 and :657,658) means no spurious update
churn.

Unit tests cover: time-based correction (slo_query stripped), query-
based correction (slo_id + end stripped), populated fields preserved,
0-end preserved (is-None check semantics), symmetric strip yields no
check_diff churn, existing duration/rrule regression preserved.
@riyazsh
riyazsh requested a review from a team as a code owner July 28, 2026 22:46
@riyazsh

riyazsh commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

The Ensure labels check is failing because a changelog/* label is required. Requesting a maintainer to apply changelog/Fixed (or an equivalent — this is a defect fix, no new API or removal). I do not have label permissions on this repo via either my personal or EMU account.

@riyazsh
riyazsh merged commit 7517a06 into main Jul 29, 2026
12 of 13 checks passed
@riyazsh
riyazsh deleted the riyaz.shiraguppi/slo-corrections-non-nullable-fields branch July 29, 2026 16:27
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