2
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-03-05 15:27:34 +08:00
discourse/app/services/upcoming_changes/action/track_status_changes.rb
Martin Brennan 0070b55cdf
UX: Consolidate upcoming change notifications for admins (#37976)
We notify admins both when an upcoming change is available
for preview (at promotion status - 1) and when an upcoming
change is automatically promoted.

There are cases where multiple changes might be deployed at the same
time, and we want to avoid sending multiple notifications to admins in a
short period of time.

This PR consolidates the notifications for both events into a single
notification that includes all relevant changes. So for new upcoming
changes, admins will receive a single notification that lists all the
changes that are now available for preview. For automatic promotions,
admins will receive a single notification that lists all the changes
that were automatically promoted.

In addition, this PR follows up on
https://github.com/discourse/discourse/pull/38051 and
directly links the notifications to a filtered list of upcoming changes
by
setting name.

<img width="338" alt="image"
src="https://github.com/user-attachments/assets/6febc7d7-0122-4f3b-8cbe-04d03721a709"
/>

<img width="332" alt="image"
src="https://github.com/user-attachments/assets/46f2f886-8a6f-46f5-8038-bce3305d788f"
/>



### Testing

* Start with a fresh DB
* Make sure `upcoming_change_verbose_logging` and
`enable_upcoming_changes` are true
* Choose a couple of upcoming changes and update their statuses in
`site_settings.yml` to `beta`
* Log in to the site and go to http://localhost:4200/sidekiq/scheduler
and find the CheckUpcomingChanges job and run it
* You should receive a notification for the changes becoming available,
do not read the notification
* Change another upcoming change to beta and run the job again
* You should not get another notification, the third change should be
merged into the first notification
* Now update some of the same changes to `stable` status
* Run the job again
* You should get a new notification saying the changes were
automatically enabled
* Now update another of the same changes to `stable` status
* You should not get another notification, the third change should be
merged into the notification for enabled changes

---------

Co-authored-by: awesomerobot <kris.aubuchon@discourse.org>
2026-03-02 10:48:27 +10:00

117 lines
4 KiB
Ruby

# frozen_string_literal: true
# Intended to be called from UpcomingChanges::Track service,
# not standalone.
#
# Lookup any previous event_type: status_changed (5) events for the change
# * If there are none, create one for the current status
# * Send an appropriate notification to admins
# * If the change was also added at the same time, and the status is correct (promotion_status - 1),
# then don't send another notification
# * If the change was not added, send a notification about the status change if it's the correct
# status (promotion_status - 1) to indicate it's available to admins
class UpcomingChanges::Action::TrackStatusChanges < Service::ActionBase
# Every admin user that are not bots
option :all_admins
# All changes that were added at the same time, we already added events
# and notified admins for them.
option :added_changes
# All changes that were removed at the same time, we don't care about
# their statuses anymore.
option :removed_changes
def call
status_changes = {}
notified_changes = []
SiteSetting.upcoming_change_site_settings.each do |change_name|
if no_previous_status_event?(change_name)
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: change_name,
event_data: {
previous_value: nil,
new_value: UpcomingChanges.change_status(change_name),
},
)
status_changes[change_name] = {
previous_value: "N/A",
new_value: UpcomingChanges.change_status(change_name),
}
next
end
next if added_changes.include?(change_name)
next if removed_changes.include?(change_name)
previous_status = previous_status_for(change_name)
current_status = UpcomingChanges.change_status(change_name)
if status_changed?(previous_status, current_status)
UpcomingChangeEvent.create!(
event_type: :status_changed,
upcoming_change_name: change_name,
event_data: {
previous_value: previous_status,
new_value: current_status,
},
)
status_changes[change_name] = { previous_value: previous_status, new_value: current_status }
# If admins were already notified about this change, don't notify them again.
# This can happen if the change was added and it already met the promotion status
# minus one (previous status) criteria for notification.
#
# However, if the status was later changed and it meets the promotion status
# minus one (previous status) criteria for notification, then we should notify
# admins here.
if should_notify_admins?(change_name) &&
!UpcomingChanges.meets_or_exceeds_status?(
change_name,
SiteSetting.promote_upcoming_changes_on_status.to_sym,
)
Rails.logger.info(
"Notifying admins about available change #{change_name} from TrackStatusChanges",
)
notified =
UpcomingChanges::Action::NotifyAdminsOfAvailableChange.call(change_name:, all_admins:)
notified_changes << change_name if notified
end
end
end
{ status_changes:, notified_changes: }
end
private
def previous_status_events
@previous_status_events ||= UpcomingChangeEvent.status_changed.to_a
end
def no_previous_status_event?(change_name)
previous_status_events.none? { |event| event.upcoming_change_name == change_name.to_s }
end
def previous_status_for(change_name)
previous_status_events
.select { |event| event.upcoming_change_name == change_name.to_s }
.last
.event_data[
"new_value"
]
end
def status_changed?(previous_status, current_status)
previous_status&.to_sym != current_status
end
def should_notify_admins?(change_name)
!UpcomingChangeEvent.exists?(
upcoming_change_name: change_name,
event_type: :admins_notified_available_change,
)
end
end