diff --git a/superset/charts/api.py b/superset/charts/api.py index 94b422438fe13..036cfda8195eb 100644 --- a/superset/charts/api.py +++ b/superset/charts/api.py @@ -660,7 +660,7 @@ def screenshot(self, pk: int, digest: str) -> WerkzeugResponse: name: digest responses: 200: - description: Chart thumbnail image + description: Chart screenshot image content: image/*: schema: @@ -677,11 +677,9 @@ def screenshot(self, pk: int, digest: str) -> WerkzeugResponse: """ chart = self.datamodel.get(pk, self._base_filters) - # Making sure the chart still exists if not chart: return self.response_404() - # fetch the chart screenshot using the current user and cache if set if cache_payload := ChartScreenshot.get_from_cache_key(digest): if cache_payload.status == StatusValues.UPDATED: return Response( @@ -689,7 +687,6 @@ def screenshot(self, pk: int, digest: str) -> WerkzeugResponse: mimetype="image/png", direct_passthrough=True, ) - # TODO: return an empty image return self.response_404() @expose("//thumbnail//", methods=("GET",)) @@ -766,6 +763,7 @@ def thumbnail(self, pk: int, digest: str, **kwargs: Any) -> WerkzeugResponse: updated_at=cache_payload.get_timestamp(), update_status=cache_payload.get_status(), ) + # TODO remove digest from params... currently does nothing if chart.digest != digest: self.incr_stats("redirect", self.thumbnail.__name__) return redirect( diff --git a/superset/charts/schemas.py b/superset/charts/schemas.py index 89e47a9dcb881..fa14d6fcd38d0 100644 --- a/superset/charts/schemas.py +++ b/superset/charts/schemas.py @@ -304,6 +304,21 @@ class ChartCacheScreenshotResponseSchema(Schema): image_url = fields.String( metadata={"description": "The url to fetch the screenshot"} ) + update_status = fields.String( + metadata={"description": "The status of the async screenshot"} + ) + updated_at = fields.String( + metadata={"description": "The timestamp of the last change in status"} + ) + + +class ChartGetCachedScreenshotResponseSchema(Schema): + update_status = fields.String( + metadata={"description": "The status of the async screenshot"} + ) + updated_at = fields.String( + metadata={"description": "The timestamp of the last change in status"} + ) class ChartDataColumnSchema(Schema): diff --git a/superset/dashboards/api.py b/superset/dashboards/api.py index 2a303bdb515e3..f7b6edc210cea 100644 --- a/superset/dashboards/api.py +++ b/superset/dashboards/api.py @@ -942,14 +942,11 @@ def thumbnail(self, pk: int, digest: str, **kwargs: Any) -> WerkzeugResponse: screenshot_obj = DashboardScreenshot(dashboard_url, digest) cache_key = screenshot_obj.get_cache_key() cache_payload = screenshot_obj.get_from_cache_key(cache_key) - if not cache_payload: + if not cache_payload or kwargs["rison"].get("force", False): cache_payload = ScreenshotCachePayload() screenshot_obj.cache.set(cache_key, cache_payload) - if ( - kwargs["rison"].get("force", False) - or cache_payload.status != StatusValues.UPDATED - ): + if cache_payload.status != StatusValues.UPDATED: cache_dashboard_thumbnail.delay( current_user=current_user, dashboard_id=dashboard.id, @@ -986,7 +983,9 @@ def thumbnail(self, pk: int, digest: str, **kwargs: Any) -> WerkzeugResponse: f".cache_dashboard_screenshot", log_to_statsd=False, ) - def cache_dashboard_screenshot(self, pk: int, **kwargs: Any) -> WerkzeugResponse: + def cache_dashboard_screenshot( + self, pk: int, force: bool, **kwargs: Any + ) -> WerkzeugResponse: """Compute and cache a screenshot. --- post: @@ -1021,7 +1020,6 @@ def cache_dashboard_screenshot(self, pk: int, **kwargs: Any) -> WerkzeugResponse payload = CacheScreenshotSchema().load(request.json) except ValidationError as error: return self.response_400(message=error.messages) - dashboard = cast(Dashboard, self.datamodel.get(pk, self._base_filters)) if not dashboard: return self.response_404() @@ -1052,6 +1050,11 @@ def cache_dashboard_screenshot(self, pk: int, **kwargs: Any) -> WerkzeugResponse image_url = get_url_path( "DashboardRestApi.screenshot", pk=dashboard.id, digest=cache_key ) + force = kwargs["rison"].get("force", False) + cache_payload = screenshot_obj.get_from_cache_key(cache_key) + if force or cache_payload is None: + cache_payload = ScreenshotCachePayload() + screenshot_obj.cache.set(cache_key, cache_payload) def trigger_celery() -> WerkzeugResponse: logger.info("Triggering screenshot ASYNC") @@ -1064,7 +1067,6 @@ def trigger_celery() -> WerkzeugResponse: ), dashboard_id=dashboard.id, dashboard_url=dashboard_url, - force=False, thumb_size=thumb_size, window_size=window_size, cache_key=cache_key, @@ -1074,6 +1076,8 @@ def trigger_celery() -> WerkzeugResponse: cache_key=cache_key, dashboard_url=dashboard_url, image_url=image_url, + updated_at=cache_payload.get_timestamp(), + update_status=cache_payload.get_status(), ) return trigger_celery() diff --git a/superset/dashboards/schemas.py b/superset/dashboards/schemas.py index 837730c459a4d..3bca54f165886 100644 --- a/superset/dashboards/schemas.py +++ b/superset/dashboards/schemas.py @@ -468,6 +468,12 @@ class DashboardCacheScreenshotResponseSchema(Schema): image_url = fields.String( metadata={"description": "The url to fetch the screenshot"} ) + update_status = fields.String( + metadata={"description": "The status of the async screenshot"} + ) + updated_at = fields.String( + metadata={"description": "The timestamp of the last change in status"} + ) class CacheScreenshotSchema(Schema): diff --git a/superset/models/dashboard.py b/superset/models/dashboard.py index 28d8aacc7bed9..6140ac5cc3a63 100644 --- a/superset/models/dashboard.py +++ b/superset/models/dashboard.py @@ -350,7 +350,6 @@ def update_thumbnail(self) -> None: cache_dashboard_thumbnail.delay( current_user=get_current_user(), dashboard_id=self.id, - force=True, ) @classmethod diff --git a/tests/integration_tests/cli_tests.py b/tests/integration_tests/cli_tests.py index 048612a08a7bb..45f90002ff273 100644 --- a/tests/integration_tests/cli_tests.py +++ b/tests/integration_tests/cli_tests.py @@ -322,6 +322,5 @@ def test_compute_thumbnails(thumbnail_mock, app_context, fs): thumbnail_mock.assert_called_with( None, dashboard.id, - force=False, ) assert response.exit_code == 0