From 211564a6da5fa76210396e858d4d1acbe7cba950 Mon Sep 17 00:00:00 2001 From: Felix Date: Wed, 9 Oct 2024 20:36:46 +0200 Subject: [PATCH] fix(dashboard-export): Fixes datasetId is not replaced with datasetUuid in Dashboard export in 4.1.x (#30425) --- superset/commands/dashboard/export.py | 34 +++++++++----- .../dashboards/commands_tests.py | 45 +++++++++++++++++++ 2 files changed, 67 insertions(+), 12 deletions(-) diff --git a/superset/commands/dashboard/export.py b/superset/commands/dashboard/export.py index fe3a6b8eab871..93cc490ad73de 100644 --- a/superset/commands/dashboard/export.py +++ b/superset/commands/dashboard/export.py @@ -130,6 +130,18 @@ def _file_content(model: Dashboard) -> str: logger.info("Unable to decode `%s` field: %s", key, value) payload[new_name] = {} + # Extract all native filter datasets and replace native + # filter dataset references with uuid + for native_filter in payload.get("metadata", {}).get( + "native_filter_configuration", [] + ): + for target in native_filter.get("targets", []): + dataset_id = target.pop("datasetId", None) + if dataset_id is not None: + dataset = DatasetDAO.find_by_id(dataset_id) + if dataset: + target["datasetUuid"] = str(dataset.uuid) + # 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"): @@ -180,16 +192,14 @@ def _export( logger.info("Unable to decode `%s` field: %s", key, value) payload[new_name] = {} - # Extract all native filter datasets and replace native - # filter dataset references with uuid - for native_filter in payload.get("metadata", {}).get( - "native_filter_configuration", [] - ): - for target in native_filter.get("targets", []): - dataset_id = target.pop("datasetId", None) - if dataset_id is not None: - dataset = DatasetDAO.find_by_id(dataset_id) - if dataset: - target["datasetUuid"] = str(dataset.uuid) - if export_related: + if export_related: + # Extract all native filter datasets and export referenced datasets + for native_filter in payload.get("metadata", {}).get( + "native_filter_configuration", [] + ): + for target in native_filter.get("targets", []): + dataset_id = target.pop("datasetId", None) + if dataset_id is not None: + dataset = DatasetDAO.find_by_id(dataset_id) + if dataset: yield from ExportDatasetsCommand([dataset_id]).run() diff --git a/tests/integration_tests/dashboards/commands_tests.py b/tests/integration_tests/dashboards/commands_tests.py index 451d924cd6c6e..38606a130403f 100644 --- a/tests/integration_tests/dashboards/commands_tests.py +++ b/tests/integration_tests/dashboards/commands_tests.py @@ -238,6 +238,51 @@ def test_export_dashboard_command(self, mock_g1, mock_g2): "version": "1.0.0", } + # @pytest.mark.usefixtures("load_covid_dashboard") + @pytest.mark.skip(reason="missing covid fixture") + @patch("superset.security.manager.g") + @patch("superset.views.base.g") + def test_export_dashboard_command_dataset_references(self, mock_g1, mock_g2): + mock_g1.user = security_manager.find_user("admin") + mock_g2.user = security_manager.find_user("admin") + + example_dashboard = ( + db.session.query(Dashboard) + .filter_by(uuid="f4065089-110a-41fa-8dd7-9ce98a65e250") + .one() + ) + command = ExportDashboardsCommand([example_dashboard.id]) + contents = dict(command.run()) + + expected_paths = { + "metadata.yaml", + f"dashboards/COVID_Vaccine_Dashboard_{example_dashboard.id}.yaml", + "datasets/examples/covid_vaccines.yaml", # referenced dataset needs to be exported + "databases/examples.yaml", + } + for chart in example_dashboard.slices: + chart_slug = secure_filename(chart.slice_name) + expected_paths.add(f"charts/{chart_slug}_{chart.id}.yaml") + assert expected_paths == set(contents.keys()) + + metadata = yaml.safe_load( + contents[f"dashboards/World_Banks_Data_{example_dashboard.id}.yaml"]() + ) + + # find the dataset references in native filter and check if they are correct + assert "native_filter_configuration" in metadata["metadata"] + + for filter_config in metadata["metadata"][ + "native_filter_configuration" + ].values(): + assert "targets" in filter_config + targets = filter_config["targets"] + + for column in targets: + # we need to find the correct datasetUuid (not datasetId) + assert "datasetUuid" in column + assert column["datasetUuid"] == "974b7a1c-22ea-49cb-9214-97b7dbd511e0" + @pytest.mark.usefixtures("load_world_bank_dashboard_with_slices") @patch("superset.security.manager.g") @patch("superset.views.base.g")