This is an automated email from the ASF dual-hosted git repository.

rusackas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 46b2d7d7a9c test(dashboard-import): pin native filter scope rootPath 
preservation (#19944) (#40135)
46b2d7d7a9c is described below

commit 46b2d7d7a9cdfe046227c43ac43ebed0b26d2901
Author: Evan Rusackas <[email protected]>
AuthorDate: Wed May 20 12:25:34 2026 -0700

    test(dashboard-import): pin native filter scope rootPath preservation 
(#19944) (#40135)
    
    Co-authored-by: Claude Code <[email protected]>
---
 .../dashboards/commands/importers/v1/utils_test.py | 115 +++++++++++++++++++++
 1 file changed, 115 insertions(+)

diff --git a/tests/unit_tests/dashboards/commands/importers/v1/utils_test.py 
b/tests/unit_tests/dashboards/commands/importers/v1/utils_test.py
index 88dfa393c6f..6fe39c1cc3d 100644
--- a/tests/unit_tests/dashboards/commands/importers/v1/utils_test.py
+++ b/tests/unit_tests/dashboards/commands/importers/v1/utils_test.py
@@ -123,6 +123,121 @@ def test_update_native_filter_config_scope_excluded():
     }
 
 
+def test_update_native_filter_config_preserves_rootpath_and_remaps_excluded():
+    """
+    Regression guard for #19944: a native filter's ``scope`` has two parts that
+    must both survive an export/import roundtrip:
+
+    - ``rootPath`` controls *which* dashboard sections (tabs/rows) the filter
+      applies to. It uses position keys (``ROOT_ID``, ``TAB-xxx``), not chart
+      IDs, so ``update_id_refs`` must leave it untouched.
+    - ``excluded`` is a list of chart IDs the filter does NOT apply to within
+      its rootPath. Those IDs must be remapped to destination-env IDs.
+
+    The original bug report — "filters are automatically applied to all charts,
+    even if a different scoping was defined before the export" — describes a
+    rootPath silently being collapsed back to ``["ROOT_ID"]`` (i.e. "apply
+    everywhere"). This test pins the post-refactor contract: the import path
+    must not mutate or drop ``rootPath``.
+    """
+    from superset.commands.dashboard.importers.v1.utils import update_id_refs
+
+    config: dict[str, Any] = {
+        "position": {
+            "CHART1": {
+                "id": "CHART1",
+                "meta": {"chartId": 101, "uuid": "uuid1"},
+                "type": "CHART",
+            },
+            "CHART2": {
+                "id": "CHART2",
+                "meta": {"chartId": 102, "uuid": "uuid2"},
+                "type": "CHART",
+            },
+            "CHART3": {
+                "id": "CHART3",
+                "meta": {"chartId": 103, "uuid": "uuid3"},
+                "type": "CHART",
+            },
+        },
+        "metadata": {
+            "native_filter_configuration": [
+                {
+                    "id": "NATIVE_FILTER-region",
+                    "name": "Region",
+                    "scope": {
+                        # Filter applies only to charts under TAB-revenue,
+                        # except chart 102 which is explicitly excluded.
+                        "rootPath": ["TAB-revenue"],
+                        "excluded": [102],
+                    },
+                },
+                {
+                    "id": "NATIVE_FILTER-product",
+                    "name": "Product",
+                    "scope": {
+                        # Different filter, different rootPath; must not be
+                        # cross-contaminated with the first filter's scope.
+                        "rootPath": ["TAB-inventory", "TAB-revenue"],
+                        "excluded": [101, 103],
+                    },
+                },
+            ],
+        },
+    }
+    chart_ids = {"uuid1": 1, "uuid2": 2, "uuid3": 3}
+    dataset_info: dict[str, dict[str, Any]] = {}
+
+    fixed = update_id_refs(config, chart_ids, dataset_info)
+    filters = fixed["metadata"]["native_filter_configuration"]
+
+    # rootPath uses position keys, not chart IDs — must pass through unchanged.
+    assert filters[0]["scope"]["rootPath"] == ["TAB-revenue"]
+    assert filters[1]["scope"]["rootPath"] == ["TAB-inventory", "TAB-revenue"]
+
+    # excluded uses chart IDs — must be remapped to destination-env IDs.
+    assert filters[0]["scope"]["excluded"] == [2]
+    assert filters[1]["scope"]["excluded"] == [1, 3]
+
+
+def test_update_native_filter_config_default_rootpath_preserved():
+    """
+    The "apply everywhere" default — ``rootPath: ["ROOT_ID"]`` — must also
+    survive untouched. A regression that special-cased this value (e.g. by
+    deleting it) would silently change "apply everywhere" into "apply nowhere"
+    on import, since downstream consumers treat a missing rootPath as empty
+    rather than as the default.
+    """
+    from superset.commands.dashboard.importers.v1.utils import update_id_refs
+
+    config: dict[str, Any] = {
+        "position": {
+            "CHART1": {
+                "id": "CHART1",
+                "meta": {"chartId": 101, "uuid": "uuid1"},
+                "type": "CHART",
+            },
+        },
+        "metadata": {
+            "native_filter_configuration": [
+                {
+                    "id": "NATIVE_FILTER-global",
+                    "name": "Global",
+                    "scope": {"rootPath": ["ROOT_ID"], "excluded": []},
+                }
+            ],
+        },
+    }
+    chart_ids = {"uuid1": 1}
+    dataset_info: dict[str, dict[str, Any]] = {}
+
+    fixed = update_id_refs(config, chart_ids, dataset_info)
+    scope = fixed["metadata"]["native_filter_configuration"][0]["scope"]
+
+    assert scope["rootPath"] == ["ROOT_ID"]
+    assert scope["excluded"] == []
+
+
 def 
test_update_id_refs_cross_filter_chart_configuration_key_and_excluded_mapping():
     from superset.commands.dashboard.importers.v1.utils import update_id_refs
 

Reply via email to