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

jli 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 de1dd53186 fix(theme-crud): enable overwrite confirmation UI for theme 
imports (#35558)
de1dd53186 is described below

commit de1dd5318675172fb0dc6c759dd5a2898e4d353e
Author: Gabriel Torres Ruiz <[email protected]>
AuthorDate: Wed Oct 15 21:15:57 2025 -0400

    fix(theme-crud): enable overwrite confirmation UI for theme imports (#35558)
---
 superset/themes/api.py                      |  12 +--
 tests/integration_tests/themes/api_tests.py | 122 ++++++++++++++++++++++++++++
 2 files changed, 125 insertions(+), 9 deletions(-)

diff --git a/superset/themes/api.py b/superset/themes/api.py
index 8314e4e15b..10b05d3a91 100644
--- a/superset/themes/api.py
+++ b/superset/themes/api.py
@@ -550,15 +550,9 @@ class ThemeRestApi(BaseSupersetModelRestApi):
 
         overwrite = request.form.get("overwrite") == "true"
 
-        try:
-            ImportThemesCommand(contents, overwrite=overwrite).run()
-            return self.response(200, message="Theme imported successfully")
-        except ValidationError as err:
-            logger.exception("Import themes validation error")
-            return self.response_400(message=str(err))
-        except Exception as ex:
-            logger.exception("Unexpected error importing themes")
-            return self.response_422(message=str(ex))
+        command = ImportThemesCommand(contents, overwrite=overwrite)
+        command.run()
+        return self.response(200, message="Theme imported successfully")
 
     @expose("/<int:pk>/set_system_default", methods=("PUT",))
     @protect()
diff --git a/tests/integration_tests/themes/api_tests.py 
b/tests/integration_tests/themes/api_tests.py
index 7828101fcf..387b9edf86 100644
--- a/tests/integration_tests/themes/api_tests.py
+++ b/tests/integration_tests/themes/api_tests.py
@@ -19,9 +19,14 @@
 
 import pytest
 import prison
+import uuid
+import yaml
 from datetime import datetime
 from freezegun import freeze_time
+from io import BytesIO
 from sqlalchemy.sql import func
+from typing import Any
+from zipfile import ZipFile
 
 import tests.integration_tests.test_app  # noqa: F401
 from superset import db
@@ -399,3 +404,120 @@ class TestThemeApi(SupersetTestCase):
         uri = f"api/v1/theme/?q={prison.dumps(theme_ids)}"
         rv = self.delete_assert_metric(uri, "bulk_delete")
         assert rv.status_code == 404
+
+    def create_theme_import_zip(self, theme_config: dict[str, Any]) -> BytesIO:
+        """Helper method to create a theme import ZIP file"""
+        buf = BytesIO()
+        with ZipFile(buf, "w") as bundle:
+            # Use a root folder like the export does
+            root = "theme_import"
+
+            # Add metadata.yaml
+            metadata = {
+                "version": "1.0.0",
+                "type": "Theme",
+                "timestamp": datetime.now().isoformat(),
+            }
+            with bundle.open(f"{root}/metadata.yaml", "w") as fp:
+                fp.write(yaml.safe_dump(metadata).encode())
+
+            # Add theme YAML file
+            theme_yaml = yaml.safe_dump(theme_config)
+            with bundle.open(
+                f"{root}/themes/{theme_config['theme_name']}.yaml", "w"
+            ) as fp:
+                fp.write(theme_yaml.encode())
+        buf.seek(0)
+        return buf
+
+    def test_import_theme(self):
+        """
+        Theme API: Test import theme
+        """
+        theme_config = {
+            "theme_name": "imported_theme",
+            "uuid": str(uuid.uuid4()),
+            "version": "1.0.0",
+            "json_data": {"colors": {"primary": "#007bff"}},
+        }
+
+        self.login(ADMIN_USERNAME)
+        uri = "api/v1/theme/import/"
+
+        buf = self.create_theme_import_zip(theme_config)
+        form_data = {
+            "formData": (buf, "theme_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": "Theme imported successfully"}
+
+        theme = 
db.session.query(Theme).filter_by(uuid=theme_config["uuid"]).one()
+        assert theme.theme_name == "imported_theme"
+
+        # Cleanup
+        db.session.delete(theme)
+        db.session.commit()
+
+    def test_import_theme_overwrite(self):
+        """
+        Theme API: Test import existing theme without and with overwrite
+        """
+        theme_config = {
+            "theme_name": "overwrite_theme",
+            "uuid": str(uuid.uuid4()),
+            "version": "1.0.0",
+            "json_data": {"colors": {"primary": "#007bff"}},
+        }
+
+        self.login(ADMIN_USERNAME)
+        uri = "api/v1/theme/import/"
+
+        # First import
+        buf = self.create_theme_import_zip(theme_config)
+        form_data = {
+            "formData": (buf, "theme_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": "Theme imported successfully"}
+
+        # Import again without overwrite flag - should fail with structured 
error
+        buf = self.create_theme_import_zip(theme_config)
+        form_data = {
+            "formData": (buf, "theme_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 == 422
+        assert len(response["errors"]) == 1
+        error = response["errors"][0]
+        assert error["message"].startswith("Error importing theme")
+        assert error["error_type"] == "GENERIC_COMMAND_ERROR"
+        assert error["level"] == "warning"
+        assert f"themes/{theme_config['theme_name']}.yaml" in 
str(error["extra"])
+        assert "Theme already exists and `overwrite=true` was not passed" in 
str(
+            error["extra"]
+        )
+
+        # Import with overwrite flag - should succeed
+        buf = self.create_theme_import_zip(theme_config)
+        form_data = {
+            "formData": (buf, "theme_export.zip"),
+            "overwrite": "true",
+        }
+        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": "Theme imported successfully"}
+
+        # Cleanup
+        theme = 
db.session.query(Theme).filter_by(uuid=theme_config["uuid"]).one()
+        db.session.delete(theme)
+        db.session.commit()

Reply via email to