mirror of
https://github.com/apache/superset.git
synced 2026-07-25 16:12:39 +00:00
feat(i18n): serve language packs as versioned, immutable-cacheable scripts
Follow-up to #39357, which inlined the Jed language pack into the bootstrap HTML to fix the code-split translation race (#35330). That made non-English full-page loads carry ~67KB (gzipped) of pack on every request since HTML is uncacheable. This keeps the synchronous-configuration guarantee but moves the pack to a content-addressed script: spa.html emits <script src="/language_pack/<lang>/<version>/script.js"> before the entry bundle, where <version> is a short content hash of the pack file. The endpoint serves window.__SUPERSET_LANGUAGE_PACK__ = {...} with Cache-Control: public, max-age=31536000, immutable, so browsers fetch the pack once per translation change instead of once per page load, and cache-bust automatically on upgrade (fixing the stale-pack window the unversioned /language_pack/<lang>/ fetch had under the 1-year SEND_FILE_MAX_AGE_DEFAULT). Details: - English emits no tag and pays nothing. - A stale version in a cached HTML page still gets current content, served no-cache so it can't pin under the wrong address. - Packs provided via COMMON_BOOTSTRAP_OVERRIDES_FUNC still ride the bootstrap payload and suppress the script tag (spa.html stashes them on window instead), preserving the historical workaround. - The endpoint is deliberately unauthenticated: catalogs are static public repo content with no user data, and must load for anonymous principals (login page, embedded). - preamble.ts fallback chain unchanged: bootstrap pack, window global, then async fetch if the script failed to load. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
committed by
Amin Ghadersohi
parent
83826839a0
commit
504c3df43f
77
tests/unit_tests/views/test_language_pack_script.py
Normal file
77
tests/unit_tests/views/test_language_pack_script.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from typing import Any
|
||||
from unittest.mock import patch
|
||||
|
||||
FAKE_PACK = {"domain": "superset", "locale_data": {"superset": {"": {}}}}
|
||||
FAKE_VERSION = "abc123def456"
|
||||
|
||||
|
||||
def test_script_served_immutable_when_version_matches(client: Any) -> None:
|
||||
with (
|
||||
patch(
|
||||
"superset.views.core.get_language_pack_version",
|
||||
return_value=FAKE_VERSION,
|
||||
),
|
||||
patch("superset.views.core.get_language_pack", return_value=FAKE_PACK),
|
||||
):
|
||||
response = client.get(f"/language_pack/fr/{FAKE_VERSION}/script.js")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.mimetype == "application/javascript"
|
||||
body = response.get_data(as_text=True)
|
||||
assert body.startswith("window.__SUPERSET_LANGUAGE_PACK__ = ")
|
||||
assert '"domain": "superset"' in body
|
||||
cache_control = response.headers["Cache-Control"]
|
||||
assert "immutable" in cache_control
|
||||
assert "max-age=31536000" in cache_control
|
||||
assert "public" in cache_control
|
||||
|
||||
|
||||
def test_script_not_cacheable_when_version_stale(client: Any) -> None:
|
||||
"""A pre-upgrade HTML page may reference an old version: serve fresh
|
||||
content, but do not let caches pin it under the stale address."""
|
||||
with (
|
||||
patch(
|
||||
"superset.views.core.get_language_pack_version",
|
||||
return_value=FAKE_VERSION,
|
||||
),
|
||||
patch("superset.views.core.get_language_pack", return_value=FAKE_PACK),
|
||||
):
|
||||
response = client.get("/language_pack/fr/000000000000/script.js")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "no-cache" in response.headers["Cache-Control"]
|
||||
assert "immutable" not in response.headers["Cache-Control"]
|
||||
|
||||
|
||||
def test_script_404_when_pack_missing(client: Any) -> None:
|
||||
with patch(
|
||||
"superset.views.core.get_language_pack_version",
|
||||
return_value=None,
|
||||
):
|
||||
response = client.get(f"/language_pack/xx/{FAKE_VERSION}/script.js")
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_script_rejects_malformed_lang_and_version(client: Any) -> None:
|
||||
assert client.get(f"/language_pack/../{FAKE_VERSION}/script.js").status_code in (
|
||||
400,
|
||||
404,
|
||||
)
|
||||
assert client.get("/language_pack/fr/not-a-hash!/script.js").status_code == 400
|
||||
Reference in New Issue
Block a user