# 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. """Table chart type plugin.""" from __future__ import annotations from typing import Any from superset.mcp_service.chart.chart_utils import ( _summarize_filters, _table_chart_what, map_table_config, ) from superset.mcp_service.chart.plugin import BaseChartPlugin from superset.mcp_service.chart.schemas import ColumnRef, TableChartConfig from superset.mcp_service.chart.validation.dataset_validator import DatasetValidator from superset.mcp_service.common.error_schemas import ChartGenerationError class TableChartPlugin(BaseChartPlugin): """Plugin for table chart type.""" chart_type = "table" display_name = "Table" native_viz_types = { "table": "Table", "ag-grid-table": "Interactive Table", } def pre_validate( self, config: dict[str, Any], ) -> ChartGenerationError | None: if "columns" not in config: return ChartGenerationError( error_type="missing_columns", message="Table chart missing required field: columns", details=( "Table charts require a 'columns' array to specify which " "columns to display" ), suggestions=[ "Add 'columns' field with array of column specifications", "Example: 'columns': [{'name': 'product'}, {'name': 'sales', " "'aggregate': 'SUM'}]", "Each column can have optional 'aggregate' for metrics", ], error_code="MISSING_COLUMNS", ) if not isinstance(config.get("columns", []), list): return ChartGenerationError( error_type="invalid_columns_format", message="Columns must be a list", details="The 'columns' field must be an array of column specifications", suggestions=[ "Ensure columns is an array: 'columns': [...]", "Each column should be an object with 'name' field", ], error_code="INVALID_COLUMNS_FORMAT", ) return None def extract_column_refs(self, config: Any) -> list[ColumnRef]: if not isinstance(config, TableChartConfig): return [] refs: list[ColumnRef] = list(config.columns) if config.filters: for f in config.filters: refs.append(ColumnRef(name=f.column)) return refs def to_form_data( self, config: Any, dataset_id: int | str | None = None ) -> dict[str, Any]: return map_table_config(config) def generate_name(self, config: Any, dataset_name: str | None = None) -> str: what = _table_chart_what(config, dataset_name) context = _summarize_filters(config.filters) return self._with_context(what, context) def resolve_viz_type(self, config: Any) -> str: return getattr(config, "viz_type", "table") def normalize_column_refs(self, config: Any, dataset_context: Any) -> Any: config_dict = config.model_dump() get_canonical = DatasetValidator._get_canonical_column_name get_canonical_metric = DatasetValidator._get_canonical_metric_name for col in config_dict.get("columns") or []: if col.get("saved_metric"): col["name"] = get_canonical_metric(col["name"], dataset_context) else: col["name"] = get_canonical(col["name"], dataset_context) DatasetValidator._normalize_filters(config_dict, dataset_context) return TableChartConfig.model_validate(config_dict) def schema_error_hint(self) -> ChartGenerationError | None: return ChartGenerationError( error_type="table_validation_error", message="Table chart configuration validation failed", details=( "The table chart configuration is missing required " "fields or has invalid structure" ), suggestions=[ "Ensure 'columns' field is an array of column specifications", "Each column needs {'name': 'column_name'}", "Optional: add 'aggregate' for metrics", "Example: 'columns': [{'name': 'product'}, " "{'name': 'sales', 'aggregate': 'SUM'}]", ], error_code="TABLE_VALIDATION_ERROR", )