mirror of
https://github.com/apache/superset.git
synced 2026-09-02 13:31:33 +00:00
Metrics fields now render ad-hoc aggregate/SQL entries as editable rows via a new AdhocMetricEditor popover instead of falling back to raw JSON for the whole field whenever any entry isn't a saved-metric string. Dataset metadata fetch extended with column verbose names and the disallow_adhoc_metrics flag needed to support it. Also redesigns the Inspector's Placement section as a collapsed panel with slider controls, and adds consistent field-row padding across the panel. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
97 lines
3.6 KiB
Python
97 lines
3.6 KiB
Python
# 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.
|
|
|
|
"""Tests for ``WidgetControlsRestApi.validate`` — the REST boundary the
|
|
Dashboard V2 Inspector's form and JSON editor commit through (see
|
|
``controlValueValidation.ts``). It is a thin wrapper over
|
|
``Widget.validate_control_values``, the same call ``set_widget_control_values``
|
|
(the MCP write tool) commits through, so a frontend edit and an MCP edit are
|
|
validated by one implementation reached by two routes, not two rule sets that
|
|
can drift apart."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from superset.utils import json
|
|
|
|
|
|
def test_validate_accepts_valid_control_values(client, full_api_access) -> None:
|
|
resp = client.post(
|
|
"/api/v1/widgets/type/metric-tile/validate",
|
|
data=json.dumps(
|
|
{
|
|
"control_values": {
|
|
"dataBinding": {"datasetId": 1, "metrics": ["count"]},
|
|
}
|
|
}
|
|
),
|
|
content_type="application/json",
|
|
)
|
|
|
|
assert resp.status_code == 200
|
|
assert resp.get_json()["result"]["errors"] == []
|
|
|
|
|
|
def test_validate_rejects_invalid_control_values(client, full_api_access) -> None:
|
|
resp = client.post(
|
|
"/api/v1/widgets/type/metric-tile/validate",
|
|
data=json.dumps({"control_values": {"decimals": "not-an-int"}}),
|
|
content_type="application/json",
|
|
)
|
|
|
|
assert resp.status_code == 200
|
|
errors = resp.get_json()["result"]["errors"]
|
|
locs = [tuple(error["loc"]) for error in errors]
|
|
# Missing the required dataBinding, and decimals is the wrong type: both
|
|
# of the widget's own model rules, not something this route invents.
|
|
assert ("dataBinding",) in locs
|
|
assert ("decimals",) in locs
|
|
|
|
|
|
def test_validate_unknown_widget_type_is_404(client, full_api_access) -> None:
|
|
resp = client.post(
|
|
"/api/v1/widgets/type/not-a-real-widget-type/validate",
|
|
data=json.dumps({"control_values": {}}),
|
|
content_type="application/json",
|
|
)
|
|
|
|
assert resp.status_code == 404
|
|
|
|
|
|
def test_validate_is_a_thin_wrapper_over_the_widget_s_own_validator(
|
|
client, full_api_access
|
|
) -> None:
|
|
"""The route's result must equal calling the widget's
|
|
``validate_control_values`` directly with the same input — proving the
|
|
REST boundary and the MCP ``set_widget_control_values`` tool (which also
|
|
calls ``validate_control_values`` directly) run through the identical
|
|
implementation rather than parallel copies of it."""
|
|
from superset.widgets.registry import registry
|
|
|
|
control_values = {"decimals": "not-an-int"}
|
|
widget = registry.get("metric-tile")
|
|
assert widget is not None
|
|
direct_errors = widget.validate_control_values(control_values)
|
|
|
|
resp = client.post(
|
|
"/api/v1/widgets/type/metric-tile/validate",
|
|
data=json.dumps({"control_values": control_values}),
|
|
content_type="application/json",
|
|
)
|
|
|
|
assert resp.status_code == 200
|
|
assert resp.get_json()["result"]["errors"] == direct_errors
|