This is an automated email from the ASF dual-hosted git repository.
beto pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 1064ad5d58 fix: enforce `ALERT_REPORTS_MAX_CUSTOM_SCREENSHOT_WIDTH`
(#32053)
1064ad5d58 is described below
commit 1064ad5d58ec4c21d6873f1be8c3c686dc51cfd3
Author: Beto Dealmeida <[email protected]>
AuthorDate: Fri Jan 31 14:56:56 2025 -0500
fix: enforce `ALERT_REPORTS_MAX_CUSTOM_SCREENSHOT_WIDTH` (#32053)
---
superset/commands/report/execute.py | 26 ++---
tests/unit_tests/commands/report/execute_test.py | 115 +++++++++++++++++++++++
2 files changed, 130 insertions(+), 11 deletions(-)
diff --git a/superset/commands/report/execute.py
b/superset/commands/report/execute.py
index 9e69258650..0c57d55bb5 100644
--- a/superset/commands/report/execute.py
+++ b/superset/commands/report/execute.py
@@ -300,13 +300,16 @@ class BaseReportState:
)
user = security_manager.find_user(username)
+ max_width = app.config["ALERT_REPORTS_MAX_CUSTOM_SCREENSHOT_WIDTH"]
+
if self._report_schedule.chart:
url = self._get_url()
+
window_width, window_height =
app.config["WEBDRIVER_WINDOW"]["slice"]
- window_size = (
- self._report_schedule.custom_width or window_width,
- self._report_schedule.custom_height or window_height,
- )
+ width = min(max_width, self._report_schedule.custom_width or
window_width)
+ height = self._report_schedule.custom_height or window_height
+ window_size = (width, height)
+
screenshots: list[Union[ChartScreenshot, DashboardScreenshot]] = [
ChartScreenshot(
url,
@@ -317,11 +320,12 @@ class BaseReportState:
]
else:
urls = self.get_dashboard_urls()
+
window_width, window_height =
app.config["WEBDRIVER_WINDOW"]["dashboard"]
- window_size = (
- self._report_schedule.custom_width or window_width,
- self._report_schedule.custom_height or window_height,
- )
+ width = min(max_width, self._report_schedule.custom_width or
window_width)
+ height = self._report_schedule.custom_height or window_height
+ window_size = (width, height)
+
screenshots = [
DashboardScreenshot(
url,
@@ -578,9 +582,9 @@ class BaseReportState:
SupersetError(
message=ex.message,
error_type=SupersetErrorType.REPORT_NOTIFICATION_ERROR,
- level=ErrorLevel.ERROR
- if ex.status >= 500
- else ErrorLevel.WARNING,
+ level=(
+ ErrorLevel.ERROR if ex.status >= 500 else
ErrorLevel.WARNING
+ ),
)
)
if notification_errors:
diff --git a/tests/unit_tests/commands/report/execute_test.py
b/tests/unit_tests/commands/report/execute_test.py
index 3d49bb0457..7c04f2f8ab 100644
--- a/tests/unit_tests/commands/report/execute_test.py
+++ b/tests/unit_tests/commands/report/execute_test.py
@@ -16,19 +16,24 @@
# under the License.
import json
+from datetime import datetime
from unittest.mock import patch
+from uuid import UUID
import pytest
from pytest_mock import MockerFixture
+from superset.app import SupersetApp
from superset.commands.report.execute import BaseReportState
from superset.dashboards.permalink.types import DashboardPermalinkState
from superset.reports.models import (
ReportRecipientType,
ReportSchedule,
+ ReportScheduleType,
ReportSourceFormat,
)
from superset.utils.core import HeaderDataType
+from superset.utils.screenshots import ChartScreenshot
from tests.integration_tests.conftest import with_feature_flags
@@ -365,3 +370,113 @@ def test_get_tab_url(
)
result: str = class_instance._get_tab_url(dashboard_state)
assert result == "http://0.0.0.0:8080/superset/dashboard/p/uri/"
+
+
+def create_report_schedule(
+ mocker: MockerFixture,
+ custom_width: int | None = None,
+ custom_height: int | None = None,
+) -> ReportSchedule:
+ """Helper function to create a ReportSchedule instance with specified
dimensions."""
+ schedule = ReportSchedule()
+ schedule.type = ReportScheduleType.REPORT
+ schedule.name = "Test Report"
+ schedule.description = "Test Description"
+ schedule.chart = mocker.MagicMock()
+ schedule.chart.id = 1
+ schedule.dashboard = None
+ schedule.database = None
+ schedule.custom_width = custom_width
+ schedule.custom_height = custom_height
+ return schedule
+
+
[email protected](
+ "test_id,custom_width,max_width,window_width,expected_width",
+ [
+ # Test when custom width exceeds max width
+ ("exceeds_max", 2000, 1600, 800, 1600),
+ # Test when custom width is less than max width
+ ("under_max", 1200, 1600, 800, 1200),
+ # Test when custom width is None (should use window width)
+ ("no_custom", None, 1600, 800, 800),
+ # Test when custom width equals max width
+ ("equals_max", 1600, 1600, 800, 1600),
+ ],
+)
+def test_screenshot_width_calculation(
+ app: SupersetApp,
+ mocker: MockerFixture,
+ test_id: str,
+ custom_width: int | None,
+ max_width: int,
+ window_width: int,
+ expected_width: int,
+) -> None:
+ """
+ Test that screenshot width is correctly calculated.
+
+ The width should be:
+ - Limited by max_width when custom_width exceeds it
+ - Equal to custom_width when it's less than max_width
+ - Equal to window_width when custom_width is None
+ """
+ from superset.commands.report.execute import BaseReportState
+
+ # Mock configuration
+ app.config.update(
+ {
+ "ALERT_REPORTS_MAX_CUSTOM_SCREENSHOT_WIDTH": max_width,
+ "WEBDRIVER_WINDOW": {
+ "slice": (window_width, 600),
+ "dashboard": (window_width, 600),
+ },
+ "ALERT_REPORTS_EXECUTORS": {},
+ }
+ )
+
+ # Create report schedule with specified custom width
+ report_schedule = create_report_schedule(mocker, custom_width=custom_width)
+
+ # Initialize BaseReportState
+ report_state = BaseReportState(
+ report_schedule=report_schedule,
+ scheduled_dttm=datetime.now(),
+ execution_id=UUID("084e7ee6-5557-4ecd-9632-b7f39c9ec524"),
+ )
+
+ # Mock security manager and screenshot
+ with (
+ patch(
+ "superset.commands.report.execute.security_manager"
+ ) as mock_security_manager,
+ patch(
+ "superset.utils.screenshots.ChartScreenshot.get_screenshot"
+ ) as mock_get_screenshot,
+ ):
+ # Mock user
+ mock_user = mocker.MagicMock()
+ mock_security_manager.find_user.return_value = mock_user
+ mock_get_screenshot.return_value = b"screenshot bytes"
+
+ # Mock get_executor to avoid database lookups
+ with patch(
+ "superset.commands.report.execute.get_executor"
+ ) as mock_get_executor:
+ mock_get_executor.return_value = ("executor", "username")
+
+ # Capture the ChartScreenshot instantiation
+ with patch(
+ "superset.commands.report.execute.ChartScreenshot",
+ wraps=ChartScreenshot,
+ ) as mock_chart_screenshot:
+ # Call the method that triggers screenshot creation
+ report_state._get_screenshots()
+
+ # Verify ChartScreenshot was created with correct window_size
+ mock_chart_screenshot.assert_called_once()
+ _, kwargs = mock_chart_screenshot.call_args
+ assert kwargs["window_size"][0] == expected_width, (
+ f"Test {test_id}: Expected width {expected_width}, "
+ f"but got {kwargs['window_size'][0]}"
+ )