fix(slo_corrections): strip null-valued mutually-exclusive fields before POST - #649
Merged
riyazsh merged 1 commit intoJul 29, 2026
Merged
Conversation
…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.
Contributor
Author
|
The |
michael-richey
approved these changes
Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Destination
POST /api/v1/slo/correctionrejects payloads that carryslo_id,slo_query, orendexplicitly set tonull. Error shape:Source resources round-tripped from
GET /api/v1/slo/correction/{id}carry bothslo_idandslo_querywith one of them explicitly null — they are mutually exclusive:slo_idset,slo_query = nullslo_queryset,slo_id = nullSimilarly, recurring rrule corrections carry
end = null(no fixed end time).Change
Add
attributes.slo_id,attributes.slo_query, andattributes.endtoSLOCorrections.resource_config.non_nullable_attr. The existingdel_null_attrhelper (datadog_sync/utils/resource_utils.py:277) strips fields listed there when their value isNone, viaprep_resource. Same pattern already used forattributes.durationandattributes.rrule(mutually-exclusive with each other), and fordowntimes.recurrence.until_datevs.until_occurrences.Safety
resources_handler.py:503(resource) +:506(destination_copy) both pass throughprep_resourcebeforecheck_diff— no spurious diff on state-vs-resource comparison when one side hasnulland the other has the field stripped.connect_resources:connect_resourcesruns atresources_handler.py:496, beforeprep_resourceat:503. Populatedslo_idgets remapped first; only the nullslo_id(the mutually-exclusive case) is stripped.is Nonesemantics:del_null_attratresource_utils.py:285usesresource[k_list[0]] is None, so integer0and empty string are preserved (matches destination API semantics, since 0 is a valid unix timestamp forend).Tests
New file
tests/unit/test_slo_corrections.pywith 6 unit tests:test_time_based_correction_strips_null_slo_query— populatedslo_id, nullslo_query→slo_querystripped,slo_id+endpreservedtest_query_based_correction_strips_null_slo_id_and_null_end— populatedslo_query, nullslo_id+ nullend→ both strippedtest_populated_fields_are_preserved— nothing null → nothing strippedtest_zero_end_is_preserved_not_stripped—end: 0(integer zero) is NOT null and must survivetest_check_diff_symmetric_strip_no_spurious_diff— symmetricprep_resourceon both sides ofcheck_diffyields empty diff; regression pin against a future asymmetric-prep refactortest_null_duration_and_rrule_still_stripped_regression— existingduration+rrulebehavior preservedLocal pytest run: 6/6 pass. Broader unit suite: 1067 pass, only pre-existing unrelated failures in
test_custom_client_trust_env.py(present on unmodifiedmain).Rollback
Three-line revert of the
non_nullable_attrlist additions.