Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: user UUIDs on export for Native Filter Configuration #18562

Merged
merged 10 commits into from
Feb 8, 2022
7 changes: 7 additions & 0 deletions superset/dashboards/commands/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from superset.commands.export import ExportModelsCommand
from superset.datasets.commands.export import ExportDatasetsCommand
from superset.datasets.dao import DatasetDAO
from superset.charts.dao import ChartDAO
from superset.models.dashboard import Dashboard
from superset.models.slice import Slice
from superset.utils.dict_import_export import EXPORT_VERSION
Expand Down Expand Up @@ -140,6 +141,12 @@ def _export(model: Dashboard) -> Iterator[Tuple[str, str]]:
target["datasetUuid"] = str(dataset.uuid)
yield from ExportDatasetsCommand([dataset_id]).run()

# grab UUID and replace with the chartIds in excluded
native_filter["scope"]["excluded"] = [
str(ChartDAO.find_by_id(chart_id).uuid)
for chart_id in native_filter.get("scope").get("excluded")
]

# the mapping between dashboard -> charts is inferred from the position
# attribute, so if it's not present we need to add a default config
if not payload.get("position"):
Expand Down
8 changes: 8 additions & 0 deletions superset/dashboards/commands/importers/v1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from superset.commands.importers.v1 import ImportModelsCommand
from superset.dashboards.commands.exceptions import DashboardImportError
from superset.dashboards.commands.importers.v1.utils import (
convert_native_filter_excluded_charts_uuid_to_id,
find_chart_uuids,
find_native_filter_datasets,
import_dashboard,
Expand Down Expand Up @@ -116,6 +117,13 @@ def _import(
chart = import_chart(session, config, overwrite=False)
chart_ids[str(chart.uuid)] = chart.id

# switch out native filters excluded uuids for chart_ids
for file_name, config in configs.items():
if file_name.startswith("dashboards/"):
convert_native_filter_excluded_charts_uuid_to_id(
config.get("metadata", {}), chart_ids
)

# store the existing relationship between dashboards and charts
existing_relationships = session.execute(
select([dashboard_slices.c.dashboard_id, dashboard_slices.c.slice_id])
Expand Down
14 changes: 13 additions & 1 deletion superset/dashboards/commands/importers/v1/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import json
import logging
from typing import Any, Dict, Set
from typing import Any, Callable, Dict, Set, Union
Copy link
Member

Choose a reason for hiding this comment

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

These new imports are not needed, right?


from flask import g
from sqlalchemy.orm import Session
Expand Down Expand Up @@ -45,6 +45,18 @@ def find_native_filter_datasets(metadata: Dict[str, Any]) -> Set[str]:
return uuids


def convert_native_filter_excluded_charts_uuid_to_id(
metadata: Dict[str, Any], chart_ids: Dict[str, int]
) -> None:
native_filter_configuration = metadata.get("native_filter_configuration")
if native_filter_configuration:
for native_filter in native_filter_configuration:
native_filter["scope"]["excluded"] = [
chart_ids.get(chart_uuid)
for chart_uuid in native_filter["scope"]["excluded"]
]


def build_uuid_to_id_map(position: Dict[str, Any]) -> Dict[str, int]:
return {
child["meta"]["uuid"]: child["meta"]["chartId"]
Expand Down
58 changes: 57 additions & 1 deletion tests/unit_tests/dashboards/commands/importers/v1/utils_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# under the License.
# pylint: disable=import-outside-toplevel, unused-argument

from typing import Any, Dict
from typing import Any, cast, Dict
hughhhh marked this conversation as resolved.
Show resolved Hide resolved


def test_update_id_refs_immune_missing( # pylint: disable=invalid-name
Expand Down Expand Up @@ -72,3 +72,59 @@ def test_update_id_refs_immune_missing( # pylint: disable=invalid-name
"metadata": {"filter_scopes": {"1": {"filter_name": {"immune": [2]}}}},
"native_filter_configuration": [],
}


def test_convert_native_filter_excluded_charts_uuid_to_id(app_context: None,):
from superset.dashboards.commands.importers.v1.utils import (
convert_native_filter_excluded_charts_uuid_to_id,
)

config = {
"position": {
"CHART1": {
"id": "CHART1",
"meta": {"chartId": 101, "uuid": "uuid1",},
"type": "CHART",
},
"CHART2": {
"id": "CHART2",
"meta": {"chartId": 102, "uuid": "uuid2",},
"type": "CHART",
},
},
"metadata": {
"filter_scopes": {
"101": {"filter_name": {"immune": [102, 103]}},
"104": {"filter_name": {"immune": [102, 103]}},
},
"native_filter_configuration": [
{"scope": {"excluded": ["uuid1", "uuid2"]}}
],
},
}
chart_ids = {"uuid1": 1, "uuid2": 2}

metadata = cast(Dict[str, Any], config.get("metadata", {}),)
convert_native_filter_excluded_charts_uuid_to_id(metadata, chart_ids)

assert config == {
"position": {
"CHART1": {
"id": "CHART1",
"meta": {"chartId": 101, "uuid": "uuid1"},
"type": "CHART",
},
"CHART2": {
"id": "CHART2",
"meta": {"chartId": 102, "uuid": "uuid2"},
"type": "CHART",
},
},
"metadata": {
"filter_scopes": {
"101": {"filter_name": {"immune": [102, 103]}},
"104": {"filter_name": {"immune": [102, 103]}},
},
"native_filter_configuration": [{"scope": {"excluded": [1, 2]}}],
},
}