You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by be...@apache.org on 2021/03/05 01:19:06 UTC
[superset] branch master updated: fix: API to allow importing old
exports (JSON/YAML) (#13444)
This is an automated email from the ASF dual-hosted git repository.
beto pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 9fc03f0 fix: API to allow importing old exports (JSON/YAML) (#13444)
9fc03f0 is described below
commit 9fc03f042402d56f27b9e3ef26ed54139e725749
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Thu Mar 4 17:18:27 2021 -0800
fix: API to allow importing old exports (JSON/YAML) (#13444)
* fix: fix API to allow importing old exports (JSON/YAML)
* Fix test
* Fix lint
* Add description to API schema
---
superset/charts/api.py | 3 +++
superset/dashboards/api.py | 13 +++++++++---
superset/dashboards/commands/importers/v0.py | 5 ++++-
superset/databases/api.py | 3 +++
superset/datasets/api.py | 13 +++++++++---
tests/dashboards/api_tests.py | 31 ++++++++++++++++++++++++++++
tests/datasets/api_tests.py | 26 +++++++++++++++++++++++
7 files changed, 87 insertions(+), 7 deletions(-)
diff --git a/superset/charts/api.py b/superset/charts/api.py
index c84a843..7c756f1 100644
--- a/superset/charts/api.py
+++ b/superset/charts/api.py
@@ -993,11 +993,14 @@ class ChartRestApi(BaseSupersetModelRestApi):
type: object
properties:
formData:
+ description: upload file (ZIP)
type: string
format: binary
passwords:
+ description: JSON map of passwords for each file
type: string
overwrite:
+ description: overwrite existing databases?
type: bool
responses:
200:
diff --git a/superset/dashboards/api.py b/superset/dashboards/api.py
index 7678711..79dbc97 100644
--- a/superset/dashboards/api.py
+++ b/superset/dashboards/api.py
@@ -19,7 +19,7 @@ import logging
from datetime import datetime
from io import BytesIO
from typing import Any, Dict
-from zipfile import ZipFile
+from zipfile import is_zipfile, ZipFile
from flask import g, make_response, redirect, request, Response, send_file, url_for
from flask_appbuilder.api import expose, protect, rison, safe
@@ -759,11 +759,14 @@ class DashboardRestApi(BaseSupersetModelRestApi):
type: object
properties:
formData:
+ description: upload file (ZIP or JSON)
type: string
format: binary
passwords:
+ description: JSON map of passwords for each file
type: string
overwrite:
+ description: overwrite existing databases?
type: bool
responses:
200:
@@ -787,8 +790,12 @@ class DashboardRestApi(BaseSupersetModelRestApi):
upload = request.files.get("formData")
if not upload:
return self.response_400()
- with ZipFile(upload) as bundle:
- contents = get_contents_from_bundle(bundle)
+ if is_zipfile(upload):
+ with ZipFile(upload) as bundle:
+ contents = get_contents_from_bundle(bundle)
+ else:
+ upload.seek(0)
+ contents = {upload.filename: upload.read()}
passwords = (
json.loads(request.form["passwords"])
diff --git a/superset/dashboards/commands/importers/v0.py b/superset/dashboards/commands/importers/v0.py
index 851ecab..3b164fe 100644
--- a/superset/dashboards/commands/importers/v0.py
+++ b/superset/dashboards/commands/importers/v0.py
@@ -317,7 +317,10 @@ class ImportDashboardsCommand(BaseCommand):
in Superset.
"""
- def __init__(self, contents: Dict[str, str], database_id: Optional[int] = None):
+ # pylint: disable=unused-argument
+ def __init__(
+ self, contents: Dict[str, str], database_id: Optional[int] = None, **kwargs: Any
+ ):
self.contents = contents
self.database_id = database_id
diff --git a/superset/databases/api.py b/superset/databases/api.py
index ba665c1..1c92ab7 100644
--- a/superset/databases/api.py
+++ b/superset/databases/api.py
@@ -751,11 +751,14 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
type: object
properties:
formData:
+ description: upload file (ZIP)
type: string
format: binary
passwords:
+ description: JSON map of passwords for each file
type: string
overwrite:
+ description: overwrite existing databases?
type: bool
responses:
200:
diff --git a/superset/datasets/api.py b/superset/datasets/api.py
index 96f3532..85adae5 100644
--- a/superset/datasets/api.py
+++ b/superset/datasets/api.py
@@ -20,7 +20,7 @@ from datetime import datetime
from distutils.util import strtobool
from io import BytesIO
from typing import Any
-from zipfile import ZipFile
+from zipfile import is_zipfile, ZipFile
import yaml
from flask import g, request, Response, send_file
@@ -659,11 +659,14 @@ class DatasetRestApi(BaseSupersetModelRestApi):
type: object
properties:
formData:
+ description: upload file (ZIP or YAML)
type: string
format: binary
passwords:
+ description: JSON map of passwords for each file
type: string
overwrite:
+ description: overwrite existing databases?
type: bool
responses:
200:
@@ -687,8 +690,12 @@ class DatasetRestApi(BaseSupersetModelRestApi):
upload = request.files.get("formData")
if not upload:
return self.response_400()
- with ZipFile(upload) as bundle:
- contents = get_contents_from_bundle(bundle)
+ if is_zipfile(upload):
+ with ZipFile(upload) as bundle:
+ contents = get_contents_from_bundle(bundle)
+ else:
+ upload.seek(0)
+ contents = {upload.filename: upload.read()}
passwords = (
json.loads(request.form["passwords"])
diff --git a/tests/dashboards/api_tests.py b/tests/dashboards/api_tests.py
index a9b0ca4..7fdb7b0 100644
--- a/tests/dashboards/api_tests.py
+++ b/tests/dashboards/api_tests.py
@@ -45,6 +45,7 @@ from tests.fixtures.importexport import (
chart_config,
database_config,
dashboard_config,
+ dashboard_export,
dashboard_metadata_config,
dataset_config,
dataset_metadata_config,
@@ -1316,6 +1317,36 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin, InsertChartMixi
db.session.delete(database)
db.session.commit()
+ def test_import_dashboard_v0_export(self):
+ num_dashboards = db.session.query(Dashboard).count()
+
+ self.login(username="admin")
+ uri = "api/v1/dashboard/import/"
+
+ buf = BytesIO()
+ buf.write(json.dumps(dashboard_export).encode())
+ buf.seek(0)
+ form_data = {
+ "formData": (buf, "20201119_181105.json"),
+ }
+ rv = self.client.post(uri, data=form_data, content_type="multipart/form-data")
+ response = json.loads(rv.data.decode("utf-8"))
+
+ assert rv.status_code == 200
+ assert response == {"message": "OK"}
+ assert db.session.query(Dashboard).count() == num_dashboards + 1
+
+ dashboard = (
+ db.session.query(Dashboard).filter_by(dashboard_title="Births 2").one()
+ )
+ chart = dashboard.slices[0]
+ dataset = chart.table
+
+ db.session.delete(dashboard)
+ db.session.delete(chart)
+ db.session.delete(dataset)
+ db.session.commit()
+
def test_import_dashboard_overwrite(self):
"""
Dashboard API: Test import existing dashboard
diff --git a/tests/datasets/api_tests.py b/tests/datasets/api_tests.py
index 6b628d7..00be830 100644
--- a/tests/datasets/api_tests.py
+++ b/tests/datasets/api_tests.py
@@ -47,6 +47,7 @@ from tests.fixtures.importexport import (
database_metadata_config,
dataset_config,
dataset_metadata_config,
+ dataset_ui_export,
)
@@ -1275,6 +1276,31 @@ class TestDatasetApi(SupersetTestCase):
db.session.delete(database)
db.session.commit()
+ def test_import_dataset_v0_export(self):
+ num_datasets = db.session.query(SqlaTable).count()
+
+ self.login(username="admin")
+ uri = "api/v1/dataset/import/"
+
+ buf = BytesIO()
+ buf.write(json.dumps(dataset_ui_export).encode())
+ buf.seek(0)
+ form_data = {
+ "formData": (buf, "dataset_export.zip"),
+ }
+ rv = self.client.post(uri, data=form_data, content_type="multipart/form-data")
+ response = json.loads(rv.data.decode("utf-8"))
+
+ assert rv.status_code == 200
+ assert response == {"message": "OK"}
+ assert db.session.query(SqlaTable).count() == num_datasets + 1
+
+ dataset = (
+ db.session.query(SqlaTable).filter_by(table_name="birth_names_2").one()
+ )
+ db.session.delete(dataset)
+ db.session.commit()
+
def test_import_dataset_overwrite(self):
"""
Dataset API: Test import existing dataset