Skip to content

chore(reports): deprecate Slack v1 and harden Slack v2 tests#39914

Merged
rusackas merged 16 commits into
masterfrom
claude/angry-bartik-10b01d
Jul 4, 2026
Merged

chore(reports): deprecate Slack v1 and harden Slack v2 tests#39914
rusackas merged 16 commits into
masterfrom
claude/angry-bartik-10b01d

Conversation

@sadpandajoe

@sadpandajoe sadpandajoe commented May 6, 2026

Copy link
Copy Markdown
Member

SUMMARY

Deprecates the legacy Slack v1 integration for Alerts and Reports and adds bulletproof unit-test coverage for SlackV2Notification ahead of v1 removal in the next major.

Why now. Slack retired the files.upload endpoint in 2025, so v1 sends that include screenshots, CSVs, or PDFs already fail at the API level — only text-only chat_postMessage sends still succeed via the legacy path. Existing recipients with the channels:read scope already auto-upgrade to SlackV2 on first send via the update_report_schedule_slack_v2 flow; the only thing keeping the upgrade from running out of the box was ALERT_REPORT_SLACK_V2 defaulting to False.

What this change does:

  • Flips the ALERT_REPORT_SLACK_V2 default to True (the docs JSON re-syncs from config.py automatically via the feature-flags-sync pre-commit hook).
  • Adds one-shot DeprecationWarning + logger.warning emissions when the v1 path still runs — both the flag-off and the missing-channels:read paths. The scope-missing logger.warning continues to fire every send so operators see the actionable scope hint in report-execution logs.
  • Updates the stale # TODO: Remove in 6.0.0 comment on SlackNotification with an accurate deprecation note (6.0 already shipped).
  • Adds an UPDATING.md entry under ## Next with operator action items (grant channels:read, recipients auto-upgrade on next send, v1 removal targeted for the next major).

Bulletproof v2 test coverage added in tests/unit_tests/reports/notifications/slack_tests.py and tests/unit_tests/utils/slack_test.py:

  • files_upload_v2 invocation with PNG (single + multiple), CSV, and PDF — asserting channel, file, title, filename, and initial_comment
  • Multi-channel fan-out (3 channels × 2 files = 6 uploads) and text-only multi-channel chat_postMessage
  • Inline-file precedence (CSV beats screenshots beats PDF)
  • Parametrized exception mapping across all 7 slack_sdk error types → the 4 NotificationException subclasses
  • Statsd .ok and .warning gauge emission via the @statsd_gauge decorator
  • execution_id propagation from g.logs_context to the success log, plus the falsy-fallback path
  • End-to-end auto-upgrade round-trip: v1 SLACK recipient with channel names → SlackV1NotificationErrorupdate_report_schedule_slack_v2 rewrites the row to channel IDs → fresh SlackV2Notification fast-paths the next send with no resolver call
  • should_use_v2_api() warning behavior: DeprecationWarning emitted exactly once across multiple calls in both flag-off and scope-missing paths

Retry bug fixed (intended, in scope): SlackV2Notification.send() was decorated with @backoff.on_exception(SlackApiError, ..., max_tries=5), but the function catches every SlackApiError internally and re-raises it as NotificationUnprocessableException before backoff can see it — so no retries ever fired and a single transient blip failed the report immediately. This PR switches the decorator to retry on NotificationUnprocessableException (the type send() actually raises for transient Slack failures), mirroring the working pattern in webhook.py. Transient failures now retry up to 5 times with exponential backoff (~150s worst case, matching the existing webhook.py budget); non-transient errors (NotificationParamException, NotificationMalformedException, NotificationAuthorizationException) still surface immediately with no retry. Locked in by test_v2_send_retries_on_transient_slack_api_error (persistent failure → 5 attempts), test_v2_send_retries_then_succeeds_on_transient_failure (fail twice then succeed → 3 attempts), and test_v2_send_does_not_retry_param_errors (permanent error → 1 attempt).

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A — no UI changes.

TESTING INSTRUCTIONS

# Unit tests
PYTHONPATH=/path/to/superset_core python3 -m pytest \
  tests/unit_tests/reports/ \
  tests/unit_tests/commands/report/ \
  tests/unit_tests/utils/slack_test.py

All 319 tests pass on this branch (29 in slack_tests.py, 14 in utils/slack_test.py, plus the existing report unit suite).

