Compare commits

...
Author SHA1 Message Date
Elizabeth Thompson de09b6bf21 fix(jinja): reuse get_request_json_body in get_dataset_id_from_context (SC-118456)
get_dataset_id_from_context (used by the metric() Jinja macro) read the
ambient Flask request via a raw request.get_json() call guarded only by
request.is_json, which checks the Content-Type header but not whether the
body actually parses as JSON. When the metric() macro is evaluated from
the MCP get_chart_data tool, the ambient request is the raw MCP transport
request -- Content-Type: application/json, but not a chart-data JSON
payload -- so request.get_json() raised an unhandled Werkzeug BadRequest.

get_form_data() had the identical pattern and was already fixed for this
in #42196, which added views/utils.get_request_json_body() to coerce a
non-JSON body to {} instead of raising. get_dataset_id_from_context() has
its own separate copy of the old pattern and was missed by that fix.

Switches to the existing helper instead of duplicating the guard.

Fixes SUPERSET-PYTHON-1516
2026-08-26 15:07:56 +00:00
2 changed files with 27 additions and 2 deletions
+2 -2
View File
@@ -1293,7 +1293,7 @@ def get_dataset_id_from_context(metric_key: str) -> int:
"""
# pylint: disable=import-outside-toplevel
from superset.daos.chart import ChartDAO
from superset.views.utils import loads_request_json
from superset.views.utils import get_request_json_body, loads_request_json
form_data: dict[str, Any] = {}
exc_message = _(
@@ -1302,7 +1302,7 @@ def get_dataset_id_from_context(metric_key: str) -> int:
)
if has_request_context():
if payload := request.get_json(cache=True) if request.is_json else None:
if payload := get_request_json_body():
if dataset_id := payload.get("datasource", {}).get("id"):
return dataset_id
form_data.update(payload.get("form_data", {}))
+25
View File
@@ -1441,6 +1441,31 @@ def test_metric_macro_no_dataset_id_no_context(mocker: MockerFixture) -> None:
DatasetDAO.find_by_id.assert_not_called()
def test_metric_macro_no_dataset_id_non_json_body_with_json_content_type(
mocker: MockerFixture,
) -> None:
"""
Test the ``metric_macro`` when the request context's Content-Type claims
JSON but the body isn't parseable JSON -- the shape of the request an MCP
tool call runs in. Previously ``request.get_json()`` let a raw Werkzeug
``BadRequest`` escape here instead of falling through to the
dataset-not-specified path.
"""
DatasetDAO = mocker.patch("superset.daos.dataset.DatasetDAO") # noqa: N806
mock_g = mocker.patch("superset.jinja_context.g")
mock_g.form_data = {}
env = SandboxedEnvironment(undefined=DebugUndefined)
with current_app.test_request_context(
data="not-json-at-all", content_type="application/json"
):
with pytest.raises(SupersetTemplateException) as excinfo:
metric_macro(env, {}, "macro_key")
assert str(excinfo.value) == (
"Please specify the Dataset ID for the ``macro_key`` metric in the Jinja macro." # noqa: E501
)
DatasetDAO.find_by_id.assert_not_called()
def test_metric_macro_no_dataset_id_with_context_missing_info(
mocker: MockerFixture,
) -> None: