From b075cc96fa9ce8bccbc74e67207d08515bc4e73c Mon Sep 17 00:00:00 2001 From: Eric Radman Date: Wed, 9 Oct 2024 12:04:19 -0400 Subject: [PATCH] Allow CSV/TSV/Excel download from shared dashboards - Always show dropdown menu - Append ?api_key for dashboards using the path /public/ - Compare api_key with dashboard API keys associated with the requested query --- .../dashboard-widget/VisualizationWidget.jsx | 30 ++++++++++++------- .../dashboards/dashboard-widget/Widget.jsx | 9 +++--- redash/handlers/query_results.py | 6 +++- redash/serializers/query_result.py | 2 +- tests/handlers/test_query_results.py | 30 +++++++++++++++++++ tests/serializers/test_query_results.py | 2 +- 6 files changed, 60 insertions(+), 19 deletions(-) diff --git a/client/app/components/dashboards/dashboard-widget/VisualizationWidget.jsx b/client/app/components/dashboards/dashboard-widget/VisualizationWidget.jsx index 9a021cc8bd..0f1dcca88b 100644 --- a/client/app/components/dashboards/dashboard-widget/VisualizationWidget.jsx +++ b/client/app/components/dashboards/dashboard-widget/VisualizationWidget.jsx @@ -27,9 +27,13 @@ function visualizationWidgetMenuOptions({ widget, canEditDashboard, onParameters const canEditParameters = canEditDashboard && !isEmpty(invoke(widget, "query.getParametersDefs")); const widgetQueryResult = widget.getQueryResult(); const isQueryResultEmpty = !widgetQueryResult || !widgetQueryResult.isEmpty || widgetQueryResult.isEmpty(); - - const downloadLink = fileType => widgetQueryResult.getLink(widget.getQuery().id, fileType); - const downloadName = fileType => widgetQueryResult.getName(widget.getQuery().name, fileType); + const parts = window.location.pathname.split("/").reverse(); + var apiKey = null; + if (parts.length > 3 && parts[2] === "public" && parts[0].length === 40) { + apiKey = parts[0]; + } + const downloadLink = (fileType) => widgetQueryResult.getLink(widget.getQuery().id, fileType, apiKey); + const downloadName = (fileType) => widgetQueryResult.getName(widget.getQuery().name, fileType); return compact([ {!isQueryResultEmpty ? ( @@ -149,7 +153,7 @@ function VisualizationWidgetFooter({ widget, isPublic, onRefresh, onExpand }) { const updatedAt = invoke(widgetQueryResult, "getUpdatedAt"); const [refreshClickButtonId, setRefreshClickButtonId] = useState(); - const refreshWidget = buttonId => { + const refreshWidget = (buttonId) => { if (!refreshClickButtonId) { setRefreshClickButtonId(buttonId); onRefresh().finally(() => setRefreshClickButtonId(null)); @@ -163,7 +167,8 @@ function VisualizationWidgetFooter({ widget, isPublic, onRefresh, onExpand }) { refreshWidget(1)} - data-test="RefreshButton"> + data-test="RefreshButton" + >