To validate the deprecation behavior manually:

  1. With ALERT_REPORT_SLACK_V2: False set explicitly in superset_config.py and a Slack recipient configured, fire a report. You should see a single DeprecationWarning and logger.warning line on the first send, then no more for that process.
  2. With the default (True) and a Slack bot lacking channels:read, fire a report. You should see logger.warning describing the missing scope on every send, plus a one-shot DeprecationWarning.
  3. With the default and a Slack bot that has channels:read, the first send for a SLACK-type recipient auto-upgrades the row to SLACKV2 with channel IDs (existing behavior — now exercised by the round-trip unit test).

ADDITIONAL INFORMATION

  • Has associated issue:
  • Required feature flags: ALERT_REPORT_SLACK_V2 default flipped to True
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

🤖 Generated with Claude Code

@github-actions github-actions Bot added the doc Namespace | Anything related to documentation label May 6, 2026
@netlify

netlify Bot commented May 6, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit 17c8d68
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a47c3328a33980008478e8b
😎 Deploy Preview https://deploy-preview-39914--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codecov

codecov Bot commented May 6, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 60.56338% with 28 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.45%. Comparing base (0e6c583) to head (17c8d68).
⚠️ Report is 4 commits behind head on master.

Files with missing lines Patch % Lines
superset/reports/notifications/slackv2.py 37.50% 15 Missing ⚠️
superset/utils/slack.py 64.51% 9 Missing and 2 partials ⚠️
superset/commands/report/execute.py 87.50% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #39914      +/-   ##
==========================================
- Coverage   64.63%   64.45%   -0.18%     
==========================================
  Files        2685     2680       -5     
  Lines      148530   148015     -515     
  Branches    34267    34007     -260     
==========================================
- Hits        95999    95401     -598     
- Misses      50775    50849      +74     
- Partials     1756     1765       +9     
Flag Coverage Δ
hive 39.18% <23.94%> (-0.01%) ⬇️
mysql 57.82% <60.56%> (+<0.01%) ⬆️
postgres 57.88% <60.56%> (+<0.01%) ⬆️
presto 40.72% <23.94%> (?)
python 59.27% <60.56%> (+0.06%) ⬆️
sqlite 57.46% <60.56%> (+<0.01%) ⬆️
unit 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@sadpandajoe sadpandajoe added risk:breaking-change Issues or PRs that will introduce breaking changes review:draft labels May 6, 2026
@sadpandajoe
sadpandajoe force-pushed the claude/angry-bartik-10b01d branch 3 times, most recently from 0002901 to a7a5d8e Compare June 5, 2026 22:26
@sadpandajoe
sadpandajoe force-pushed the claude/angry-bartik-10b01d branch from a7a5d8e to fe4c923 Compare June 11, 2026 00:51
@sadpandajoe
sadpandajoe requested a review from Copilot June 12, 2026 21:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR deprecates the legacy Slack v1 integration for Alerts & Reports, flips ALERT_REPORT_SLACK_V2 to default True, and adds extensive unit test coverage around Slack v2 notification behavior and the v1→v2 upgrade decision path.

Changes:

  • Default-enable Slack v2 via ALERT_REPORT_SLACK_V2=True and document the deprecation/removal plan for Slack v1 (incl. operator action items).
  • Add once-per-process deprecation warning emitters for Slack v1 fallback paths and expand should_use_v2_api() test coverage.
  • Add broad Slack v2 send-path unit tests (uploads, multi-channel fan-out, exception mapping, statsd gauges, logging context) and adjust one integration test for the new probe/upgrade behavior.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
UPDATING.md Adds an operator-facing “Next” entry describing Slack v1 deprecation and Slack v2 defaults/scopes.
superset/config.py Flips ALERT_REPORT_SLACK_V2 default to True and expands the flag’s inline description.
docs/static/feature-flags.json Syncs the feature flag default/description to match config.py.
superset/utils/slack.py Adds deprecation warning helpers and emits them from should_use_v2_api(); updates fallback warning text.
superset/reports/notifications/slack.py Updates Slack v1 deprecation comment to reflect current removal plan/Slack API changes.
superset/reports/notifications/slackv2.py Changes backoff decorator to retry the exception type actually raised by send().
tests/unit_tests/utils/slack_test.py Adds unit tests for should_use_v2_api() warning behavior and cache reset fixture.
tests/unit_tests/reports/notifications/slack_tests.py Adds extensive Slack v2 send-path tests and a backoff-sleep skipping fixture.
tests/integration_tests/reports/commands_tests.py Mocks the v2 probe/upgrade channel resolution for a Slack token callable integration test.

