fix: add subdirectory deployment support for brandSpinnerUrl (#37523)

Co-authored-by: Evan Rusackas <evan@preset.io>
This commit is contained in:
innovark
2026-07-14 00:01:14 +03:00
committed by GitHub
parent 18f1dd394b
commit d4a31d1d78
2 changed files with 159 additions and 0 deletions

View File

@@ -649,6 +649,27 @@ def get_spa_payload(extra_data: dict[str, Any] | None = None) -> dict[str, Any]:
return payload
def _ensure_static_assets_prefix(url_or_path: str) -> str:
"""Add the configured static asset prefix to root-relative asset paths."""
static_assets_prefix = app.config.get("STATIC_ASSETS_PREFIX", "")
if (
url_or_path.startswith("//")
or not url_or_path.startswith("/")
or not static_assets_prefix
):
return url_or_path
normalized_prefix = static_assets_prefix.rstrip("/")
if normalized_prefix and (
url_or_path == normalized_prefix
or url_or_path.startswith(f"{normalized_prefix}/")
):
return url_or_path
return f"{normalized_prefix}{url_or_path}"
def get_spa_template_context(
entry: str | None = "spa",
extra_bootstrap_data: dict[str, Any] | None = None,
@@ -694,6 +715,12 @@ def get_spa_template_context(
# User has customized APP_NAME, use it as brandAppName
theme_tokens["brandAppName"] = app_name_from_config
brand_spinner_url = theme_tokens.get("brandSpinnerUrl")
if isinstance(brand_spinner_url, str) and brand_spinner_url:
theme_tokens["brandSpinnerUrl"] = _ensure_static_assets_prefix(
brand_spinner_url
)
# Write the modified theme data back to payload
if "common" not in payload:
payload["common"] = {}

View File

@@ -18,6 +18,7 @@
from unittest.mock import MagicMock, mock_open, patch
from superset.themes.types import ThemeMode
from superset.utils import json
from superset.views.base import (
_load_theme_from_model,
_merge_theme_dicts,
@@ -892,6 +893,137 @@ class TestBrandAppNameFallback:
assert result["default_title"] == "Superset"
class TestBrandSpinnerUrlPrefix:
"""Test brandSpinnerUrl static asset prefix handling."""
@patch("superset.views.base.get_spa_payload")
@patch("superset.views.base.app")
def test_brandspinnerurl_adds_static_assets_prefix(self, mock_app, mock_payload):
"""Test that root-relative spinner URLs include the static assets prefix."""
from superset.views.base import get_spa_template_context
mock_app.config = {
"STATIC_ASSETS_PREFIX": "/analytics",
}
mock_payload.return_value = {
"common": {
"theme": {
"default": {
"token": {
"brandSpinnerUrl": "/static/assets/spinner.gif",
}
},
"dark": {
"token": {
"brandSpinnerUrl": "/static/assets/dark-spinner.gif",
}
},
}
}
}
result = get_spa_template_context("app")
assert (
result["theme_tokens"]["brandSpinnerUrl"]
== "/analytics/static/assets/spinner.gif"
)
bootstrap_data = json.loads(result["bootstrap_data"])
assert (
bootstrap_data["common"]["theme"]["default"]["token"]["brandSpinnerUrl"]
== "/analytics/static/assets/spinner.gif"
)
assert (
bootstrap_data["common"]["theme"]["dark"]["token"]["brandSpinnerUrl"]
== "/analytics/static/assets/dark-spinner.gif"
)
@patch("superset.views.base.get_spa_payload")
@patch("superset.views.base.app")
def test_brandspinnerurl_keeps_absolute_url(self, mock_app, mock_payload):
"""Test that absolute spinner URLs are not prefixed."""
from superset.views.base import get_spa_template_context
mock_app.config = {
"STATIC_ASSETS_PREFIX": "/analytics",
}
mock_payload.return_value = {
"common": {
"theme": {
"default": {
"token": {
"brandSpinnerUrl": "https://cdn.example.com/spinner.gif",
}
}
}
}
}
result = get_spa_template_context("app")
assert (
result["theme_tokens"]["brandSpinnerUrl"]
== "https://cdn.example.com/spinner.gif"
)
@patch("superset.views.base.get_spa_payload")
@patch("superset.views.base.app")
def test_brandspinnerurl_keeps_protocol_relative_url(self, mock_app, mock_payload):
"""Test that protocol-relative spinner URLs are not prefixed."""
from superset.views.base import get_spa_template_context
mock_app.config = {
"STATIC_ASSETS_PREFIX": "/analytics",
}
mock_payload.return_value = {
"common": {
"theme": {
"default": {
"token": {
"brandSpinnerUrl": "//cdn.example.com/spinner.gif",
}
}
}
}
}
result = get_spa_template_context("app")
assert (
result["theme_tokens"]["brandSpinnerUrl"] == "//cdn.example.com/spinner.gif"
)
@patch("superset.views.base.get_spa_payload")
@patch("superset.views.base.app")
def test_brandspinnerurl_does_not_duplicate_static_assets_prefix(
self, mock_app, mock_payload
):
"""Test that already-prefixed spinner URLs are not prefixed again."""
from superset.views.base import get_spa_template_context
mock_app.config = {
"STATIC_ASSETS_PREFIX": "/analytics",
}
mock_payload.return_value = {
"common": {
"theme": {
"default": {
"token": {
"brandSpinnerUrl": "/analytics/static/assets/spinner.gif",
}
}
}
}
}
result = get_spa_template_context("app")
assert (
result["theme_tokens"]["brandSpinnerUrl"]
== "/analytics/static/assets/spinner.gif"
)
class TestGetDefaultSpinnerSvg:
"""Test get_default_spinner_svg function"""