feat(scheduled reports): Create ScheduledReport model with migration & feature flag#108386
feat(scheduled reports): Create ScheduledReport model with migration & feature flag#108386
ScheduledReport model with migration & feature flag#108386Conversation
…te feature flag for gating scheduled reports
|
This PR has a migration; here is the generated SQL for for --
-- Create model ScheduledReport
--
CREATE TABLE "sentry_scheduledreport" ("id" bigint NOT NULL PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY, "date_updated" timestamp with time zone NOT NULL, "date_added" timestamp with time zone NOT NULL, "created_by_id" bigint NULL, "name" varchar(255) NOT NULL, "source_type" integer NOT NULL CHECK ("source_type" >= 0), "source_id" bigint NOT NULL, "frequency" integer NOT NULL CHECK ("frequency" >= 0), "day_of_week" integer NULL CHECK ("day_of_week" >= 0), "day_of_month" integer NULL CHECK ("day_of_month" >= 0), "hour" integer NOT NULL CHECK ("hour" >= 0), "time_range" varchar(32) NULL, "recipient_emails" jsonb NOT NULL, "is_active" boolean NOT NULL, "next_run_at" timestamp with time zone NOT NULL, "organization_id" bigint NOT NULL, CONSTRAINT "sentry_scheduledreport_valid_time_range" CHECK (("time_range" IN ('1h', '1d', '24h', '7d', '14d', '30d', '90d') OR "time_range" IS NULL)));
ALTER TABLE "sentry_scheduledreport" ADD CONSTRAINT "sentry_scheduledrepo_organization_id_17c1a16b_fk_sentry_or" FOREIGN KEY ("organization_id") REFERENCES "sentry_organization" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID;
ALTER TABLE "sentry_scheduledreport" VALIDATE CONSTRAINT "sentry_scheduledrepo_organization_id_17c1a16b_fk_sentry_or";
CREATE INDEX CONCURRENTLY "sentry_scheduledreport_created_by_id_583538e5" ON "sentry_scheduledreport" ("created_by_id");
CREATE INDEX CONCURRENTLY "sentry_scheduledreport_source_id_c9a6d438" ON "sentry_scheduledreport" ("source_id");
CREATE INDEX CONCURRENTLY "sentry_scheduledreport_next_run_at_7da9d749" ON "sentry_scheduledreport" ("next_run_at");
CREATE INDEX CONCURRENTLY "sentry_scheduledreport_organization_id_17c1a16b" ON "sentry_scheduledreport" ("organization_id");
CREATE INDEX CONCURRENTLY "sentry_sche_organiz_15bc2b_idx" ON "sentry_scheduledreport" ("organization_id", "source_type", "source_id");
CREATE INDEX CONCURRENTLY "sentry_sche_is_acti_fbe604_idx" ON "sentry_scheduledreport" ("is_active", "next_run_at"); |
…ation-feature-flag
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| next_run_at = models.DateTimeField(db_index=True) | ||
|
|
||
| class Meta: | ||
| app_label = "sentry" |
There was a problem hiding this comment.
Make this part of reports so that it gets its own separate migration file
| # Polymorphic source reference: points to ExploreSavedQuery.id or Dashboard.id | ||
| source_type = BoundedPositiveIntegerField( | ||
| choices=ScheduledReportSourceType.as_choices(), | ||
| ) | ||
| source_id = BoundedBigIntegerField(db_index=True) |
There was a problem hiding this comment.
The other option here could be to include the FKs to both, make them nullable and have a constraint to allow only one. Not sure if you're adding more types over time though.
The main benefit there is that if the linked row is deleted, then this row will be removed, rather than being orphaned. Up to you to decide what's best though
| # Schedule configuration | ||
| frequency = BoundedPositiveIntegerField( | ||
| choices=ScheduledReportFrequency.as_choices(), | ||
| ) | ||
| day_of_week = BoundedPositiveIntegerField(null=True, blank=True) # 0=Monday, for weekly | ||
| day_of_month = BoundedPositiveIntegerField(null=True, blank=True) # 1-31, for monthly | ||
| hour = BoundedPositiveIntegerField() # 0-23, stored in UTC |
There was a problem hiding this comment.
You could considering looking at how cron monitors handles out schedules. I'm wondering if this makes sense as just a crontab string?
| # Delivery configuration: list of org member email strings | ||
| recipient_emails = models.JSONField(default=list) |
There was a problem hiding this comment.
I think it would be better to link to the org member rows explicitly. That way, when an org member is removed, we stop sending these reports to them automatically, rather than having to remember to clean up.
https://www.notion.so/sentry/Scheduled-Reports-on-Saved-Queries-Dashboards-3098b10e4b5d8017808bdea381cb4465
PR 1 for adding scheduled report delivery for Explore saved queries (CSV email attachments) and eventually Dashboards (likely PDF email attachments).
ScheduledReportmodel insrc/sentry/reports/models.pywith scheduling configuration (frequency, day/time, etc.), polymorphic source reference (Explore saved queries and Dashboards), recipient email addresses, and scheduling state.1031_create_scheduled_report_modelto create the above model.organizations:scheduled-reportsfeature flag to gate this feature.