def test_export_dashboard_command_invalid_dataset(self, mock_g1, mock_g2):
     """Test that an error is raised when exporting an invalid dataset"""
     mock_g1.user = security_manager.find_user("admin")
     mock_g2.user = security_manager.find_user("admin")
     command = ExportDashboardsCommand([-1])
     contents = command.run()
     with self.assertRaises(DashboardNotFoundError):
         next(contents)
    def test_export_dashboard_command_no_access(self, mock_g1, mock_g2):
        """Test that users can't export datasets they don't have access to"""
        mock_g1.user = security_manager.find_user("gamma")
        mock_g2.user = security_manager.find_user("gamma")

        example_dashboard = db.session.query(Dashboard).filter_by(id=1).one()
        command = ExportDashboardsCommand([example_dashboard.id])
        contents = command.run()
        with self.assertRaises(DashboardNotFoundError):
            next(contents)
Example #3
0
    def export_dashboards(dashboard_file: Optional[str] = None) -> None:
        """Export dashboards to ZIP file"""
        # pylint: disable=import-outside-toplevel
        from superset.dashboards.commands.export import ExportDashboardsCommand
        from superset.models.dashboard import Dashboard

        g.user = security_manager.find_user(username="******")

        dashboard_ids = [id_ for (id_,) in db.session.query(Dashboard.id).all()]
        timestamp = datetime.now().strftime("%Y%m%dT%H%M%S")
        root = f"dashboard_export_{timestamp}"
        dashboard_file = dashboard_file or f"{root}.zip"

        try:
            with ZipFile(dashboard_file, "w") as bundle:
                for file_name, file_content in ExportDashboardsCommand(
                    dashboard_ids
                ).run():
                    with bundle.open(f"{root}/{file_name}", "w") as fp:
                        fp.write(file_content.encode())
        except Exception:  # pylint: disable=broad-except
            logger.exception(
                "There was an error when exporting the dashboards, please check "
                "the exception traceback in the log"
            )
            sys.exit(1)
    def test_export_dashboard_command_no_related(self, mock_g1, mock_g2):
        """
        Test that only the dashboard is exported when export_related=False.
        """
        mock_g1.user = security_manager.find_user("admin")
        mock_g2.user = security_manager.find_user("admin")

        example_dashboard = (db.session.query(Dashboard).filter_by(
            slug="world_health").one())
        command = ExportDashboardsCommand([example_dashboard.id],
                                          export_related=False)
        contents = dict(command.run())

        expected_paths = {
            "metadata.yaml",
            "dashboards/World_Banks_Data.yaml",
        }
        assert expected_paths == set(contents.keys())
    def test_export_dashboard_command_key_order(self, mock_g1, mock_g2):
        """Test that they keys in the YAML have the same order as export_fields"""
        mock_g1.user = security_manager.find_user("admin")
        mock_g2.user = security_manager.find_user("admin")

        example_dashboard = db.session.query(Dashboard).filter_by(id=1).one()
        command = ExportDashboardsCommand([example_dashboard.id])
        contents = dict(command.run())

        metadata = yaml.safe_load(contents["dashboards/World_Banks_Data.yaml"])
        assert list(metadata.keys()) == [
            "dashboard_title",
            "description",
            "css",
            "slug",
            "uuid",
            "position",
            "metadata",
            "version",
        ]
