fix(mcp): fix pre_validate aliases and sql_expression normalization

Three pre_validate methods were checking only canonical field names, but
the Pydantic schemas accept validation aliases. For example
PieChartConfig.dimension accepts "groupby" as an alias, so sending
{"groupby": ...} would pass the schema but be incorrectly rejected by
pre_validate.

Four normalize_column_refs implementations did not guard against
sql_expression metrics (name=None), causing AttributeError when
_get_canonical_column_name(None, ...) was called. XYChartPlugin already
handled this correctly; the fix brings the other plugins in line.

Pre-validate alias fixes:
- TableChartPlugin: accept columns/all_columns/groupby (AliasChoices)
- PieChartPlugin: accept dimension/groupby (AliasChoices)
- PivotTableChartPlugin: accept rows/groupby/dimension (AliasChoices)

sql_expression normalization guards:
- BigNumberChartPlugin: skip metric normalization when sql_expression set
- PieChartPlugin: same for metric field
- MixedTimeseriesChartPlugin: skip in _norm_list helper
- PivotTableChartPlugin: skip in _norm_col_list helper
This commit is contained in:
Amin Ghadersohi
2026-06-26 15:00:57 +00:00
parent 4e28b22a10
commit 3b7834510d
5 changed files with 21 additions and 7 deletions

View File

@@ -208,7 +208,9 @@ class BigNumberChartPlugin(BaseChartPlugin):
config_dict = config.model_dump()
if config_dict.get("metric"):
if config_dict["metric"].get("saved_metric"):
if config_dict["metric"].get("sql_expression"):
pass
elif config_dict["metric"].get("saved_metric"):
config_dict["metric"]["name"] = (
DatasetValidator._get_canonical_metric_name(
config_dict["metric"]["name"], dataset_context

View File

@@ -132,6 +132,8 @@ class MixedTimeseriesChartPlugin(BaseChartPlugin):
def _norm_list(key: str) -> None:
if config_dict.get(key):
for col in config_dict[key]:
if col.get("sql_expression"):
continue
if col.get("saved_metric"):
col["name"] = DatasetValidator._get_canonical_metric_name(
col["name"], dataset_context

View File

@@ -47,7 +47,7 @@ class PieChartPlugin(BaseChartPlugin):
) -> ChartGenerationError | None:
missing_fields = []
if "dimension" not in config:
if "dimension" not in config and "groupby" not in config:
missing_fields.append("'dimension' (category column for slices)")
if "metric" not in config:
missing_fields.append("'metric' (value metric for slice sizes)")
@@ -104,7 +104,9 @@ class PieChartPlugin(BaseChartPlugin):
)
)
if config_dict.get("metric"):
if config_dict["metric"].get("saved_metric"):
if config_dict["metric"].get("sql_expression"):
pass
elif config_dict["metric"].get("saved_metric"):
config_dict["metric"]["name"] = (
DatasetValidator._get_canonical_metric_name(
config_dict["metric"]["name"], dataset_context

View File

@@ -47,7 +47,7 @@ class PivotTableChartPlugin(BaseChartPlugin):
) -> ChartGenerationError | None:
missing_fields = []
if not config.get("rows"):
if not (config.get("rows") or config.get("groupby") or config.get("dimension")):
missing_fields.append("'rows' (row grouping columns)")
if not config.get("metrics"):
missing_fields.append("'metrics' (aggregation metrics)")
@@ -67,7 +67,10 @@ class PivotTableChartPlugin(BaseChartPlugin):
error_code="MISSING_PIVOT_FIELDS",
)
if not isinstance(config.get("rows", []), list):
rows_val = (
config.get("rows") or config.get("groupby") or config.get("dimension") or []
)
if not isinstance(rows_val, list):
return ChartGenerationError(
error_type="invalid_rows_format",
message="Rows must be a list of columns",
@@ -123,6 +126,8 @@ class PivotTableChartPlugin(BaseChartPlugin):
def _norm_col_list(key: str) -> None:
if config_dict.get(key):
for col in config_dict[key]:
if col.get("sql_expression"):
continue
if col.get("saved_metric"):
col["name"] = DatasetValidator._get_canonical_metric_name(
col["name"], dataset_context

View File

@@ -46,7 +46,10 @@ class TableChartPlugin(BaseChartPlugin):
self,
config: dict[str, Any],
) -> ChartGenerationError | None:
if not config.get("columns"):
columns = (
config.get("columns") or config.get("all_columns") or config.get("groupby")
)
if not columns:
return ChartGenerationError(
error_type="missing_columns",
message="Table chart missing required field: columns",
@@ -63,7 +66,7 @@ class TableChartPlugin(BaseChartPlugin):
error_code="MISSING_COLUMNS",
)
if not isinstance(config.get("columns", []), list):
if not isinstance(columns, list):
return ChartGenerationError(
error_type="invalid_columns_format",
message="Columns must be a list",