Compare commits

..
7 changed files with 37 additions and 123 deletions
+22 -19
View File
@@ -86,7 +86,7 @@
"classnames": "^2.2.5",
"content-disposition": "^2.0.1",
"d3-scale": "^4.0.2",
"dayjs": "^1.11.22",
"dayjs": "^1.11.21",
"dom-to-image-more": "^3.10.2",
"dom-to-pdf": "^0.3.2",
"echarts": "^6.1.0",
@@ -220,7 +220,7 @@
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
"baseline-browser-mapping": "^2.11.14",
"cheerio": "1.2.0",
"concurrently": "^10.0.5",
"concurrently": "^10.0.4",
"copy-webpack-plugin": "^14.0.0",
"cross-env": "^10.1.0",
"css-loader": "^7.1.4",
@@ -17190,9 +17190,9 @@
"license": "MIT"
},
"node_modules/concurrently": {
"version": "10.0.5",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.5.tgz",
"integrity": "sha512-JaP/CoftUrCcAFW/g//RbgEGwlelnEae6cfBLgH6ZdO6s8jPkn6p9SB9u6pdVxYXoiSnFqseOlHfrEfF82TVOg==",
"version": "10.0.4",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.4.tgz",
"integrity": "sha512-trZql+7l/0+WRAsAnEdctr4+iiOS6ZrViI6H8QWcCF9MFS/LT0dKpe8vluB1to6it+OxSI4VospFTIFMW8DJRw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -18541,9 +18541,9 @@
}
},
"node_modules/dayjs": {
"version": "1.11.22",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.22.tgz",
"integrity": "sha512-1YRnxzt/AabP3GHxnaB9/b+ZScCKu5TeF+co+BWG+lnWVIwEcTFc1FVE0WLNmNO3sA6GGXL40i5qkHfbLzpwrg==",
"version": "1.11.21",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz",
"integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==",
"license": "MIT"
},
"node_modules/debounce": {
@@ -34873,18 +34873,12 @@
}
},
"node_modules/react-error-boundary": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.1.3.tgz",
"integrity": "sha512-GnSKpCohFi2nQmJCWwP8O8wub7zexlePvpsejvQr35vS5RTouS1+utTNOmyc540yw5vyOXnSL1rBWsCQDmkyUA==",
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.1.2.tgz",
"integrity": "sha512-3DpCr5HVdZ0caUjYE/kIHBEJN0mNP3ZCgf16c48uJ5TbWjorKVp+YG8W3XqlJ7vJAVNw6wNIImyPXmFydwmyng==",
"license": "MIT",
"peerDependencies": {
"@types/react": "^18.0.0 || ^19.0.0",
"react": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-google-recaptcha": {
@@ -42968,7 +42962,7 @@
"d3-scale": "^4.0.2",
"d3-time": "^3.1.0",
"d3-time-format": "^4.1.0",
"dayjs": "^1.11.22",
"dayjs": "^1.11.21",
"dompurify": "^3.4.13",
"fetch-retry": "^6.0.0",
"handlebars": "^4.7.9",
@@ -42980,7 +42974,7 @@
"re-resizable": "^6.11.2",
"react-ace": "^14.0.1",
"react-draggable": "^4.7.1",
"react-error-boundary": "^6.1.3",
"react-error-boundary": "^6.1.2",
"react-js-cron": "^6.0.2",
"react-markdown": "^10.1.0",
"react-resize-detector": "^7.1.2",
@@ -43079,6 +43073,15 @@
"node": ">=12"
}
},
"packages/superset-ui-core/node_modules/dompurify": {
"version": "3.4.13",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.13.tgz",
"integrity": "sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
},
"packages/superset-ui-core/node_modules/react-ace": {
"version": "14.0.1",
"resolved": "https://registry.npmjs.org/react-ace/-/react-ace-14.0.1.tgz",
+2 -2
View File
@@ -163,7 +163,7 @@
"classnames": "^2.2.5",
"content-disposition": "^2.0.1",
"d3-scale": "^4.0.2",
"dayjs": "^1.11.22",
"dayjs": "^1.11.21",
"dom-to-image-more": "^3.10.2",
"dom-to-pdf": "^0.3.2",
"echarts": "^6.1.0",
@@ -297,7 +297,7 @@
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
"baseline-browser-mapping": "^2.11.14",
"cheerio": "1.2.0",
"concurrently": "^10.0.5",
"concurrently": "^10.0.4",
"copy-webpack-plugin": "^14.0.0",
"cross-env": "^10.1.0",
"css-loader": "^7.1.4",
@@ -67,7 +67,7 @@
"d3-scale": "^4.0.2",
"d3-time": "^3.1.0",
"d3-time-format": "^4.1.0",
"dayjs": "^1.11.22",
"dayjs": "^1.11.21",
"dompurify": "^3.4.13",
"fetch-retry": "^6.0.0",
"handlebars": "^4.7.9",
@@ -79,7 +79,7 @@
"re-resizable": "^6.11.2",
"react-ace": "^14.0.1",
"react-draggable": "^4.7.1",
"react-error-boundary": "^6.1.3",
"react-error-boundary": "^6.1.2",
"react-js-cron": "^6.0.2",
"react-markdown": "^10.1.0",
"react-resize-detector": "^7.1.2",
@@ -88,7 +88,12 @@ export const ResultsPaneOnDashboard = ({
return (
<Wrapper>
<Tabs activeKey={activeTabKey} onChange={setActiveTabKey} items={items} />
<Tabs
fullHeight
activeKey={activeTabKey}
onChange={setActiveTabKey}
items={items}
/>
</Wrapper>
);
};
-18
View File
@@ -106,10 +106,6 @@ def load_chart_data_into_cache(
) -> None:
# pylint: disable=import-outside-toplevel
from superset.commands.chart.data.get_data_command import ChartDataCommand
from superset.commands.chart.exceptions import (
ChartDataCacheLoadError,
ChartDataQueryFailedError,
)
with override_user(_load_user_from_job_metadata(job_metadata), force=False):
try:
@@ -127,20 +123,6 @@ def load_chart_data_into_cache(
except SoftTimeLimitExceeded as ex:
_handle_soft_time_limit(job_metadata, ex, "loading chart data")
raise
except (ChartDataCacheLoadError, ChartDataQueryFailedError) as ex:
# These map to 422/400 in the synchronous chart/data endpoint (see
# ChartDataRestApi._get_data_response) - expected, client-facing
# validation failures (e.g. a chart still referencing columns a
# customer has since dropped from the dataset), not application
# bugs. The failure is already delivered to the client via
# update_job below; re-raising would only surface it a second
# time as an unhandled Celery task exception.
logger.info("Chart data query failed while loading into cache: %s", ex)
async_query_manager.update_job(
job_metadata,
async_query_manager.STATUS_ERROR,
errors=sanitize_error_dicts([{"message": str(ex.message)}]),
)
except Exception as ex:
# Extract SIP-40 style errors when available
if isinstance(ex, SupersetErrorException):
@@ -110,11 +110,8 @@ class TestAsyncQueries(SupersetTestCase):
"status": "pending",
"errors": [],
}
# ChartDataQueryFailedError mirrors the synchronous chart/data endpoint's
# 400 (see ChartDataRestApi._get_data_response) - an expected validation
# failure, not a bug, so the task reports it via update_job and does not
# re-raise (see superset/tasks/async_queries.py).
load_chart_data_into_cache(job_metadata, query_context)
with pytest.raises(ChartDataQueryFailedError):
load_chart_data_into_cache(job_metadata, query_context)
mock_run_command.assert_called_once_with(cache=True)
errors = [{"message": "Error: foo"}]
+3 -76
View File
@@ -21,10 +21,7 @@ import pytest
from celery.exceptions import SoftTimeLimitExceeded
from flask_babel import lazy_gettext as _
from superset.commands.chart.exceptions import (
ChartDataCacheLoadError,
ChartDataQueryFailedError,
)
from superset.commands.chart.exceptions import ChartDataQueryFailedError
from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
from superset.exceptions import (
OAuth2RedirectError,
@@ -46,7 +43,7 @@ def test_load_chart_data_into_cache_with_error(
job_metadata = {"user_id": 1}
form_data = {}
err_message = "Something went wrong"
err = RuntimeError(err_message)
err = ChartDataQueryFailedError(_(err_message))
mock_user = mock.MagicMock()
mock_query_context_schema = mock.MagicMock()
@@ -57,7 +54,7 @@ def test_load_chart_data_into_cache_with_error(
mock_query_context_schema.load.side_effect = err
with pytest.raises(RuntimeError):
with pytest.raises(ChartDataQueryFailedError):
load_chart_data_into_cache(job_metadata, form_data)
expected_errors = [{"message": err_message}]
@@ -67,76 +64,6 @@ def test_load_chart_data_into_cache_with_error(
)
@mock.patch("superset.tasks.async_queries.security_manager")
@mock.patch("superset.tasks.async_queries.async_query_manager")
@mock.patch("superset.commands.chart.data.get_data_command.ChartDataCommand")
@mock.patch("superset.tasks.async_queries.ChartDataQueryContextSchema")
def test_load_chart_data_into_cache_with_query_failed_error_does_not_reraise(
mock_query_context_schema_cls: mock.MagicMock,
mock_command_cls: mock.MagicMock,
mock_async_query_manager: mock.MagicMock,
mock_security_manager: mock.MagicMock,
) -> None:
"""
ChartDataQueryFailedError maps to a 400 in the synchronous chart/data
endpoint (see ChartDataRestApi._get_data_response) - an expected,
client-facing validation failure (e.g. a chart still referencing columns
a customer has since dropped from the dataset), not an application bug.
The task must still report it to the client via update_job, but must not
re-raise it - that would surface it a second time as an unhandled Celery
task exception.
"""
from superset.tasks.async_queries import load_chart_data_into_cache
job_metadata = {"user_id": 1}
form_data: dict[str, Any] = {}
err_message = "Columns missing in dataset: ['foo']"
mock_security_manager.get_user_by_id.return_value = mock.MagicMock()
mock_async_query_manager.STATUS_ERROR = "error"
mock_query_context_schema_cls.return_value.load.return_value = mock.MagicMock()
mock_command_cls.return_value.run.side_effect = ChartDataQueryFailedError(
_(err_message)
)
# Should not raise.
load_chart_data_into_cache(job_metadata, form_data)
mock_async_query_manager.update_job.assert_called_once_with(
job_metadata, "error", errors=[{"message": err_message}]
)
@mock.patch("superset.tasks.async_queries.security_manager")
@mock.patch("superset.tasks.async_queries.async_query_manager")
@mock.patch("superset.commands.chart.data.get_data_command.ChartDataCommand")
@mock.patch("superset.tasks.async_queries.ChartDataQueryContextSchema")
def test_load_chart_data_into_cache_with_cache_load_error_does_not_reraise(
mock_query_context_schema_cls: mock.MagicMock,
mock_command_cls: mock.MagicMock,
mock_async_query_manager: mock.MagicMock,
mock_security_manager: mock.MagicMock,
) -> None:
"""Same as above, for the sibling 422-mapped ChartDataCacheLoadError."""
from superset.tasks.async_queries import load_chart_data_into_cache
job_metadata = {"user_id": 1}
form_data: dict[str, Any] = {}
err_message = "Cache load failed"
mock_security_manager.get_user_by_id.return_value = mock.MagicMock()
mock_async_query_manager.STATUS_ERROR = "error"
mock_query_context_schema_cls.return_value.load.return_value = mock.MagicMock()
mock_command_cls.return_value.run.side_effect = ChartDataCacheLoadError(err_message)
# Should not raise.
load_chart_data_into_cache(job_metadata, form_data)
mock_async_query_manager.update_job.assert_called_once_with(
job_metadata, "error", errors=[{"message": err_message}]
)
@mock.patch("superset.tasks.async_queries.security_manager")
@mock.patch("superset.tasks.async_queries.async_query_manager")
@mock.patch("superset.tasks.async_queries.ChartDataQueryContextSchema")