Comment thread superset/utils/slack.py Outdated
Comment thread superset/reports/notifications/slackv2.py Outdated
@rusackas rusackas moved this to Propose for Lazy Consensus in Apache Superset 7.0 Jun 15, 2026
@sadpandajoe
sadpandajoe marked this pull request as ready for review June 15, 2026 17:27
@sadpandajoe
sadpandajoe marked this pull request as draft June 15, 2026 17:48
@sadpandajoe
sadpandajoe force-pushed the claude/angry-bartik-10b01d branch from 87903a0 to 08a45de Compare June 15, 2026 17:52
@dosubot dosubot Bot added the alert-reports Namespace | Anything related to the Alert & Reports feature label Jun 15, 2026
@sadpandajoe
sadpandajoe marked this pull request as ready for review June 17, 2026 21:15
@bito-code-review

bito-code-review Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #8ef6f1

Actionable Suggestions - 0
Review Details
  • Files reviewed - 7 · Commit Range: f4638a8..59f9beb
    • superset/config.py
    • superset/reports/notifications/slack.py
    • superset/reports/notifications/slackv2.py
    • superset/utils/slack.py
    • tests/integration_tests/reports/commands_tests.py
    • tests/unit_tests/reports/notifications/slack_tests.py
    • tests/unit_tests/utils/slack_test.py
  • Files skipped - 2
    • UPDATING.md - Reason: Filter setting
    • docs/static/feature-flags.json - Reason: Filter setting
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

sadpandajoe and others added 5 commits June 25, 2026 17:54
…True, harden v2 tests

Flips the ALERT_REPORT_SLACK_V2 feature flag default to True so the v2
auto-upgrade path runs out of the box, and adds one-shot DeprecationWarning
+ logger.warning emissions when v1 still runs (flag explicitly off, or bot
missing the channels:read scope). Slack retired the legacy files.upload
endpoint in 2025, so v1 file uploads are already broken at the API level —
only text-only chat_postMessage sends still succeed via the legacy path.

The bulk of the change is bulletproof unit-test coverage for SlackV2Notification
ahead of v1 removal in the next major:

- files_upload_v2 invocation with PNG (single + multiple), CSV, and PDF,
  asserting channel, file, title, filename, and initial_comment kwargs
- multi-channel fan-out (3 channels x 2 files = 6 uploads) and text-only
  multi-channel chat_postMessage
- inline-file precedence (CSV beats screenshots beats PDF)
- parametrized exception mapping across 7 slack_sdk error types -> the
  4 NotificationException subclasses
- statsd .ok and .warning gauge emission via the @statsd_gauge decorator
- execution_id propagation from g.logs_context to the success log, plus
  the falsy g.logs_context fallback path
- end-to-end auto-upgrade round-trip: v1 SLACK recipient with channel
  names raises SlackV1NotificationError -> update_report_schedule_slack_v2
  rewrites the row to channel IDs -> SlackV2Notification fast-paths the
  next send with no further channel resolution
- should_use_v2_api() warning behavior: deprecation warning emitted exactly
  once across multiple calls in both the flag-off and scope-missing paths,
  with the scope-missing logger.warning continuing to fire each call so
  operators see the actionable scope hint in their report-execution logs

Also locks in current behavior of the @backoff.on_exception(SlackApiError, ...)
decorator on send(): because send() catches every SlackApiError internally
and re-raises as NotificationUnprocessableException, backoff never sees the
target exception type and no retries actually fire. Test asserts call_count
== 1 with a docstring marking this as a known design issue to address
separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…test

With ALERT_REPORT_SLACK_V2 now defaulting to True, a SLACK recipient's
first send triggers the v1->v2 auto-upgrade, which calls
get_channels_with_search to resolve channel names to channel IDs. The
existing test mocked WebClient.conversations_list to return a plain dict
that lacked the `.data` attribute the upgrade path reads, so the
upgrade raised "'dict' object has no attribute 'data'" and the test
errored.

Patch get_channels_with_search directly (matching the pattern already
used by the other v2-conversion tests in this file) so the upgrade can
resolve channels without going through the WebClient mock plumbing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t errors

