feat(rum): add rum_retention_filters resource - #716
michael-richey wants to merge 4 commits into
Conversation
michael-richey
left a comment
There was a problem hiding this comment.
Good implementation overall, and the tests are thorough for both generic and exclusion filter paths.
What I liked:
- Parent-scoped handling is explicit and readable.
_application_idremapping pattern is applied consistently.- CRUD path switching via
type(retention_filtersvsexclusion_filters) is well covered by tests.
No blockers in this PR diff.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Destination filters cannot be reconciled when persisted source-to-destination state is unavailable, risking duplicates or conflicts.
Get a fresh assessment by requesting another Copilot review.
Review effort: Balanced
Findings: 1
Open (1)
What changed in this PR
Adds parent-scoped RUM retention and exclusion filter synchronization.
Changes:
- Implements enumeration, ID remapping, and CRUD routing.
- Registers and documents the resource.
- Adds unit coverage for primary workflows.
| File | Description |
|---|---|
datadog_sync/model/rum_retention_filters.py |
Implements the resource model and CRUD behavior. |
datadog_sync/models/__init__.py |
Registers the model. |
tests/unit/test_rum_retention_filters.py |
Tests routing, remapping, and CRUD operations. |
README.md |
Documents support and dependencies. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| "ignore_order": True, | ||
| "exclude_regex_paths": [r".*\['_application_id'\]"], | ||
| }, | ||
| skip_resource_mapping=True, |
|
Fixed in |
Integration Test Findings (EU1 → US5 sync)Ran a full integration test: imported all RUM resources from the EU1 internal testing org, then synced to the US5 internal testing org. Found 3 failures in 1.
|
|
Fixed in
New test verifies only |
Re-test Results (after fix
|
Additional Finding:
|
| Scenario | Result | Details |
|---|---|---|
| Create | ❌ FAIL | default_sessions/default_errors: source in meta not stripped (previously reported) |
| Update | ❌ FAIL | Same source/meta bug on update path |
| Skip | ✅ PASS | No diff detected when source and destination match |
| Delete | ❌ FAIL | error_tracking_exclusion_filter cannot be deleted (new finding) |
Summary of all issues on this PR
default_sessions/default_errors:meta.source: "default"sent in PATCH body → 400. Fix attempted in8c28470added"attributes.source"toexcluded_attributes, butsourceis undermeta, notattributes. Need to add"meta"or"meta.source"toexcluded_attributes.error_tracking_exclusion_filterupdate: Fixed ✅ — only sendsenabledon update.error_tracking_exclusion_filterdelete: New finding — API rejects deletion. Need to skip or handle gracefully.
|
Fixed in
|
dec6067 to
2a81276
Compare
0b8775f to
062e450
Compare
2a81276 to
6d64fe1
Compare
Add the rum_retention_filters resource type, unifying the generic
retention_filters and exclusion_filters types (distinguished by data.type)
which live on separate sub-paths under /api/v2/rum/applications/{app_id}.
Retention filters are parent-scoped under a RUM application, but the
application id is not part of the filter body. The model injects a synthetic
_application_id during enumeration and remaps it (source app id -> destination
app id) via resource_connections before apply. prep_resource runs after
connect_resources and removes excluded_attributes, so _application_id is
deliberately NOT excluded (it must survive prep so create/update can read it)
and is instead kept out of diffs via deep_diff_config.exclude_regex_paths
(same pattern as users.py's handle/service_account fields).
- datadog_sync/model/rum_retention_filters.py (new) -- RUMRetentionFilters
- datadog_sync/models/__init__.py -- register RUMRetentionFilters
- tests/unit/test_rum_retention_filters.py (new) -- 9 unit tests pinning
get_resources (app iteration + retention/exclusion merge + _application_id
injection), import passthrough, create (per-type sub-path, id popped,
_application_id re-attached), update (destination id + app id from state),
delete (per-type sub-path), and connect_resources app-id remap.
- README.md -- add rum_retention_filters (depends on rum_applications)
Order syncing is handled by a separate rum_retention_filters_order resource
(follow-up PR), matching the codebase convention (logs_archives_order,
sensitive_data_scanner_groups_order, etc.) since order must be applied after
filters exist at the destination.
Integration tests + VCR cassettes are deferred (require sandbox-org API
access to record).
Per review: skip_resource_mapping=True means the apply pre-pass never lists destination filters, so create_resource always POSTs when state is absent. Before POSTing, check if a matching filter already exists at the destination (scoped by app + type + name) and adopt it via update instead of creating a duplicate or failing with a conflict. New test verifies the reconciliation path.
…g filter Per integration test findings (EU1 -> US5 sync): 1. default_sessions / default_errors (400 'attribute source must be one of ui terraform'): the 'source' field is a runtime-only response attribute not in the OpenAPI spec. Added 'attributes.source' to excluded_attributes so it is stripped by prep_resource before the PATCH. 2. error_tracking_exclusion_filter (400 'only enabled can be updated on the error tracking exclusion filter'): the API only allows toggling 'enabled' on this special exclusion filter. update_resource now strips all attributes except 'enabled' when the destination id matches 'error_tracking_exclusion_filter'. New test verifies only 'enabled' is sent for the error_tracking filter.
… delete Per integration test re-test findings: 1. default_sessions/default_errors still failing: 'source' is under 'meta', not 'attributes'. Changed excluded_attributes from 'attributes.source' to 'meta' (the entire meta object contains runtime-only fields: updated_at, updated_by_handle, edition_mode, source). 2. error_tracking_exclusion_filter cannot be deleted (system-provisioned). delete_resource now detects this filter id and skips the delete with a warning instead of failing. New test verifies error_tracking filter delete is skipped.
062e450 to
90ac6fb
Compare
6d64fe1 to
e7798c9
Compare
|
Thanks for building this out — the API coverage is valuable. I did find one correctness issue to address before merge: Potential key collision across applications
For exclusion filters specifically, Datadog’s API docs indicate the built-in Error Tracking filter uses the fixed id Impact:
Suggested fix:
Once that is in, this should be in great shape. |

Summary
Adds
rum_retention_filters, unifying the genericretention_filtersandexclusion_filterstypes (distinguished bydata.type), which live on separate sub-paths under/api/v2/rum/applications/{app_id}.Retention filters are parent-scoped under a RUM application, but the application id is not part of the filter body. The model injects a synthetic
_application_idduring enumeration and remaps it (source app id → destination app id) viaresource_connectionsbefore apply.Stacked on #715 (
rum_metrics), which is stacked on #714 (rum_applicationsprep).Design notes
prep_resourceruns afterconnect_resourcesand removesexcluded_attributes. So_application_idis deliberately not inexcluded_attributes(it must survive prep so create/update can read it) and is instead kept out of diffs viadeep_diff_config.exclude_regex_paths— the same patternusers.pyuses forhandle/service_account.idis excluded (server-assigned on create; update rewrites it to the destination id).retention_filters→/retention_filters/{rf_id};exclusion_filters→/retention_filters/exclusion/{ef_id}.id(popped); update setsresource["id"] = destination_id.Changes
datadog_sync/model/rum_retention_filters.py(new) —RUMRetentionFiltersdatadog_sync/models/__init__.py— registerRUMRetentionFilterstests/unit/test_rum_retention_filters.py(new) — 9 unit testsREADME.md— addrum_retention_filters(depends onrum_applications)Testing
pytest tests/unit/test_rum_retention_filters.py→ 9 passedFollow-ups
rum_retention_filters_orderresource (next PR), matching the codebase convention (logs_archives_order,sensitive_data_scanner_groups_order,logs_indexes_order,logs_pipelines_order). Order must be applied after filters exist at the destination, so apre_apply_hook(which runs before apply) is incorrect; a dedicated order resource synced after the filters is the only correct design.AsyncMockclients.