Example #6
0
    def export(self, **kwargs: Any) -> Response:
        """Export dashboards
        ---
        get:
          description: >-
            Exports multiple Dashboards and downloads them as YAML files.
          parameters:
          - in: query
            name: q
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/get_export_ids_schema'
          responses:
            200:
              description: Dashboard export
              content:
                text/plain:
                  schema:
                    type: string
            400:
              $ref: '#/components/responses/400'
            401:
              $ref: '#/components/responses/401'
            404:
              $ref: '#/components/responses/404'
            422:
              $ref: '#/components/responses/422'
            500:
              $ref: '#/components/responses/500'
        """
        requested_ids = kwargs["rison"]
        token = request.args.get("token")

        if is_feature_enabled("VERSIONED_EXPORT"):
            timestamp = datetime.now().strftime("%Y%m%dT%H%M%S")
            root = f"dashboard_export_{timestamp}"
            filename = f"{root}.zip"

            buf = BytesIO()
            with ZipFile(buf, "w") as bundle:
                try:
                    for file_name, file_content in ExportDashboardsCommand(
                            requested_ids).run():
                        with bundle.open(f"{root}/{file_name}", "w") as fp:
                            fp.write(file_content.encode())
                except DashboardNotFoundError:
                    return self.response_404()
            buf.seek(0)

            response = send_file(
                buf,
                mimetype="application/zip",
                as_attachment=True,
                attachment_filename=filename,
            )
            if token:
                response.set_cookie(token, "done", max_age=600)
            return response

        query = self.datamodel.session.query(Dashboard).filter(
            Dashboard.id.in_(requested_ids))
        query = self._base_filters.apply_all(query)
        ids = [item.id for item in query.all()]
        if not ids:
            return self.response_404()
        export = Dashboard.export_dashboards(ids)
        resp = make_response(export, 200)
        resp.headers["Content-Disposition"] = generate_download_headers(
            "json")["Content-Disposition"]
        if token:
            resp.set_cookie(token, "done", max_age=600)
        return resp
    def test_export_dashboard_command(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(id=1).one()
        command = ExportDashboardsCommand([example_dashboard.id])
        contents = dict(command.run())

        expected_paths = {
            "metadata.yaml",
            "dashboards/World_Banks_Data.yaml",
            "charts/Region_Filter.yaml",
            "datasets/examples/wb_health_population.yaml",
            "databases/examples.yaml",
            "charts/Worlds_Population.yaml",
            "charts/Most_Populated_Countries.yaml",
            "charts/Growth_Rate.yaml",
            "charts/Rural.yaml",
            "charts/Life_Expectancy_VS_Rural.yaml",
            "charts/Rural_Breakdown.yaml",
            "charts/Worlds_Pop_Growth.yaml",
            "charts/Box_plot.yaml",
            "charts/Treemap.yaml",
        }
        assert expected_paths == set(contents.keys())

        metadata = yaml.safe_load(contents["dashboards/World_Banks_Data.yaml"])

        # remove chart UUIDs from metadata so we can compare
        for chart_info in metadata["position"].values():
            if isinstance(chart_info, dict) and "uuid" in chart_info.get(
                    "meta", {}):
                del chart_info["meta"]["chartId"]
                del chart_info["meta"]["uuid"]

        assert metadata == {
            "dashboard_title": "World Bank's Data",
            "description": None,
            "css": "",
            "slug": "world_health",
            "uuid": str(example_dashboard.uuid),
            "position": {
                "DASHBOARD_CHART_TYPE-0": {
                    "children": [],
                    "id": "DASHBOARD_CHART_TYPE-0",
                    "meta": {
                        "height": 50,
                        "width": 4
                    },
                    "type": "CHART",
                },
                "DASHBOARD_CHART_TYPE-1": {
                    "children": [],
                    "id": "DASHBOARD_CHART_TYPE-1",
                    "meta": {
                        "height": 50,
                        "width": 4
                    },
                    "type": "CHART",
                },
                "DASHBOARD_CHART_TYPE-2": {
                    "children": [],
                    "id": "DASHBOARD_CHART_TYPE-2",
                    "meta": {
                        "height": 50,
                        "width": 4
                    },
                    "type": "CHART",
                },
                "DASHBOARD_CHART_TYPE-3": {
                    "children": [],
                    "id": "DASHBOARD_CHART_TYPE-3",
                    "meta": {
                        "height": 50,
                        "width": 4
                    },
                    "type": "CHART",
                },
                "DASHBOARD_CHART_TYPE-4": {
                    "children": [],
                    "id": "DASHBOARD_CHART_TYPE-4",
                    "meta": {
                        "height": 50,
                        "width": 4
                    },
                    "type": "CHART",
                },
                "DASHBOARD_CHART_TYPE-5": {
                    "children": [],
                    "id": "DASHBOARD_CHART_TYPE-5",
                    "meta": {
                        "height": 50,
                        "width": 4
                    },
                    "type": "CHART",
                },
                "DASHBOARD_CHART_TYPE-6": {
                    "children": [],
                    "id": "DASHBOARD_CHART_TYPE-6",
                    "meta": {
                        "height": 50,
                        "width": 4
                    },
                    "type": "CHART",
                },
                "DASHBOARD_CHART_TYPE-7": {
                    "children": [],
                    "id": "DASHBOARD_CHART_TYPE-7",
                    "meta": {
                        "height": 50,
                        "width": 4
                    },
                    "type": "CHART",
                },
                "DASHBOARD_CHART_TYPE-8": {
                    "children": [],
                    "id": "DASHBOARD_CHART_TYPE-8",
                    "meta": {
                        "height": 50,
                        "width": 4
                    },
                    "type": "CHART",
                },
                "DASHBOARD_CHART_TYPE-9": {
                    "children": [],
                    "id": "DASHBOARD_CHART_TYPE-9",
                    "meta": {
                        "height": 50,
                        "width": 4
                    },
                    "type": "CHART",
                },
                "DASHBOARD_VERSION_KEY": "v2",
            },
            "metadata": {
                "timed_refresh_immune_slices": [],
                "expanded_slices": {},
                "refresh_frequency": 0,
                "default_filters": "{}",
                "color_scheme": None,
            },
            "version": "1.0.0",
        }
    def test_export_dashboard_command(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(
            slug="world_health").one())
        command = ExportDashboardsCommand([example_dashboard.id])
        contents = dict(command.run())

        expected_paths = {
            "metadata.yaml",
            "dashboards/World_Banks_Data.yaml",
            "datasets/examples/wb_health_population.yaml",
            "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["dashboards/World_Banks_Data.yaml"])

        # remove chart UUIDs from metadata so we can compare
        for chart_info in metadata["position"].values():
            if isinstance(chart_info, dict) and "uuid" in chart_info.get(
                    "meta", {}):
                del chart_info["meta"]["chartId"]
                del chart_info["meta"]["uuid"]

        assert metadata == {
            "dashboard_title": "World Bank's Data",
            "description": None,
            "css": None,
            "slug": "world_health",
            "uuid": str(example_dashboard.uuid),
            "position": {
                "CHART-36bfc934": {
                    "children": [],
                    "id": "CHART-36bfc934",
                    "meta": {
                        "height": 25,
                        "sliceName": "Region Filter",
                        "width": 2
                    },
                    "type": "CHART",
                },
                "CHART-37982887": {
                    "children": [],
                    "id": "CHART-37982887",
                    "meta": {
                        "height": 25,
                        "sliceName": "World's Population",
                        "width": 2,
                    },
                    "type": "CHART",
                },
                "CHART-17e0f8d8": {
                    "children": [],
                    "id": "CHART-17e0f8d8",
                    "meta": {
                        "height": 92,
                        "sliceName": "Most Populated Countries",
                        "width": 3,
                    },
                    "type": "CHART",
                },
                "CHART-2ee52f30": {
                    "children": [],
                    "id": "CHART-2ee52f30",
                    "meta": {
                        "height": 38,
                        "sliceName": "Growth Rate",
                        "width": 6
                    },
                    "type": "CHART",
                },
                "CHART-2d5b6871": {
                    "children": [],
                    "id": "CHART-2d5b6871",
                    "meta": {
                        "height": 52,
                        "sliceName": "% Rural",
                        "width": 7
                    },
                    "type": "CHART",
                },
                "CHART-0fd0d252": {
                    "children": [],
                    "id": "CHART-0fd0d252",
                    "meta": {
                        "height": 50,
                        "sliceName": "Life Expectancy VS Rural %",
                        "width": 8,
                    },
                    "type": "CHART",
                },
                "CHART-97f4cb48": {
                    "children": [],
                    "id": "CHART-97f4cb48",
                    "meta": {
                        "height": 38,
                        "sliceName": "Rural Breakdown",
                        "width": 3
                    },
                    "type": "CHART",
                },
                "CHART-b5e05d6f": {
                    "children": [],
                    "id": "CHART-b5e05d6f",
                    "meta": {
                        "height": 50,
                        "sliceName": "World's Pop Growth",
                        "width": 4,
                    },
                    "type": "CHART",
                },
                "CHART-e76e9f5f": {
                    "children": [],
                    "id": "CHART-e76e9f5f",
                    "meta": {
                        "height": 50,
                        "sliceName": "Box plot",
                        "width": 4
                    },
                    "type": "CHART",
                },
                "CHART-a4808bba": {
                    "children": [],
                    "id": "CHART-a4808bba",
                    "meta": {
                        "height": 50,
                        "sliceName": "Treemap",
                        "width": 8
                    },
                    "type": "CHART",
                },
                "CHART-3nc0d8sk": {
                    "children": [],
                    "id": "CHART-3nc0d8sk",
                    "meta": {
                        "height": 50,
                        "sliceName": "Treemap",
                        "width": 8
                    },
                    "type": "CHART",
                },
                "COLUMN-071bbbad": {
                    "children": ["ROW-1e064e3c", "ROW-afdefba9"],
                    "id": "COLUMN-071bbbad",
                    "meta": {
                        "background": "BACKGROUND_TRANSPARENT",
                        "width": 9
                    },
                    "type": "COLUMN",
                },
                "COLUMN-fe3914b8": {
                    "children": ["CHART-36bfc934", "CHART-37982887"],
                    "id": "COLUMN-fe3914b8",
                    "meta": {
                        "background": "BACKGROUND_TRANSPARENT",
                        "width": 2
                    },
                    "type": "COLUMN",
                },
                "GRID_ID": {
                    "children":
                    ["ROW-46632bc2", "ROW-3fa26c5d", "ROW-812b3f13"],
                    "id": "GRID_ID",
                    "type": "GRID",
                },
                "HEADER_ID": {
                    "id": "HEADER_ID",
                    "meta": {
                        "text": "World's Bank Data"
                    },
                    "type": "HEADER",
                },
                "ROOT_ID": {
                    "children": ["GRID_ID"],
                    "id": "ROOT_ID",
                    "type": "ROOT"
                },
                "ROW-1e064e3c": {
                    "children": ["COLUMN-fe3914b8", "CHART-2d5b6871"],
                    "id": "ROW-1e064e3c",
                    "meta": {
                        "background": "BACKGROUND_TRANSPARENT"
                    },
                    "type": "ROW",
                },
                "ROW-3fa26c5d": {
                    "children": ["CHART-b5e05d6f", "CHART-0fd0d252"],
                    "id": "ROW-3fa26c5d",
                    "meta": {
                        "background": "BACKGROUND_TRANSPARENT"
                    },
                    "type": "ROW",
                },
                "ROW-46632bc2": {
                    "children": ["COLUMN-071bbbad", "CHART-17e0f8d8"],
                    "id": "ROW-46632bc2",
                    "meta": {
                        "background": "BACKGROUND_TRANSPARENT"
                    },
                    "type": "ROW",
                },
                "ROW-812b3f13": {
                    "children": ["CHART-a4808bba", "CHART-e76e9f5f"],
                    "id": "ROW-812b3f13",
                    "meta": {
                        "background": "BACKGROUND_TRANSPARENT"
                    },
                    "type": "ROW",
                },
                "ROW-afdefba9": {
                    "children": ["CHART-2ee52f30", "CHART-97f4cb48"],
                    "id": "ROW-afdefba9",
                    "meta": {
                        "background": "BACKGROUND_TRANSPARENT"
                    },
                    "type": "ROW",
                },
                "DASHBOARD_VERSION_KEY": "v2",
            },
            "metadata": {
                "mock_key": "mock_value"
            },
            "version": "1.0.0",
        }