The @backoff.on_exception decorator on SlackV2Notification.send() was
configured to retry on SlackApiError, but the function's own try/except
catches every SlackApiError and re-raises as NotificationUnprocessableException
before the decorator can see it. As a result, no retries were happening —
a single transient failure (rate limit, connection blip) would fail the
report immediately, defeating the intent of the 5-attempt retry budget.

Switch the decorator to retry on NotificationUnprocessableException, which
is the exception type that send() actually raises for transient Slack
failures (SlackApiError, SlackClientNotConnectedError, and the SlackClientError
catch-all). Mirrors the working pattern already in webhook.py.

Non-transient errors (NotificationParamException, NotificationMalformedException,
NotificationAuthorizationException) still surface immediately — they aren't
retryable and shouldn't be retried.

Test changes:
- Replaces the prior "locks in broken behavior" regression test with
  test_v2_send_retries_on_transient_slack_api_error asserting call_count == 5
- Adds test_v2_send_does_not_retry_param_errors verifying that BotUserAccessError
  → NotificationParamException is NOT retried (call_count == 1)
- Adds an autouse fixture that patches backoff._sync.time.sleep so unit-test
  retries complete in milliseconds rather than the ~150s of real exponential
  backoff. Without this, the parametrized exception-mapping cases that map
  to NotificationUnprocessableException balloon the test runtime by ~75s

The v1 SlackNotification has the same bug but is being deprecated in this
release; not worth fixing there since v1's file_uploads endpoint is already
dead at Slack's side and only the text-only chat_postMessage path still works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-liner style

