diff --git a/superset/charts/api.py b/superset/charts/api.py index cb6a33dff..75473e71a 100644 --- a/superset/charts/api.py +++ b/superset/charts/api.py @@ -61,6 +61,7 @@ from superset.charts.schemas import ( thumbnail_query_schema, ) from superset.commands.exceptions import CommandInvalidError +from superset.commands.importers.v1.utils import remove_root from superset.constants import RouteMethod from superset.exceptions import SupersetSecurityException from superset.extensions import event_logger @@ -869,12 +870,12 @@ class ChartRestApi(BaseSupersetModelRestApi): 500: $ref: '#/components/responses/500' """ - upload = request.files.get("file") + upload = request.files.get("formData") if not upload: return self.response_400() with ZipFile(upload) as bundle: contents = { - file_name: bundle.read(file_name).decode() + remove_root(file_name): bundle.read(file_name).decode() for file_name in bundle.namelist() } diff --git a/superset/commands/importers/v1/utils.py b/superset/commands/importers/v1/utils.py index c9475d83e..623911246 100644 --- a/superset/commands/importers/v1/utils.py +++ b/superset/commands/importers/v1/utils.py @@ -14,6 +14,7 @@ # under the License. import logging +from pathlib import Path from typing import Any, Dict import yaml @@ -28,6 +29,13 @@ IMPORT_VERSION = "1.0.0" logger = logging.getLogger(__name__) +def remove_root(file_path: str) -> str: + """Remove the first directory of a path""" + full_path = Path(file_path) + relative_path = Path(*full_path.parts[1:]) + return str(relative_path) + + class MetadataSchema(Schema): version = fields.String(required=True, validate=validate.Equal(IMPORT_VERSION)) type = fields.String(required=True) @@ -39,14 +47,14 @@ def load_yaml(file_name: str, content: str) -> Dict[str, Any]: try: return yaml.safe_load(content) except yaml.parser.ParserError: - logger.exception("Invalid YAML in %s", METADATA_FILE_NAME) + logger.exception("Invalid YAML in %s", file_name) raise ValidationError({file_name: "Not a valid YAML file"}) def load_metadata(contents: Dict[str, str]) -> Dict[str, str]: """Apply validation and load a metadata file""" if METADATA_FILE_NAME not in contents: - # if the contents ahve no METADATA_FILE_NAME this is probably + # if the contents have no METADATA_FILE_NAME this is probably # a original export without versioning that should not be # handled by this command raise IncorrectVersionError(f"Missing {METADATA_FILE_NAME}") diff --git a/superset/dashboards/api.py b/superset/dashboards/api.py index da8e2d14d..7cd918c2f 100644 --- a/superset/dashboards/api.py +++ b/superset/dashboards/api.py @@ -30,6 +30,7 @@ from werkzeug.wsgi import FileWrapper from superset import is_feature_enabled, thumbnail_cache from superset.commands.exceptions import CommandInvalidError +from superset.commands.importers.v1.utils import remove_root from superset.constants import RouteMethod from superset.dashboards.commands.bulk_delete import BulkDeleteDashboardCommand from superset.dashboards.commands.create import CreateDashboardCommand @@ -687,12 +688,12 @@ class DashboardRestApi(BaseSupersetModelRestApi): 500: $ref: '#/components/responses/500' """ - upload = request.files.get("file") + upload = request.files.get("formData") if not upload: return self.response_400() with ZipFile(upload) as bundle: contents = { - file_name: bundle.read(file_name).decode() + remove_root(file_name): bundle.read(file_name).decode() for file_name in bundle.namelist() } diff --git a/superset/databases/api.py b/superset/databases/api.py index 51c2edacc..892d3ef8c 100644 --- a/superset/databases/api.py +++ b/superset/databases/api.py @@ -36,6 +36,7 @@ from sqlalchemy.exc import ( from superset import event_logger from superset.commands.exceptions import CommandInvalidError +from superset.commands.importers.v1.utils import remove_root from superset.constants import RouteMethod from superset.databases.commands.create import CreateDatabaseCommand from superset.databases.commands.delete import DeleteDatabaseCommand @@ -766,12 +767,12 @@ class DatabaseRestApi(BaseSupersetModelRestApi): 500: $ref: '#/components/responses/500' """ - upload = request.files.get("file") + upload = request.files.get("formData") if not upload: return self.response_400() with ZipFile(upload) as bundle: contents = { - file_name: bundle.read(file_name).decode() + remove_root(file_name): bundle.read(file_name).decode() for file_name in bundle.namelist() } diff --git a/superset/datasets/api.py b/superset/datasets/api.py index cd2c59484..e5ddae5d0 100644 --- a/superset/datasets/api.py +++ b/superset/datasets/api.py @@ -29,6 +29,7 @@ from marshmallow import ValidationError from superset import event_logger, is_feature_enabled from superset.commands.exceptions import CommandInvalidError +from superset.commands.importers.v1.utils import remove_root from superset.connectors.sqla.models import SqlaTable from superset.constants import RouteMethod from superset.databases.filters import DatabaseFilter @@ -634,12 +635,12 @@ class DatasetRestApi(BaseSupersetModelRestApi): 500: $ref: '#/components/responses/500' """ - upload = request.files.get("file") + upload = request.files.get("formData") if not upload: return self.response_400() with ZipFile(upload) as bundle: contents = { - file_name: bundle.read(file_name).decode() + remove_root(file_name): bundle.read(file_name).decode() for file_name in bundle.namelist() } diff --git a/tests/charts/api_tests.py b/tests/charts/api_tests.py index 3acfdb9ca..4e44bcc43 100644 --- a/tests/charts/api_tests.py +++ b/tests/charts/api_tests.py @@ -1187,18 +1187,20 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin): buf = BytesIO() with ZipFile(buf, "w") as bundle: - with bundle.open("metadata.yaml", "w") as fp: + with bundle.open("chart_export/metadata.yaml", "w") as fp: fp.write(yaml.safe_dump(chart_metadata_config).encode()) - with bundle.open("databases/imported_database.yaml", "w") as fp: + with bundle.open( + "chart_export/databases/imported_database.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(database_config).encode()) - with bundle.open("datasets/imported_dataset.yaml", "w") as fp: + with bundle.open("chart_export/datasets/imported_dataset.yaml", "w") as fp: fp.write(yaml.safe_dump(dataset_config).encode()) - with bundle.open("charts/imported_chart.yaml", "w") as fp: + with bundle.open("chart_export/charts/imported_chart.yaml", "w") as fp: fp.write(yaml.safe_dump(chart_config).encode()) buf.seek(0) form_data = { - "file": (buf, "chart_export.zip"), + "formData": (buf, "chart_export.zip"), } rv = self.client.post(uri, data=form_data, content_type="multipart/form-data") response = json.loads(rv.data.decode("utf-8")) @@ -1233,18 +1235,20 @@ class TestChartApi(SupersetTestCase, ApiOwnersTestCaseMixin): buf = BytesIO() with ZipFile(buf, "w") as bundle: - with bundle.open("metadata.yaml", "w") as fp: + with bundle.open("chart_export/metadata.yaml", "w") as fp: fp.write(yaml.safe_dump(dataset_metadata_config).encode()) - with bundle.open("databases/imported_database.yaml", "w") as fp: + with bundle.open( + "chart_export/databases/imported_database.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(database_config).encode()) - with bundle.open("datasets/imported_dataset.yaml", "w") as fp: + with bundle.open("chart_export/datasets/imported_dataset.yaml", "w") as fp: fp.write(yaml.safe_dump(dataset_config).encode()) - with bundle.open("charts/imported_chart.yaml", "w") as fp: + with bundle.open("chart_export/charts/imported_chart.yaml", "w") as fp: fp.write(yaml.safe_dump(chart_config).encode()) buf.seek(0) form_data = { - "file": (buf, "chart_export.zip"), + "formData": (buf, "chart_export.zip"), } rv = self.client.post(uri, data=form_data, content_type="multipart/form-data") response = json.loads(rv.data.decode("utf-8")) diff --git a/tests/dashboards/api_tests.py b/tests/dashboards/api_tests.py index 72aee218e..24d461d1d 100644 --- a/tests/dashboards/api_tests.py +++ b/tests/dashboards/api_tests.py @@ -1096,20 +1096,26 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin): buf = BytesIO() with ZipFile(buf, "w") as bundle: - with bundle.open("metadata.yaml", "w") as fp: + with bundle.open("dashboard_export/metadata.yaml", "w") as fp: fp.write(yaml.safe_dump(dashboard_metadata_config).encode()) - with bundle.open("databases/imported_database.yaml", "w") as fp: + with bundle.open( + "dashboard_export/databases/imported_database.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(database_config).encode()) - with bundle.open("datasets/imported_dataset.yaml", "w") as fp: + with bundle.open( + "dashboard_export/datasets/imported_dataset.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(dataset_config).encode()) - with bundle.open("charts/imported_chart.yaml", "w") as fp: + with bundle.open("dashboard_export/charts/imported_chart.yaml", "w") as fp: fp.write(yaml.safe_dump(chart_config).encode()) - with bundle.open("dashboards/imported_dashboard.yaml", "w") as fp: + with bundle.open( + "dashboard_export/dashboards/imported_dashboard.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(dashboard_config).encode()) buf.seek(0) form_data = { - "file": (buf, "dashboard_export.zip"), + "formData": (buf, "dashboard_export.zip"), } rv = self.client.post(uri, data=form_data, content_type="multipart/form-data") response = json.loads(rv.data.decode("utf-8")) @@ -1147,20 +1153,26 @@ class TestDashboardApi(SupersetTestCase, ApiOwnersTestCaseMixin): buf = BytesIO() with ZipFile(buf, "w") as bundle: - with bundle.open("metadata.yaml", "w") as fp: + with bundle.open("dashboard_export/metadata.yaml", "w") as fp: fp.write(yaml.safe_dump(dataset_metadata_config).encode()) - with bundle.open("databases/imported_database.yaml", "w") as fp: + with bundle.open( + "dashboard_export/databases/imported_database.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(database_config).encode()) - with bundle.open("datasets/imported_dataset.yaml", "w") as fp: + with bundle.open( + "dashboard_export/datasets/imported_dataset.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(dataset_config).encode()) - with bundle.open("charts/imported_chart.yaml", "w") as fp: + with bundle.open("dashboard_export/charts/imported_chart.yaml", "w") as fp: fp.write(yaml.safe_dump(chart_config).encode()) - with bundle.open("dashboards/imported_dashboard.yaml", "w") as fp: + with bundle.open( + "dashboard_export/dashboards/imported_dashboard.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(dashboard_config).encode()) buf.seek(0) form_data = { - "file": (buf, "dashboard_export.zip"), + "formData": (buf, "dashboard_export.zip"), } rv = self.client.post(uri, data=form_data, content_type="multipart/form-data") response = json.loads(rv.data.decode("utf-8")) diff --git a/tests/databases/api_tests.py b/tests/databases/api_tests.py index 88213b6cc..9a290e159 100644 --- a/tests/databases/api_tests.py +++ b/tests/databases/api_tests.py @@ -836,16 +836,20 @@ class TestDatabaseApi(SupersetTestCase): buf = BytesIO() with ZipFile(buf, "w") as bundle: - with bundle.open("metadata.yaml", "w") as fp: + with bundle.open("database_export/metadata.yaml", "w") as fp: fp.write(yaml.safe_dump(database_metadata_config).encode()) - with bundle.open("databases/imported_database.yaml", "w") as fp: + with bundle.open( + "database_export/databases/imported_database.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(database_config).encode()) - with bundle.open("datasets/imported_dataset.yaml", "w") as fp: + with bundle.open( + "database_export/datasets/imported_dataset.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(dataset_config).encode()) buf.seek(0) form_data = { - "file": (buf, "database_export.zip"), + "formData": (buf, "database_export.zip"), } rv = self.client.post(uri, data=form_data, content_type="multipart/form-data") response = json.loads(rv.data.decode("utf-8")) @@ -876,16 +880,20 @@ class TestDatabaseApi(SupersetTestCase): buf = BytesIO() with ZipFile(buf, "w") as bundle: - with bundle.open("metadata.yaml", "w") as fp: + with bundle.open("database_export/metadata.yaml", "w") as fp: fp.write(yaml.safe_dump(dataset_metadata_config).encode()) - with bundle.open("databases/imported_database.yaml", "w") as fp: + with bundle.open( + "database_export/databases/imported_database.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(database_config).encode()) - with bundle.open("datasets/imported_dataset.yaml", "w") as fp: + with bundle.open( + "database_export/datasets/imported_dataset.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(dataset_config).encode()) buf.seek(0) form_data = { - "file": (buf, "database_export.zip"), + "formData": (buf, "database_export.zip"), } rv = self.client.post(uri, data=form_data, content_type="multipart/form-data") response = json.loads(rv.data.decode("utf-8")) diff --git a/tests/datasets/api_tests.py b/tests/datasets/api_tests.py index ea16c61cf..37ef6a46f 100644 --- a/tests/datasets/api_tests.py +++ b/tests/datasets/api_tests.py @@ -1185,16 +1185,20 @@ class TestDatasetApi(SupersetTestCase): buf = BytesIO() with ZipFile(buf, "w") as bundle: - with bundle.open("metadata.yaml", "w") as fp: + with bundle.open("dataset_export/metadata.yaml", "w") as fp: fp.write(yaml.safe_dump(dataset_metadata_config).encode()) - with bundle.open("databases/imported_database.yaml", "w") as fp: + with bundle.open( + "dataset_export/databases/imported_database.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(database_config).encode()) - with bundle.open("datasets/imported_dataset.yaml", "w") as fp: + with bundle.open( + "dataset_export/datasets/imported_dataset.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(dataset_config).encode()) buf.seek(0) form_data = { - "file": (buf, "dataset_export.zip"), + "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")) @@ -1225,16 +1229,20 @@ class TestDatasetApi(SupersetTestCase): buf = BytesIO() with ZipFile(buf, "w") as bundle: - with bundle.open("metadata.yaml", "w") as fp: + with bundle.open("dataset_export/metadata.yaml", "w") as fp: fp.write(yaml.safe_dump(database_metadata_config).encode()) - with bundle.open("databases/imported_database.yaml", "w") as fp: + with bundle.open( + "dataset_export/databases/imported_database.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(database_config).encode()) - with bundle.open("datasets/imported_dataset.yaml", "w") as fp: + with bundle.open( + "dataset_export/datasets/imported_dataset.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(dataset_config).encode()) buf.seek(0) form_data = { - "file": (buf, "dataset_export.zip"), + "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")) @@ -1253,14 +1261,18 @@ class TestDatasetApi(SupersetTestCase): buf = BytesIO() with ZipFile(buf, "w") as bundle: - with bundle.open("databases/imported_database.yaml", "w") as fp: + with bundle.open( + "dataset_export/databases/imported_database.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(database_config).encode()) - with bundle.open("datasets/imported_dataset.yaml", "w") as fp: + with bundle.open( + "dataset_export/datasets/imported_dataset.yaml", "w" + ) as fp: fp.write(yaml.safe_dump(dataset_config).encode()) buf.seek(0) form_data = { - "file": (buf, "dataset_export.zip"), + "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"))