EnxDev opened a new pull request, #44082: URL: https://github.com/apache/superset/pull/44082
### SUMMARY “Export Data to Excel” previously worked only when `EXCEL_EXPORT_S3_BUCKET` was configured. On deployments without a bucket, the menu item was still visible because it is controlled by the `can_export` permission, but the endpoint returned `501` and the user received an error toast. The endpoint now selects the execution path based on the existing storage configuration: * **Bucket configured:** the existing asynchronous flow is unchanged. The export is queued through Celery, uploaded to storage, and emailed as a download link. The endpoint returns `202` with a job ID. * **No bucket configured:** the workbook is generated during the request and returned directly as a file download. This path does not require a Celery worker, export storage, or email delivery. #### Execution-path selection Availability is derived from the storage configuration rather than a separate feature flag. Adding a flag would create another setting that could disagree with the actual storage configuration. The frontend does not choose the execution path. It sends the same request and handles either a synchronous file response or an asynchronous job response. This means changing the server configuration cannot leave the frontend requesting an unsupported path. #### Synchronous export limit The synchronous path checks the export size before running any query. A new `EXCEL_EXPORT_SYNC_MAX_ROWS` configuration value, with a default of `100_000`, limits the combined `row_limit` of every query the export would execute. The synchronous export is refused with an actionable `400` response when: * the combined row limit exceeds the configured maximum; or * any query does not have a finite row limit. The error explains that `EXCEL_EXPORT_S3_BUCKET` must be configured to run the export asynchronously. The server request timeout remains a final safety mechanism, not the execution-path selector. Waiting for a timeout would waste work already performed and would not give the user an actionable explanation. The budget check runs before acquiring the export lock, so a rejected request never leaves a lock held. #### Image exports remain asynchronous `mode=images` is not supported by the synchronous path. Image exports render charts through the headless webdriver, and their cost cannot be bounded by the row budget. When no bucket is configured, the endpoint rejects image exports with a message explaining that they must run in the background. #### Shared workbook generation Workbook generation has moved from the Celery task into: ```text superset/dashboards/excel_export/workbook.py ``` Both execution paths now use the same workbook builder. The Celery task remains responsible only for task execution, storage upload, email delivery, and async-specific error handling. Each query context is resolved once. The sizing step returns an `InlineExportPlan` containing the resolved contexts, and the workbook builder executes those exact contexts. This matters when `EXCEL_EXPORT_QUERY_CONTEXT_BUILDER` points to an external service: resolving the context twice would add unnecessary cost and could allow one set of queries to be measured while a different set is executed. #### Skipped charts An “Export Summary” worksheet is now added whenever any chart is skipped, rather than only when every chart is skipped. This keeps the skipped-chart information inside the workbook regardless of how the file is delivered. It is particularly important for the synchronous path, where there is no email containing that information. The summary-sheet behavior matches the implementation in #43805. #### Concurrency and cleanup Both execution paths use the existing per-user and per-dashboard distributed lock. The synchronous path releases the lock and deletes its temporary file in a `finally` block, whether the request succeeds or fails. This change does not migrate Excel exports to GTF or introduce `task_key` deduplication. #43407 applies to asynchronous chart-data queries, not dashboard XLSX exports. #### Guest and embedded sessions Guest, embedded, anonymous, and no-email sessions remain blocked on both paths. The synchronous path does not bypass the existing restriction. A regression test covers a guest token that grants access to the dashboard and confirms that the export is still rejected. Guest export support remains part of #43805. #### Relationship to #43805 #43805 replaces `EXCEL_EXPORT_S3_BUCKET` with the pluggable `EXPORT_STORAGE` configuration. The storage check introduced here is isolated in: ```text superset/dashboards/excel_export/storage.py ``` After #43805 lands, the helper can be updated to check for both an `EXPORT_STORAGE` bucket and backend. Rebase conflicts are expected in three areas also changed by #43805: * the `export_xlsx` endpoint; * the task’s upload logic; * the frontend `onExportXlsx` handler. The summary-sheet change is intentionally written to match #43805. #### API behavior `POST /api/v1/dashboard/<id>/export_xlsx/` now returns one of two successful responses: * `200` with the generated `.xlsx` file when synchronous export is used; * `202` with a job ID when the export is queued. The endpoint no longer returns `501` solely because `EXCEL_EXPORT_S3_BUCKET` is not configured. Both successful response types are documented in OpenAPI. ### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF To capture on a deployment without `EXCEL_EXPORT_S3_BUCKET` configured: **Before** Download → Export Data to Excel → error toast: “Excel export is not configured on this server.” No file is downloaded. **After** Download → Export Data to Excel → the item displays “Preparing export…” and is disabled while the request runs → the workbook downloads → a “Dashboard data exported to Excel” toast appears. ### TESTING INSTRUCTIONS #### Automated ```bash pytest tests/unit_tests/tasks/test_export_dashboard_excel.py tests/unit_tests/dashboards/ ``` 218 tests. ```bash pytest tests/integration_tests/dashboards/api_tests.py -k export_xlsx ``` 20 tests covering: * configured storage preserving the asynchronous `202` response; * unconfigured storage returning a synchronous `200` XLSX response; * both paths receiving the same dashboard, filter state, mode, and user; * the workbook executing the exact query contexts measured by the budget; * over-budget and indeterminate-budget refusals; * `mode=images` being rejected without storage; * lock release after synchronous success and failure; * temporary-file cleanup after success and failure; * concurrent exports being rejected by the existing lock; * guest sessions remaining blocked; * existing dashboard access checks. ```bash npm run test -- DownloadMenuItems.test.tsx ``` 21 tests. #### Manual With no `EXCEL_EXPORT_S3_BUCKET` configured: 1. Open a dashboard with a few charts and apply a native filter. 2. Select Download → **Export Data to Excel**. 3. Confirm that the workbook downloads directly, contains one worksheet per chart, and reflects the active filter. 4. Enable the webdriver screenshot flags and confirm that **Export Images to Excel** is rejected with a message explaining that `EXCEL_EXPORT_S3_BUCKET` is required. 5. Set `EXCEL_EXPORT_SYNC_MAX_ROWS = 10` and retry the data export. Confirm that it is rejected with an actionable `400` response before any query runs. 6. Add a chart without a saved `query_context` whose visualization type is outside the rebuild allowlist, such as `mixed_timeseries`. Confirm that the workbook contains an “Export Summary” worksheet naming the skipped chart. Then configure `EXCEL_EXPORT_S3_BUCKET`, start the worker and SMTP service, and confirm that the existing asynchronous path is unchanged: * the endpoint returns `202`; * the UI displays “You’ll receive an email when it’s ready”; * the download link arrives by email. ### ADDITIONAL INFORMATION * [ ] Has associated issue * [ ] Required feature flags * [x] Changes UI * [ ] Includes DB migration (follow the approval process in [[SIP-59](https://github.com/apache/superset/issues/13351)](https://github.com/apache/superset/issues/13351)) * [x] Introduces new feature or API behavior * [ ] Removes an existing feature or API -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