Replaces the multi-section paragraph form with the single-bullet,
PR-link-prefixed style used by the historical entries in this file
(see the original Slack v2 deprecation in 4.1.0 / #29264). Same
information, less ceremony.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code-review changes:

- Replace module-level `_v1_*_warning_emitted` booleans with `functools.cache`-
  decorated `_emit_v1_*_deprecation` helpers. Bare module globals had a
  read-then-write race under multi-threaded WSGI workers; functools.cache is
  thread-safe under the GIL and produces actually-once-per-process semantics
  without the noqa: PLW0603 escape hatch.
- Mention `groups:read` (in addition to `channels:read`) wherever the scope
  requirement appears: deprecation message constant, config.py comment, the
  scope-missing logger.warning, UPDATING.md, and (auto-synced) feature-flags.json.
  The v2 channel resolver queries both public_channel and private_channel types,
  so granting only `channels:read` silently breaks private-channel reports.
- Add `test_propagates_non_slack_api_errors_from_probe` — locks in that any
  exception other than SlackApiError (network, transport) propagates out of
  should_use_v2_api rather than masquerading as a missing-scope warning.
- Drop a tautological `assert_not_called()` on `get_channels_with_search` in
  the auto-upgrade round-trip test. SlackV2Notification.send() never calls that
  helper in any path, so the assertion was true by construction rather than
  by the test exercising a real fast path.
- Pin assertions on the deprecation-warning *message* to the exported
  `_SLACK_V1_DEPRECATION_MESSAGE` constant instead of substring fragments.
- Update the test autouse fixture to clear the new functools.cache caches
  rather than reset the now-removed module globals.

Three architectural concerns from review (auto-upgrade transaction race,
concurrent worker upgrade race, end-of-deprecation cleanup migration) are
pre-existing on the upgrade path and tracked as separate follow-up tasks
rather than expanded into this PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
sadpandajoe and others added 2 commits June 25, 2026 17:54
…esponse

`should_use_v2_api` only read the probe error code via `response.data`, so a
`SlackApiError` carrying a plain-dict response (as the SDK and the unit tests
produce) resolved to an empty code, skipped the scope-missing branch, and never
emitted the v1 DeprecationWarning. Read the code from `.data` when present and
fall back to the response dict itself, fixing
test_returns_false_when_scope_missing_and_emits_deprecation_once.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Close two gaps in the Slack v1-deprecation tests surfaced in review:

- should_use_v2_api scope detection was only exercised with a plain-dict
  response; the production-default path reads the error code via
  getattr(response, "data") on a real SlackResponse. Add a case using the
  existing MockResponse helper so that branch is covered.
- The backoff switch to NotificationUnprocessableException is meant to let a
  transient failure retry and ultimately succeed; only persistent-fail (5x)
  and never-retry were tested. Add a fail-twice-then-succeed case asserting
  3 attempts and the success gauge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sadpandajoe
sadpandajoe force-pushed the claude/angry-bartik-10b01d branch from 59f9beb to b4ce1ff Compare June 25, 2026 17:55
Comment thread tests/unit_tests/utils/slack_test.py
Comment thread tests/unit_tests/utils/slack_test.py
Comment thread tests/unit_tests/utils/slack_test.py
Comment thread superset/utils/slack.py
Comment thread superset/utils/slack.py
Comment thread tests/integration_tests/reports/commands_tests.py
Comment thread tests/unit_tests/reports/notifications/slack_tests.py
Comment thread tests/unit_tests/reports/notifications/slack_tests.py
Comment thread tests/unit_tests/reports/notifications/slack_tests.py
Comment thread tests/unit_tests/reports/notifications/slack_tests.py
Comment thread tests/unit_tests/reports/notifications/slack_tests.py
Comment thread superset/reports/notifications/slackv2.py Outdated

@bito-code-review bito-code-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #b90a32

Actionable Suggestions - 2
  • tests/unit_tests/reports/notifications/slack_tests.py - 2
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset/reports/notifications/slack.py - 1
    • Misleading doc on v2 upgrade trigger · Line 59-60
Review Details
  • Files reviewed - 7 · Commit Range: 3a46222..b4ce1ff
    • superset/config.py
    • superset/reports/notifications/slack.py
    • superset/reports/notifications/slackv2.py
    • superset/utils/slack.py
    • tests/integration_tests/reports/commands_tests.py
    • tests/unit_tests/reports/notifications/slack_tests.py
    • tests/unit_tests/utils/slack_test.py
  • Files skipped - 2
    • UPDATING.md - Reason: Filter setting
    • docs/static/feature-flags.json - Reason: Filter setting
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread tests/unit_tests/reports/notifications/slack_tests.py
Comment thread tests/unit_tests/reports/notifications/slack_tests.py

@bito-code-review bito-code-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #6adeae

Actionable Suggestions - 1
  • superset/reports/notifications/slackv2.py - 1
Review Details
  • Files reviewed - 5 · Commit Range: b4ce1ff..3f60bf2
    • superset/config.py
    • superset/reports/notifications/slackv2.py
    • superset/utils/slack.py
    • tests/unit_tests/reports/notifications/slack_tests.py
    • tests/unit_tests/utils/slack_test.py
  • Files skipped - 2
    • UPDATING.md - Reason: Filter setting
    • docs/static/feature-flags.json - Reason: Filter setting
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread superset/reports/notifications/slackv2.py
sadpandajoe and others added 2 commits June 26, 2026 10:12
…test coverage

Address three review findings on the Slack v1 deprecation path:

- utils/slack.py: should_use_v2_api() now catches the SDK base
  SlackClientError (aliased to avoid the local class) so probe failures
  like SlackClientNotConnectedError fall back to v1 instead of escaping
  raw and aborting the recipient loop in _send(). Non-SDK errors still
  propagate.
- commands/report/execute.py: update_report_schedule_slack_v2() resolves
  all Slack recipients into a temporary list and swaps type/config only
  after every recipient resolves, so a mid-loop failure can no longer
  persist a partially upgraded schedule.
- slack_tests.py: add an SDK-faithful SlackResponse (real status_code)
  so the 429/5xx retry branch in _give_up_slack_api_retry is exercised;
  the prior plain-dict mocks left it uncovered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #1ff323

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • tests/unit_tests/commands/report/execute_test.py - 2
  • superset/utils/slack.py - 1
Review Details
  • Files reviewed - 5 · Commit Range: 3f60bf2..1378c28
    • superset/commands/report/execute.py
    • superset/utils/slack.py
    • tests/unit_tests/commands/report/execute_test.py
    • tests/unit_tests/reports/notifications/slack_tests.py
    • tests/unit_tests/utils/slack_test.py
  • Files skipped - 0
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

…-10b01d

# Conflicts:
#	superset/commands/report/execute.py
#	superset/utils/slack.py
#	tests/unit_tests/commands/report/execute_test.py
#	tests/unit_tests/utils/slack_test.py
Comment thread UPDATING.md Outdated

The migration is transactional (all-or-nothing) and idempotent — it can be safely re-run or resumed. Note that AES-GCM, unlike AES-CBC, does not support querying directly over encrypted columns; audit any code that filters on an encrypted column before switching. See the SIP at `docs/sip/authenticated-encryption-at-rest.md` for details.

- [39914](https://github.com/apache/superset/pull/39914) `ALERT_REPORT_SLACK_V2` now defaults to `True` and the legacy Slack v1 integration (`Slack` recipient type, `files.upload` API) is deprecated for removal in the next major. Slack retired `files.upload` in 2025, so v1 file-bearing sends already fail at the API level — only text-only `chat_postMessage` still works via the legacy path. Grant your Slack bot the `channels:read` scope (and `groups:read` if you use private channels) so existing `Slack` recipients can be auto-upgraded to `SlackV2` on next send. Operators who explicitly override the flag to `False` will see a one-shot `DeprecationWarning` plus a `logger.warning`; remove the override or grant the scopes to clear it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Posting on Elizabeth's behalf — this is her PR reviewer agent. Forward any pushback to her and she'll loop me back in.

Quick clarification on the files.upload framing in the PR description and UPDATING.md — my understanding is that Slack deprecated files.upload for new apps created after the cutoff, but existing/legacy apps that were already using it before that date are grandfathered and the endpoint still works for them. The PR description says "Slack retired the files.upload endpoint in 2025" and the UPDATING.md entry repeats this, which reads as a universal retirement.

If that distinction is right, the claim that "v1 file-bearing sends already fail at the API level" is only true for newer app installs — operators running older Slack apps may still be getting working file uploads via v1 today. That affects how urgent the migration feels to those operators.

Could you confirm whether the retirement is app-creation-date-gated, and if so, would it be worth softening the wording in UPDATING.md to something like "Slack deprecated files.upload for new apps in 2025 and has announced its eventual full retirement" rather than stating it as already done universally? Happy to be corrected if I have the timeline wrong.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right that there was a new-vs-existing split — that's worth being precise about, so I've tightened the wording.

Timeline: Slack stopped letting new apps register files.upload on May 16, 2024, but existing apps were grandfathered, so legacy integrations kept working past that date. The sunset for existing apps was originally announced for March 2025, then pushed back, and files.upload was finally retired for all apps on November 12, 2025. So there's no grandfathering left — every app, new or old, now gets an API error on files.upload, which is why Superset's v1 file-bearing sends already fail rather than "will eventually."

I went a bit more specific than the "eventual retirement" framing precisely because that window has since closed. Updated the entry to call out both dates and the new-vs-existing distinction.

Refs: https://docs.slack.dev/changelog/2024/05/16/apps/ (new-app cutoff) and https://docs.slack.dev/changelog/2025/03/17/files-upload-extension/ (final Nov 12 2025 retirement).

Add precise dates and the new-vs-existing-apps distinction to the
UPDATING.md Slack v2 entry: new apps were blocked from files.upload in
May 2024 and the method was fully retired for all apps on November 12,
2025. Keeps the existing "v1 file-bearing sends now fail at the API
level" conclusion, which holds now that retirement is universal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread superset/reports/notifications/slackv2.py
Comment thread superset/utils/slack.py
Comment thread superset/utils/slack.py
Comment thread superset/utils/slack.py
Comment thread superset/utils/slack.py
Comment thread superset/utils/slack.py
Comment thread tests/unit_tests/commands/report/execute_test.py
Comment thread tests/unit_tests/reports/notifications/slack_tests.py
Comment thread tests/unit_tests/reports/notifications/slack_tests.py
Comment thread tests/unit_tests/reports/notifications/slack_tests.py
Comment thread tests/unit_tests/reports/notifications/slack_tests.py
Comment thread tests/unit_tests/utils/slack_test.py
Comment thread tests/unit_tests/utils/slack_test.py
Comment thread tests/unit_tests/utils/slack_test.py
Comment thread tests/unit_tests/utils/slack_test.py
Comment thread tests/unit_tests/utils/slack_test.py
Comment thread superset/reports/notifications/slack.py Outdated
Comment thread superset/utils/slack.py
The v1 SlackNotification deprecation comment said recipients with the
`channels:read` scope are auto-upgraded to SlackV2, but the v2 probe
(`should_use_v2_api`) lists both public and private channel types and so
requires `channels:read` and `groups:read`. Name both scopes to match
the deprecation message and the probe's log line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #f13d86

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: 1378c28..e975e18
    • superset/reports/notifications/slack.py
  • Files skipped - 1
    • UPDATING.md - Reason: Filter setting
  • Tools
    • MyPy (Static Code Analysis) - ✔︎ Successful
    • Astral Ruff (Static Code Analysis) - ✔︎ Successful
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread superset/utils/slack.py
@@ -34,12 +36,42 @@

logger = logging.getLogger(__name__)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add an explicit type annotation to this module-level constant to satisfy the type-hint requirement for relevant variables. [custom_rule]

Severity Level: Minor ⚠️

Why it matters? 🤔

This new module-level constant is unannotated even though its type is clear and stable, so it matches the rule requiring type hints on newly added Python variables that can be annotated.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset/utils/slack.py
**Line:** 38:38
**Comment:**
	*Custom Rule: Add an explicit type annotation to this module-level constant to satisfy the type-hint requirement for relevant variables.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines 1990 to 1995
def test_slack_token_callable_chart_report(
screenshot_mock,
slack_client_mock_class,
get_channels_with_search_mock,
create_report_slack_chart,
):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add a type annotation for the newly added get_channels_with_search_mock parameter and ensure the modified test function includes explicit type hints (including return type). [custom_rule]

Severity Level: Minor ⚠️

Why it matters? 🤔

The modified test function test_slack_token_callable_chart_report still omits type hints on its parameters and has no return type annotation, which violates the Python type-hints rule for newly added or modified code.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** tests/integration_tests/reports/commands_tests.py
**Line:** 1990:1995
**Comment:**
	*Custom Rule: Add a type annotation for the newly added `get_channels_with_search_mock` parameter and ensure the modified test function includes explicit type hints (including return type).

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +49 to +60
@pytest.fixture(autouse=True)
def _skip_backoff_sleep():
"""Make any @backoff.on_exception retries instant.

SlackV2Notification.send() retries up to 5 times with `backoff.expo(factor=10,
base=2)` — that's ~150s of real sleep on a persistently-failing send. We
don't care about the wall-clock waits in unit tests; patching `time.sleep`
inside backoff's sync runner keeps the assertion semantics (call_count,
raised exception type) without the wait.
"""
with patch("backoff._sync.time.sleep"):
yield

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add an explicit return type annotation to this fixture function to satisfy the type-hint requirement. [custom_rule]

Severity Level: Minor ⚠️

Why it matters? 🤔

This is a new Python fixture function with no return type annotation, which violates the type-hint requirement for modified code.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** tests/unit_tests/reports/notifications/slack_tests.py
**Line:** 49:60
**Comment:**
	*Custom Rule: Add an explicit return type annotation to this fixture function to satisfy the type-hint requirement.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +650 to +677
def test_v2_inline_files_precedence(mock_header_data) -> None:
"""CSV beats screenshots beats PDF; only one inline-file type is sent."""
content = _make_content(
mock_header_data,
csv=b"a,b\n1,2",
screenshots=[b"shot-1"],
pdf=b"%PDF",
)
notification = _make_v2_notification(content, target="C12345")
file_type, files = notification._get_inline_files()
assert file_type == "csv"
assert files == [b"a,b\n1,2"]

content = _make_content(
mock_header_data,
screenshots=[b"shot-1"],
pdf=b"%PDF",
)
notification = _make_v2_notification(content, target="C12345")
file_type, files = notification._get_inline_files()
assert file_type == "png"
assert files == [b"shot-1"]

content = _make_content(mock_header_data, pdf=b"%PDF")
notification = _make_v2_notification(content, target="C12345")
file_type, files = notification._get_inline_files()
assert file_type == "pdf"
assert files == [b"%PDF"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add a type annotation for the mock_header_data test parameter. [custom_rule]

Severity Level: Minor ⚠️

Why it matters? 🤔

The test parameter mock_header_data is unannotated even though it can be typed, so this is a real type-hint omission under the rule.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** tests/unit_tests/reports/notifications/slack_tests.py
**Line:** 650:677
**Comment:**
	*Custom Rule: Add a type annotation for the `mock_header_data` test parameter.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +370 to +399
def test_returns_false_when_scope_missing_and_emits_deprecation_once(self, mocker):
mocker.patch(
"superset.utils.slack.feature_flag_manager.is_feature_enabled",
return_value=True,
)
mock_client = mocker.Mock()
# The Slack SDK exposes the error code as `response["error"]`; that is
# what `should_use_v2_api` branches on to decide whether the v1
# deprecation warning is the appropriate signal.
mock_client.conversations_list.side_effect = SlackApiError(
message="missing_scope", response={"ok": False, "error": "missing_scope"}
)
mocker.patch("superset.utils.slack.get_slack_client", return_value=mock_client)
logger_mock = mocker.patch("superset.utils.slack.logger")

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
assert should_use_v2_api() is False
assert should_use_v2_api() is False
assert should_use_v2_api() is False

deprecation_warnings = [
w for w in caught if issubclass(w.category, DeprecationWarning)
]
# DeprecationWarning emitted exactly once across multiple calls.
assert len(deprecation_warnings) == 1
assert str(deprecation_warnings[0].message) == _SLACK_V1_DEPRECATION_MESSAGE
# The user-visible scope-missing log fires every time, since operators
# need to see the actionable message in their report-execution logs.
assert logger_mock.warning.call_count == 3

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add missing type annotations to this method signature so all parameters and the return type are explicitly typed. [custom_rule]

Severity Level: Minor ⚠️

Why it matters? 🤔

This added test method lacks explicit annotations for its parameters and return value, which is exactly the type-hint omission the rule flags.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** tests/unit_tests/utils/slack_test.py
**Line:** 370:399
**Comment:**
	*Custom Rule: Add missing type annotations to this method signature so all parameters and the return type are explicitly typed.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Comment on lines +404 to +435
def test_scope_missing_detected_via_slack_response_data_shape(self, mocker):
"""The real Slack SDK sets `SlackApiError.response` to a `SlackResponse`
whose payload lives in `.data` — not a plain dict. This is the
production-default code path, so it must be exercised directly:
`should_use_v2_api` reads the error code via `getattr(response, "data")`
and the scope-missing branch must still fire.
"""
mocker.patch(
"superset.utils.slack.feature_flag_manager.is_feature_enabled",
return_value=True,
)
mock_client = mocker.Mock()
# MockResponse mirrors SlackResponse: the error payload is on `.data`,
# exactly as the live SDK delivers it.
mock_client.conversations_list.side_effect = SlackApiError(
message="missing_scope",
response=MockResponse({"ok": False, "error": "missing_scope"}),
)
mocker.patch("superset.utils.slack.get_slack_client", return_value=mock_client)
logger_mock = mocker.patch("superset.utils.slack.logger")

with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
assert should_use_v2_api() is False

deprecation_warnings = [
w for w in caught if issubclass(w.category, DeprecationWarning)
]
assert len(deprecation_warnings) == 1
assert logger_mock.warning.call_count == 1
assert "channels:read" in logger_mock.warning.call_args.args[0]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Provide complete type hints on this new test method signature, including typing for injected fixtures and the return value. [custom_rule]

Severity Level: Minor ⚠️

Why it matters? 🤔

This newly added test method is missing type hints on its parameters and return type, so it violates the stated Python type-hint rule.

Fix in Cursor Fix in VSCode Claude

(Use Cmd/Ctrl + Click for best experience)

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** tests/unit_tests/utils/slack_test.py
**Line:** 404:435
**Comment:**
	*Custom Rule: Provide complete type hints on this new test method signature, including typing for injected fixtures and the return value.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

@rusackas rusackas left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Nice cleanup on the atomic v1→v2 upgrade path, and the per-call retry helper is a real fix — the old decorator never actually fired since send() swallowed the SlackApiError before backoff could see it. Test coverage here is thorough, and thanks for tightening up the files.upload timeline wording per Elizabeth's note.

One non-blocking nit: the PR description (and one test docstring) still describe the retry fix as swapping the decorator's exception type, but the final code retries at the per-API-call level via _call_slack_api — worth a quick edit for posterity, but not holding this up over it.

@rusackas
rusackas merged commit 1e50316 into master Jul 4, 2026
59 checks passed
@rusackas
rusackas deleted the claude/angry-bartik-10b01d branch July 4, 2026 23:41
@github-project-automation github-project-automation Bot moved this from Propose for Lazy Consensus to Lazy Consensus Reached in Apache Superset 7.0 Jul 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

alert-reports Namespace | Anything related to the Alert & Reports feature doc Namespace | Anything related to documentation risk:breaking-change Issues or PRs that will introduce breaking changes size/XXL

Projects

Status: Lazy Consensus Reached

Development

Successfully merging this pull request may close these issues.

4 participants