You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ma...@apache.org on 2019/07/18 05:22:16 UTC

[incubator-superset] branch master updated: Remove unnecessary fields from dashboard exported json (#7879)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new b512502  Remove unnecessary fields from dashboard exported json (#7879)
b512502 is described below

commit b512502d724fb323828110ecba14e5b9570dc09c
Author: Maxim Sukharev <ma...@smacker.ru>
AuthorDate: Thu Jul 18 07:22:10 2019 +0200

    Remove unnecessary fields from dashboard exported json (#7879)
    
    * Remove unnecessary fields from dashboard exported json
    
    This commit makes export respect export_fields and doesn't export
    unnecessary relations (like users with passwords hashes) which are
    ignored during the import.
    
    * Allow to import dashboard without position_json
    
    In case charts were added from chart-edit page and wasn't re-ordered on
    the dashboard position_json field is empty and import was failing with
    error.
    
    * Update export/import tests
---
 superset/models/core.py      | 37 ++++++++++++++++++++++++++-----------
 tests/import_export_tests.py | 15 +++++++++++++++
 2 files changed, 41 insertions(+), 11 deletions(-)

diff --git a/superset/models/core.py b/superset/models/core.py
index 66f2d0e..37a7592 100755
--- a/superset/models/core.py
+++ b/superset/models/core.py
@@ -617,7 +617,10 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
                 existing_dashboard = dash
 
         dashboard_to_import.id = None
-        alter_positions(dashboard_to_import, old_to_new_slc_id_dict)
+        # position_json can be empty for dashboards
+        # with charts added from chart-edit page and without re-arranging
+        if dashboard_to_import.position_json:
+            alter_positions(dashboard_to_import, old_to_new_slc_id_dict)
         dashboard_to_import.alter_params(import_time=import_time)
         if new_expanded_slices:
             dashboard_to_import.alter_params(expanded_slices=new_expanded_slices)
@@ -658,37 +661,49 @@ class Dashboard(Model, AuditMixinNullable, ImportMixin):
         for dashboard_id in dashboard_ids:
             # make sure that dashboard_id is an integer
             dashboard_id = int(dashboard_id)
-            copied_dashboard = (
+            dashboard = (
                 db.session.query(Dashboard)
                 .options(subqueryload(Dashboard.slices))
                 .filter_by(id=dashboard_id)
                 .first()
             )
-            make_transient(copied_dashboard)
-            for slc in copied_dashboard.slices:
-                make_transient(slc)
+            # remove ids and relations (like owners, created by, slices, ...)
+            copied_dashboard = dashboard.copy()
+            for slc in dashboard.slices:
                 datasource_ids.add((slc.datasource_id, slc.datasource_type))
+                copied_slc = slc.copy()
+                # save original id into json
+                # we need it to update dashboard's json metadata on import
+                copied_slc.id = slc.id
                 # add extra params for the import
-                slc.alter_params(
+                copied_slc.alter_params(
                     remote_id=slc.id,
                     datasource_name=slc.datasource.name,
                     schema=slc.datasource.schema,
                     database_name=slc.datasource.database.name,
                 )
+                # set slices without creating ORM relations
+                slices = copied_dashboard.__dict__.setdefault("slices", [])
+                slices.append(copied_slc)
             copied_dashboard.alter_params(remote_id=dashboard_id)
             copied_dashboards.append(copied_dashboard)
 
             eager_datasources = []
-            for dashboard_id, dashboard_type in datasource_ids:
+            for datasource_id, datasource_type in datasource_ids:
                 eager_datasource = ConnectorRegistry.get_eager_datasource(
-                    db.session, dashboard_type, dashboard_id
+                    db.session, datasource_type, datasource_id
                 )
-                eager_datasource.alter_params(
+                copied_datasource = eager_datasource.copy()
+                copied_datasource.alter_params(
                     remote_id=eager_datasource.id,
                     database_name=eager_datasource.database.name,
                 )
-                make_transient(eager_datasource)
-                eager_datasources.append(eager_datasource)
+                datasource_class = copied_datasource.__class__
+                for field_name in datasource_class.export_children:
+                    field_val = getattr(eager_datasource, field_name).copy()
+                    # set children without creating ORM relations
+                    copied_datasource.__dict__[field_name] = field_val
+                eager_datasources.append(copied_datasource)
 
         return json.dumps(
             {"dashboards": copied_dashboards, "datasources": eager_datasources},
diff --git a/tests/import_export_tests.py b/tests/import_export_tests.py
index d5c3609..03068a4 100644
--- a/tests/import_export_tests.py
+++ b/tests/import_export_tests.py
@@ -204,6 +204,18 @@ class ImportExportTests(SupersetTestCase):
                 exp_params.pop(k)
         self.assertEquals(exp_params, actual_params)
 
+    def assert_only_exported_slc_fields(self, expected_dash, actual_dash):
+        """ only exported json has this params
+            imported/created dashboard has relationships to other models instead
+        """
+        expected_slices = sorted(expected_dash.slices, key=lambda s: s.slice_name or "")
+        actual_slices = sorted(actual_dash.slices, key=lambda s: s.slice_name or "")
+        for e_slc, a_slc in zip(expected_slices, actual_slices):
+            params = a_slc.params_dict
+            self.assertEqual(e_slc.datasource.name, params["datasource_name"])
+            self.assertEqual(e_slc.datasource.schema, params["schema"])
+            self.assertEqual(e_slc.datasource.database.name, params["database_name"])
+
     def test_export_1_dashboard(self):
         self.login("admin")
         birth_dash = self.get_dash_by_slug("births")
@@ -216,6 +228,7 @@ class ImportExportTests(SupersetTestCase):
         )["dashboards"]
 
         birth_dash = self.get_dash_by_slug("births")
+        self.assert_only_exported_slc_fields(birth_dash, exported_dashboards[0])
         self.assert_dash_equals(birth_dash, exported_dashboards[0])
         self.assertEquals(
             birth_dash.id,
@@ -250,12 +263,14 @@ class ImportExportTests(SupersetTestCase):
         self.assertEquals(2, len(exported_dashboards))
 
         birth_dash = self.get_dash_by_slug("births")
+        self.assert_only_exported_slc_fields(birth_dash, exported_dashboards[0])
         self.assert_dash_equals(birth_dash, exported_dashboards[0])
         self.assertEquals(
             birth_dash.id, json.loads(exported_dashboards[0].json_metadata)["remote_id"]
         )
 
         world_health_dash = self.get_dash_by_slug("world_health")
+        self.assert_only_exported_slc_fields(world_health_dash, exported_dashboards[1])
         self.assert_dash_equals(world_health_dash, exported_dashboards[1])
         self.assertEquals(
             world_health_dash.id,