Skip to content

feat(scheduled reports): Add API endpoints & serializers#108418

Open
shashjar wants to merge 8 commits intoshashjar/scheduled-reports-implement-execution-tasksfrom
shashjar/scheduled-reports-api-endpoints-and-serializer
Open

feat(scheduled reports): Add API endpoints & serializers#108418
shashjar wants to merge 8 commits intoshashjar/scheduled-reports-implement-execution-tasksfrom
shashjar/scheduled-reports-api-endpoints-and-serializer

Conversation

@shashjar
Copy link
Member

@shashjar shashjar commented Feb 18, 2026

https://www.notion.so/sentry/Scheduled-Reports-on-Saved-Queries-Dashboards-3098b10e4b5d8017808bdea381cb4465

Follow-up to #108407

PR 4 for adding scheduled report delivery for Explore saved queries (CSV email attachments) and eventually Dashboards (likely PDF email attachments).

  • Adds CRUD API endpoints for scheduled reports (GET/POST /scheduled-reports/, GET/PUT/DELETE /scheduled-reports/{id}/)
  • Adds test send endpoint (POST /scheduled-reports/test/) for "Send Test Email" functionality (with rate-limiting per user)
  • Adds input & output serializers for the ScheduledReport model
  • All endpoints gated behind organizations:scheduled-reports feature flag
  • Per-org limit of 25 scheduled reports enforced on creation
  • Removes the ScheduledReport model import from sentry/models/__init__.py (no longer needed, now discovered via API endpoint imports)

@shashjar shashjar changed the base branch from master to shashjar/scheduled-reports-implement-execution-tasks February 18, 2026 00:09
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Feb 18, 2026
@github-actions github-actions bot added the Scope: Frontend Automatically applied to PRs that change frontend components label Feb 18, 2026
@github-actions
Copy link
Contributor

🚨 Warning: This pull request contains Frontend and Backend changes!

It's discouraged to make changes to Sentry's Frontend and Backend in a single pull request. The Frontend and Backend are not atomically deployed. If the changes are interdependent of each other, they must be separated into two pull requests and be made forward or backwards compatible, such that the Backend or Frontend can be safely deployed independently.

Have questions? Please ask in the #discuss-dev-infra channel.

Copy link
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

if source_type is not None:
source_type_id = ScheduledReportSourceType.get_id_for_type_name(source_type)
if source_type_id is not None:
queryset = queryset.filter(source_type=source_type_id)
Copy link
Contributor

Choose a reason for hiding this comment

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

Invalid sourceType filter silently ignored, returns all reports

Low Severity

When an invalid sourceType query parameter is provided (e.g., ?sourceType=unknown), get_id_for_type_name returns None, which causes the if source_type_id is not None check to skip the filter entirely. The endpoint silently returns all reports instead of returning a 400 error or an empty list. This is inconsistent with how invalid sourceId is handled (which correctly returns a 400 error). API consumers filtering by an unrecognized source type would unknowingly receive unfiltered results.

Fix in Cursor Fix in Web

raise serializers.ValidationError(
{
"recipientEmails": "All recipient emails must belong to members of this organization."
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Email validation incorrectly aggregates users across all emails

Low Severity

_validate_recipient_emails collects all user IDs returned by get_many_by_email into a single set and then checks that every user ID belongs to an org member. Since get_many_by_email can return multiple users for the same email, if an email is verified by both an org member and a non-member user, the validation incorrectly rejects the entire request. The check conflates "does at least one user with this email belong to the org" with "do all users with any matching email belong to the org."

Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant