diff --git a/superset/dashboards/commands/importers/v1/__init__.py b/superset/dashboards/commands/importers/v1/__init__.py index 1c40a40512..00e01356c6 100644 --- a/superset/dashboards/commands/importers/v1/__init__.py +++ b/superset/dashboards/commands/importers/v1/__init__.py @@ -25,7 +25,10 @@ from superset.charts.commands.importers.v1.utils import import_chart from superset.charts.schemas import ImportV1ChartSchema from superset.commands.importers.v1 import ImportModelsCommand from superset.dashboards.commands.exceptions import DashboardImportError -from superset.dashboards.commands.importers.v1.utils import import_dashboard +from superset.dashboards.commands.importers.v1.utils import ( + import_dashboard, + update_id_refs, +) from superset.dashboards.dao import DashboardDAO from superset.dashboards.schemas import ImportV1DashboardSchema from superset.databases.commands.importers.v1.utils import import_database @@ -128,8 +131,8 @@ class ImportDashboardsCommand(ImportModelsCommand): dashboard_chart_ids: List[Tuple[int, int]] = [] for file_name, config in configs.items(): if file_name.startswith("dashboards/"): + config = update_id_refs(config, chart_ids) dashboard = import_dashboard(session, config, overwrite=overwrite) - for uuid in find_chart_uuids(config["position"]): chart_id = chart_ids[uuid] if (dashboard.id, chart_id) not in existing_relationships: diff --git a/superset/dashboards/commands/importers/v1/utils.py b/superset/dashboards/commands/importers/v1/utils.py index c21d9696ac..de3e4e84a6 100644 --- a/superset/dashboards/commands/importers/v1/utils.py +++ b/superset/dashboards/commands/importers/v1/utils.py @@ -29,6 +29,70 @@ logger = logging.getLogger(__name__) JSON_KEYS = {"position": "position_json", "metadata": "json_metadata"} +def build_uuid_to_id_map(position: Dict[str, Any]) -> Dict[str, int]: + return { + child["meta"]["uuid"]: child["meta"]["chartId"] + for child in position.values() + if ( + isinstance(child, dict) + and child["type"] == "CHART" + and "uuid" in child["meta"] + ) + } + + +def update_id_refs(config: Dict[str, Any], chart_ids: Dict[str, int]) -> Dict[str, Any]: + """Update dashboard metadata to use new IDs""" + if not config.get("metadata"): + return config + + fixed = config.copy() + + # build map old_id => new_id + old_ids = build_uuid_to_id_map(fixed["position"]) + id_map = {old_id: chart_ids[uuid] for uuid, old_id in old_ids.items()} + + # fix metadata + metadata = fixed["metadata"] + if "timed_refresh_immune_slices" in metadata: + metadata["timed_refresh_immune_slices"] = [ + id_map[old_id] for old_id in metadata["timed_refresh_immune_slices"] + ] + + if "filter_scopes" in metadata: + # in filter_scopes the key is the chart ID as a string; we need to udpate + # them to be the new ID as a string: + metadata["filter_scopes"] = { + str(id_map[int(old_id)]): columns + for old_id, columns in metadata["filter_scopes"].items() + } + + # now update columns to use new IDs: + for columns in metadata["filter_scopes"].values(): + for attributes in columns.values(): + attributes["immune"] = [ + id_map[old_id] for old_id in attributes["immune"] + ] + + if "expanded_slices" in metadata: + metadata["expanded_slices"] = { + str(id_map[int(old_id)]): value + for old_id, value in metadata["expanded_slices"].items() + } + + # fix position + position = fixed["position"] + for child in position.values(): + if ( + isinstance(child, dict) + and child["type"] == "CHART" + and "uuid" in child["meta"] + ): + child["meta"]["chartId"] = chart_ids[child["meta"]["uuid"]] + + return fixed + + def import_dashboard( session: Session, config: Dict[str, Any], overwrite: bool = False ) -> Dashboard: diff --git a/tests/dashboards/commands_tests.py b/tests/dashboards/commands_tests.py index d8eccc5790..31c6f6098f 100644 --- a/tests/dashboards/commands_tests.py +++ b/tests/dashboards/commands_tests.py @@ -263,6 +263,12 @@ class TestImportDashboardsCommand(SupersetTestCase): dashboard = ( db.session.query(Dashboard).filter_by(uuid=dashboard_config["uuid"]).one() ) + + assert len(dashboard.slices) == 1 + chart = dashboard.slices[0] + assert str(chart.uuid) == chart_config["uuid"] + new_chart_id = chart.id + assert dashboard.dashboard_title == "Test dash" assert dashboard.description is None assert dashboard.css == "" @@ -272,7 +278,7 @@ class TestImportDashboardsCommand(SupersetTestCase): "children": [], "id": "CHART-SVAlICPOSJ", "meta": { - "chartId": 83, + "chartId": new_chart_id, "height": 50, "sliceName": "Number of California Births", "uuid": "0c23747a-6528-4629-97bf-e4b78d3b9df1", @@ -305,17 +311,18 @@ class TestImportDashboardsCommand(SupersetTestCase): assert json.loads(dashboard.json_metadata) == { "color_scheme": None, "default_filters": "{}", - "expanded_slices": {}, + "expanded_slices": {str(new_chart_id): True}, + "filter_scopes": { + str(new_chart_id): { + "region": {"scope": ["ROOT_ID"], "immune": [new_chart_id]} + }, + }, "import_time": 1604342885, "refresh_frequency": 0, "remote_id": 7, - "timed_refresh_immune_slices": [], + "timed_refresh_immune_slices": [new_chart_id], } - assert len(dashboard.slices) == 1 - chart = dashboard.slices[0] - assert str(chart.uuid) == chart_config["uuid"] - dataset = chart.table assert str(dataset.uuid) == dataset_config["uuid"] diff --git a/tests/fixtures/importexport.py b/tests/fixtures/importexport.py index f0c35a1f43..e56676a531 100644 --- a/tests/fixtures/importexport.py +++ b/tests/fixtures/importexport.py @@ -488,8 +488,9 @@ dashboard_config = { }, }, "metadata": { - "timed_refresh_immune_slices": [], - "expanded_slices": {}, + "timed_refresh_immune_slices": [83], + "filter_scopes": {"83": {"region": {"scope": ["ROOT_ID"], "immune": [83]}},}, + "expanded_slices": {"83": True}, "refresh_frequency": 0, "default_filters": "{}", "color_scheme": None,