mirror of
https://github.com/apache/superset.git
synced 2026-07-18 20:55:47 +00:00
Compare commits
2 Commits
fix-report
...
feat/viz-b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10bcc8bab5 | ||
|
|
fb484cc932 |
@@ -168,6 +168,12 @@ NATIVE_FILTER_DEFAULT_ROW_LIMIT = 1000
|
|||||||
# max rows retrieved by filter select auto complete
|
# max rows retrieved by filter select auto complete
|
||||||
FILTER_SELECT_ROW_LIMIT = 10000
|
FILTER_SELECT_ROW_LIMIT = 10000
|
||||||
|
|
||||||
|
# Upper bound on the number of entries in user-supplied list fields that each
|
||||||
|
# fan out into one or more additional database queries within a single chart
|
||||||
|
# request, to limit query amplification. Set to 0 to disable a bound.
|
||||||
|
VIZ_TIME_COMPARE_MAX_LIST_SIZE = 10
|
||||||
|
VIZ_DECK_SLICES_MAX_LIST_SIZE = 10
|
||||||
|
|
||||||
# SupersetClient HTTP retry configuration
|
# SupersetClient HTTP retry configuration
|
||||||
# Controls retry behavior for all HTTP requests made through SupersetClient
|
# Controls retry behavior for all HTTP requests made through SupersetClient
|
||||||
# This helps handle transient server errors (like 502 Bad Gateway) automatically
|
# This helps handle transient server errors (like 502 Bad Gateway) automatically
|
||||||
|
|||||||
@@ -1082,6 +1082,15 @@ class NVD3TimeSeriesViz(NVD3Viz):
|
|||||||
if not isinstance(time_compare, list):
|
if not isinstance(time_compare, list):
|
||||||
time_compare = [time_compare]
|
time_compare = [time_compare]
|
||||||
|
|
||||||
|
max_time_compare = current_app.config["VIZ_TIME_COMPARE_MAX_LIST_SIZE"]
|
||||||
|
if max_time_compare and len(time_compare) > max_time_compare:
|
||||||
|
raise QueryObjectValidationError(
|
||||||
|
_(
|
||||||
|
"Too many time comparisons requested; the maximum is %(limit)s.",
|
||||||
|
limit=max_time_compare,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
for option in time_compare:
|
for option in time_compare:
|
||||||
query_object = self.query_obj()
|
query_object = self.query_obj()
|
||||||
try:
|
try:
|
||||||
@@ -1664,7 +1673,17 @@ class DeckGLMultiLayer(BaseViz):
|
|||||||
from superset import db
|
from superset import db
|
||||||
from superset.models.slice import Slice
|
from superset.models.slice import Slice
|
||||||
|
|
||||||
slice_ids = self.form_data.get("deck_slices")
|
slice_ids = self.form_data.get("deck_slices") or []
|
||||||
|
if not isinstance(slice_ids, list):
|
||||||
|
slice_ids = []
|
||||||
|
max_deck_slices = current_app.config["VIZ_DECK_SLICES_MAX_LIST_SIZE"]
|
||||||
|
if max_deck_slices and len(slice_ids) > max_deck_slices:
|
||||||
|
raise QueryObjectValidationError(
|
||||||
|
_(
|
||||||
|
"Too many layers requested; the maximum is %(limit)s.",
|
||||||
|
limit=max_deck_slices,
|
||||||
|
)
|
||||||
|
)
|
||||||
slices = db.session.query(Slice).filter(Slice.id.in_(slice_ids)).all()
|
slices = db.session.query(Slice).filter(Slice.id.in_(slice_ids)).all()
|
||||||
|
|
||||||
features: dict[str, list[Any]] = {}
|
features: dict[str, list[Any]] = {}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
# KIND, either express or implied. See the License for the
|
# KIND, either express or implied. See the License for the
|
||||||
# specific language governing permissions and limitations
|
# specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
@@ -91,6 +92,56 @@ def test_get_df_payload_captures_generic_exception_as_viz_get_df_error() -> None
|
|||||||
assert obj.errors[0]["message"] == "boom"
|
assert obj.errors[0]["message"] == "boom"
|
||||||
|
|
||||||
|
|
||||||
|
def _datasource() -> SqlaTable:
|
||||||
|
database = Database(database_name="d", sqlalchemy_uri="sqlite://")
|
||||||
|
return SqlaTable(
|
||||||
|
table_name="t",
|
||||||
|
columns=[],
|
||||||
|
metrics=[],
|
||||||
|
main_dttm_col=None,
|
||||||
|
database=database,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_extra_queries_rejects_too_many_time_compares() -> None:
|
||||||
|
obj = viz.NVD3TimeSeriesViz(
|
||||||
|
datasource=_datasource(),
|
||||||
|
form_data={
|
||||||
|
"viz_type": "line",
|
||||||
|
"time_compare": [f"{i} days ago" for i in range(11)],
|
||||||
|
},
|
||||||
|
force=True,
|
||||||
|
)
|
||||||
|
with pytest.raises(QueryObjectValidationError, match="Too many time comparisons"):
|
||||||
|
obj.run_extra_queries()
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_extra_queries_allows_time_compare_within_limit() -> None:
|
||||||
|
obj = viz.NVD3TimeSeriesViz(
|
||||||
|
datasource=_datasource(),
|
||||||
|
form_data={"viz_type": "line", "time_compare": ["1 day ago"]},
|
||||||
|
force=True,
|
||||||
|
)
|
||||||
|
query_obj = {"from_dttm": datetime(2021, 1, 2), "to_dttm": datetime(2021, 1, 3)}
|
||||||
|
with (
|
||||||
|
patch.object(viz.NVD3TimeSeriesViz, "query_obj", return_value=query_obj),
|
||||||
|
patch.object(
|
||||||
|
viz.NVD3TimeSeriesViz, "get_df_payload", return_value={"df": None}
|
||||||
|
),
|
||||||
|
):
|
||||||
|
obj.run_extra_queries() # must not raise the limit error
|
||||||
|
|
||||||
|
|
||||||
|
def test_deck_multi_rejects_too_many_slices() -> None:
|
||||||
|
obj = viz.DeckGLMultiLayer(
|
||||||
|
datasource=_datasource(),
|
||||||
|
form_data={"viz_type": "deck_multi", "deck_slices": list(range(11))},
|
||||||
|
force=True,
|
||||||
|
)
|
||||||
|
with pytest.raises(QueryObjectValidationError, match="Too many layers"):
|
||||||
|
obj.get_data(None)
|
||||||
|
|
||||||
|
|
||||||
def test_get_df_payload_captures_query_object_validation_error() -> None:
|
def test_get_df_payload_captures_query_object_validation_error() -> None:
|
||||||
"""
|
"""
|
||||||
``QueryObjectValidationError`` is reported as ``VIZ_GET_DF_ERROR``.
|
``QueryObjectValidationError`` is reported as ``VIZ_GET_DF_ERROR``.
|
||||||
|
|||||||
Reference in New Issue
Block a user