Skip to content

feat(broadcasts): Add sync_locked to protect admin edits from changelog sync#113633

Merged
rahulchhabria merged 5 commits intomasterfrom
feat/broadcast-sync-locked
Apr 22, 2026
Merged

feat(broadcasts): Add sync_locked to protect admin edits from changelog sync#113633
rahulchhabria merged 5 commits intomasterfrom
feat/broadcast-sync-locked

Conversation

@rahulchhabria
Copy link
Copy Markdown
Contributor

Adds a sync_locked BooleanField to the Broadcast model so admins can edit changelog-sourced broadcasts in /_admin/broadcasts/:id/ without the next run of GetSentry's hourly changelog-sync job overwriting their changes.

Why: Today, the changelog sync task does Broadcast.objects.update_or_create(upstream_id=..., defaults=...) every hour, which clobbers any title/message/link/category/is_active/date_expires edits made by an admin within ~59 minutes. Typo fixes and deactivations don't stick.

How it works:

  • New sync_locked field on Broadcast (default False, with db_default=False for zero-downtime deploy).
  • The admin PUT handler at BroadcastDetailsEndpoint flips sync_locked=True whenever any sync-managed field (title, message, link, media_url, category, is_active, date_expires) is edited on a broadcast that has an upstream_id set. Admins can clear the flag explicitly by passing syncLocked: false.
  • Targeted-field edits (roles, plans, region, etc., handled by GetSentry's TargetedBroadcast endpoint override) do not trigger the lock — those fields are already safe from the sync.
  • AdminBroadcastSerializer now exposes upstreamId and syncLocked so the UI can render banners/badges.

Admin UI revamp at static/gsAdmin/views/broadcastDetails.tsx:

  • Defaults to a read-only overview (previous default was a mix of read-only labels with an isolated Activate/Deactivate action).
  • New "Edit Broadcast" action toggles into a full editable form (title, message, link, mediaUrl, category, dateExpires, isActive + targeted fields). skipConfirmModal: true so it doesn't route through the admin confirmation dialog.
  • Changelog-sourced broadcasts show a "From Changelog" / "Sync Locked" badge and an info/warning banner while editing.
  • A "Re-enable changelog sync" action appears on locked broadcasts, which PUTs syncLocked: false to hand control back to the hourly sync.
  • Empty optional fields are stripped from the payload before submit so the DRF URLField validator doesn't reject blank mediaUrl.

Companion PR: getsentry/getsentry updates _process_entry in getsentry/tasks/broadcasts.py to skip the update branch when sync_locked=True. This sentry PR is backwards compatible — the default False value means existing behavior is unchanged until the getsentry side also merges.

Testing: Five new cases in tests/sentry/api/endpoints/test_broadcast_details.py cover the happy path, the non-changelog case, hasSeen-only updates (shouldn't lock), explicit unlock via syncLocked: false, and serializer exposure of the new fields.

@github-actions github-actions Bot added Scope: Frontend Automatically applied to PRs that change frontend components Scope: Backend Automatically applied to PRs that change backend components labels Apr 22, 2026
@github-actions
Copy link
Copy Markdown
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.

@rahulchhabria rahulchhabria marked this pull request as ready for review April 22, 2026 03:57
@rahulchhabria rahulchhabria requested review from a team as code owners April 22, 2026 03:57
Comment thread static/gsAdmin/views/broadcastDetails.tsx
…og sync

Broadcasts created by GetSentry's hourly changelog-sync job are rewritten
on every run, which overwrites typo fixes and targeting tweaks made by
admins. Introduce a `sync_locked` flag on the Broadcast model that the
admin PUT endpoint flips automatically whenever a sync-managed field
(title, message, link, media_url, category, is_active, date_expires) is
edited on a changelog-sourced broadcast. The corresponding GetSentry
sync task change teaches the job to skip updates for locked broadcasts
while still creating new ones.

Also updates the admin UI at /_admin/broadcasts/:id/ to default to a
read-only view with an explicit "Edit Broadcast" action, surface the
changelog origin and lock state as badges, and expose a
"Re-enable changelog sync" action for clearing the lock.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown
Contributor

This PR has a migration; here is the generated SQL for src/sentry/migrations/1071_add_broadcast_sync_locked.py

for 1071_add_broadcast_sync_locked in sentry

--
-- Add field sync_locked to broadcast
--
ALTER TABLE "sentry_broadcast" ADD COLUMN "sync_locked" boolean DEFAULT false NOT NULL;

The admin broadcast edit form stripped every null from the payload to
work around URLField validation on an empty mediaUrl. dateExpires uses
null to mean "no expiration" and the backend validator declares it
allow_null=True, with a sentinel check to distinguish "omitted" from
"explicitly null". Stripping it silently reverted the field on save.

Keep stripping '' and undefined, but only strip null for fields the
backend does not accept null on.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@markstory markstory left a comment

Choose a reason for hiding this comment

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

Looks good. I tested the UI out locally and it behaves well.

help: fromChangelog
? 'Edit broadcast content. Saving will lock this broadcast from future changelog syncs.'
: 'Edit broadcast content.',
visible: true,
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.

Couldn't this be visible: isAdmin && !isEditing similar to the toggle action?

Replace conditional actions.push() calls with a single array literal
where each action controls its own visibility via the `visible` prop.
Matches the pattern already used by the toggle-activation action.

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
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 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit ca6a82f. Configure here.

Comment thread static/gsAdmin/views/broadcastDetails.tsx
The activate/deactivate dropdown action remained visible during edit
mode, allowing an out-of-band PUT that would be reverted by the edit
form's isActive field on save. Match the gating used by the other
admin actions.

Co-Authored-By: Claude <noreply@anthropic.com>
@rahulchhabria rahulchhabria merged commit e14d295 into master Apr 22, 2026
82 checks passed
@rahulchhabria rahulchhabria deleted the feat/broadcast-sync-locked branch April 22, 2026 21:26
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.

2 participants