mirror of
https://github.com/apache/superset.git
synced 2026-08-12 11:11:01 +00:00
fix(chart API): Consider time grain filters with the filters_dashboard_id param (#41290)
This commit is contained in:
@@ -306,7 +306,7 @@ def get_dashboard_filter_context(
|
||||
return context
|
||||
|
||||
|
||||
def apply_dashboard_filter_context(
|
||||
def apply_dashboard_filter_context( # noqa: C901
|
||||
query_context: dict[str, Any],
|
||||
extra_form_data: dict[str, Any],
|
||||
) -> None:
|
||||
@@ -332,6 +332,17 @@ def apply_dashboard_filter_context(
|
||||
for key in EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS:
|
||||
if key in extra_form_data:
|
||||
extras[key] = extra_form_data[key]
|
||||
|
||||
# EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS is originally used with form_data objects,
|
||||
# not query_context objects. form_data objects expect time_grain_sqla as a
|
||||
# top-level key, but query_context objects expect it as an extra key.
|
||||
if custom_time_grain := extra_form_data.get("time_grain_sqla"):
|
||||
extras["time_grain_sqla"] = custom_time_grain
|
||||
# get_time_grain() resolves grain from the first adhoc column (columns[0])
|
||||
columns = query.get("columns") or []
|
||||
if columns and isinstance(columns[0], dict):
|
||||
columns[0]["timeGrain"] = custom_time_grain
|
||||
|
||||
if extras:
|
||||
query["extras"] = extras
|
||||
|
||||
|
||||
@@ -113,6 +113,102 @@ def test_apply_dashboard_filter_context_does_not_duplicate_filters(
|
||||
assert ExtraCache().filter_values("country") == ["USA"]
|
||||
|
||||
|
||||
def test_apply_dashboard_filter_context_applies_time_grain_to_extras() -> None:
|
||||
"""
|
||||
A dashboard time-grain filter must land in ``query["extras"]``, where
|
||||
get_time_grain() reads it for charts that have no adhoc x-axis column.
|
||||
"""
|
||||
query_context_json: dict[str, Any] = {
|
||||
"queries": [{"extras": {"time_grain_sqla": "P1D", "having": "", "where": ""}}],
|
||||
}
|
||||
|
||||
apply_dashboard_filter_context(query_context_json, {"time_grain_sqla": "P1M"})
|
||||
|
||||
assert query_context_json["queries"][0]["extras"]["time_grain_sqla"] == "P1M"
|
||||
|
||||
|
||||
def test_apply_dashboard_filter_context_overrides_x_axis_time_grain() -> None:
|
||||
"""
|
||||
For charts with an adhoc X-Axis, the dashboard grain must override the
|
||||
BASE_AXIS column's ``timeGrain`` (which get_time_grain() reads before
|
||||
falling back to extras), mirroring the frontend's normalizeTimeColumn.
|
||||
"""
|
||||
query_context_json: dict[str, Any] = {
|
||||
"queries": [
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"timeGrain": "P1D",
|
||||
"columnType": "BASE_AXIS",
|
||||
"sqlExpression": "order_date",
|
||||
}
|
||||
],
|
||||
"extras": {"time_grain_sqla": "P1D"},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
apply_dashboard_filter_context(query_context_json, {"time_grain_sqla": "P1Y"})
|
||||
|
||||
query = query_context_json["queries"][0]
|
||||
assert query["columns"][0]["timeGrain"] == "P1Y"
|
||||
assert query["extras"]["time_grain_sqla"] == "P1Y"
|
||||
|
||||
|
||||
def test_apply_dashboard_filter_context_grain_targets_first_adhoc_column() -> None:
|
||||
"""
|
||||
The grain override must land on ``columns[0]`` to match frontend logic.
|
||||
"""
|
||||
query_context_json: dict[str, Any] = {
|
||||
"queries": [
|
||||
{
|
||||
"columns": [
|
||||
{"timeGrain": "P1D", "sqlExpression": "order_date"},
|
||||
{"columnType": "BASE_AXIS", "sqlExpression": "other"},
|
||||
],
|
||||
"extras": {},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
apply_dashboard_filter_context(query_context_json, {"time_grain_sqla": "P1Y"})
|
||||
|
||||
columns = query_context_json["queries"][0]["columns"]
|
||||
assert columns[0]["timeGrain"] == "P1Y" # the column get_time_grain reads
|
||||
assert "timeGrain" not in columns[1] # the BASE_AXIS-tagged one is untouched
|
||||
|
||||
|
||||
def test_apply_dashboard_filter_context_keeps_grain_when_no_grain_filter() -> None:
|
||||
"""
|
||||
When the dashboard applies a non-grain filter (e.g. a value filter), the
|
||||
chart's own x-axis ``timeGrain`` must be preserved -- not wiped -- since no
|
||||
dashboard grain was provided.
|
||||
"""
|
||||
query_context_json: dict[str, Any] = {
|
||||
"queries": [
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"timeGrain": "P1M",
|
||||
"columnType": "BASE_AXIS",
|
||||
"sqlExpression": "order_date",
|
||||
}
|
||||
],
|
||||
"extras": {"time_grain_sqla": "P1M"},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
# extra_form_data carries a value filter but NO time_grain_sqla
|
||||
apply_dashboard_filter_context(
|
||||
query_context_json,
|
||||
{"filters": [{"col": "country", "op": "IN", "val": ["US"]}]},
|
||||
)
|
||||
|
||||
query = query_context_json["queries"][0]
|
||||
assert query["columns"][0]["timeGrain"] == "P1M"
|
||||
|
||||
|
||||
def _extract_filename(form_value: str) -> str | None:
|
||||
"""Run _extract_export_params_from_request with a form filename value."""
|
||||
from superset.charts.data.api import ChartDataRestApi
|
||||
|
||||
Reference in New Issue
Block a user