This is an automated email from the ASF dual-hosted git repository.
rusackas 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 c503f92642a fix(charts): restrict Prophet time grain schema validation
to supported Prophet grains (#43585)
c503f92642a is described below
commit c503f92642ac5566f4dc0e6a91079477abdd01f5
Author: Francesco.Castaldi <[email protected]>
AuthorDate: Wed Sep 9 06:43:33 2026 +0200
fix(charts): restrict Prophet time grain schema validation to supported
Prophet grains (#43585)
Co-authored-by: FrancescoCastaldi <[email protected]>
---
superset/charts/schemas.py | 14 ++++++++++++--
tests/unit_tests/charts/test_schemas.py | 24 +++++++++++++++++-------
2 files changed, 29 insertions(+), 9 deletions(-)
diff --git a/superset/charts/schemas.py b/superset/charts/schemas.py
index 75c3320b705..cd43524f20c 100644
--- a/superset/charts/schemas.py
+++ b/superset/charts/schemas.py
@@ -48,6 +48,7 @@ from superset.utils.core import (
PostProcessingBoxplotWhiskerType,
PostProcessingContributionOrientation,
)
+from superset.utils.pandas_postprocessing.utils import PROPHET_TIME_GRAIN_MAP
if TYPE_CHECKING:
from superset.common.query_context import QueryContext
@@ -73,6 +74,15 @@ def get_time_grain_choices() -> Any:
]
+def validate_time_grain_sqla(value: Any) -> None:
+ """Ensure the time grain is supported by the configured engine specs."""
+ choices = get_time_grain_choices()
+ validate.OneOf(
+ choices=choices,
+ error=_("Must be one of: {choices}."),
+ )(value)
+
+
# Fallback upper bound for the number of Prophet forecast periods when the
# application config cannot be read (for example, outside of an app context).
DEFAULT_MAX_PROPHET_PERIODS = 10000
@@ -778,7 +788,7 @@ class
ChartDataProphetOptionsSchema(ChartDataPostProcessingOperationOptionsSchem
"[ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations)
durations.",
"example": "P1D",
},
- validate=validate.OneOf(choices=get_time_grain_choices()),
+ validate=validate.OneOf(choices=tuple(PROPHET_TIME_GRAIN_MAP.keys())),
required=True,
)
periods = fields.Integer(
@@ -1167,7 +1177,7 @@ class ChartDataExtrasSchema(Schema):
"[ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Durations)
durations.",
"example": "P1D",
},
- validate=validate.OneOf(choices=get_time_grain_choices()),
+ validate=validate_time_grain_sqla,
allow_none=True,
)
instant_time_comparison_range = fields.String(
diff --git a/tests/unit_tests/charts/test_schemas.py
b/tests/unit_tests/charts/test_schemas.py
index 6610ab18147..9960b0cc2e2 100644
--- a/tests/unit_tests/charts/test_schemas.py
+++ b/tests/unit_tests/charts/test_schemas.py
@@ -352,21 +352,31 @@ def
test_chart_data_query_object_schema_deprecated_fields_renamed(
@pytest.mark.parametrize(
"app",
- [{"TIME_GRAIN_ADDONS": {"PT10M": "10 minutes"}}],
+ [{"TIME_GRAIN_ADDONS": {"PT7M": "7 minutes"}}],
indirect=True,
)
def test_time_grain_validation_with_config_addons(app_context: None) -> None:
- """Test that validation includes TIME_GRAIN_ADDONS from config"""
- schema = ChartDataProphetOptionsSchema()
+ """
+ Test that custom TIME_GRAIN_ADDONS are accepted by ChartDataExtrasSchema
+ (SQLA) but rejected by ChartDataProphetOptionsSchema (which only supports
+ mapped Prophet grains).
+ """
+ # Custom addon grain is valid for SQLA time grain
+ extras_schema = ChartDataExtrasSchema()
+ extras_result = extras_schema.load({"time_grain_sqla": "PT7M"})
+ assert extras_result["time_grain_sqla"] == "PT7M"
- # Custom time grain should now be valid
+ # Custom addon grain is not supported by Prophet and should be rejected
+ prophet_schema = ChartDataProphetOptionsSchema()
custom_data = {
- "time_grain": "PT10M",
+ "time_grain": "PT7M",
"periods": 5,
"confidence_interval": 0.9,
}
- result = schema.load(custom_data)
- assert result["time_grain"] == "PT10M"
+ with pytest.raises(ValidationError) as exc_info:
+ prophet_schema.load(custom_data)
+ assert "time_grain" in exc_info.value.messages
+ assert "Must be one of" in str(exc_info.value.messages["time_grain"])
def test_prophet_periods_within_bound(app_context: None) -> None: