0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-06 11:36:31 +08:00
discourse/app/services/upcoming_changes/action/notification_data_merger.rb
Martin Brennan 89c3787d74
FIX: Upcoming change notification data JSON limit (#40626)
When notifying admins of upcoming changes that are
available or promoted, we are storing the names of
the upcoming changes in notification data JSON when
merging with existing unread notifications.

However, notifications have a 1000 char limit for
data, so we were ending up in a situation where we
got an AR error when trying to save the notification with the
merged data that exceeded the limit.

This did not roll back other actions in
`UpcomingChanges::NotifyPromotion`, so a staff action log
was still created, but then the same process would keep
happening.

The UI only ever shows 2 upcoming change names max in
the notification, so we can limit the number of names we store in
the notification data, and also add a transaction to the
service to ensure safe rollback.

Fixes this issue:

```
Failed to notify about promotion of 'granular_anonymous_and_logged_in_groups_permissions': PG::StringDataRightTruncation: ERROR:  value too long for type character varying(1000)
```
2026-06-08 15:40:49 +10:00

40 lines
1.5 KiB
Ruby
Vendored

# frozen_string_literal: true
# Consolidates upcoming change notification data for both available and promoted
# changes. We do this so admins are not overwhelmed by many separate
# notifications for upcoming changes being available or promoted in cases like
# deployments where this is possible.
#
# Used in Jobs::Scheduled::NotifyAdminsOfAvailableUpcomingChanges and
# UpcomingChanges::NotifyPromotion, and only unread notifications are considered
# for merging.
class UpcomingChanges::Action::NotificationDataMerger < Service::ActionBase
MAX_STORED_NAMES = 5
option :existing_notification_data
option :new_change_name
def call
if existing_notification_data
existing_data = JSON.parse(existing_notification_data, symbolize_names: true)
names =
Array.wrap(existing_data[:upcoming_change_names] || [existing_data[:upcoming_change_name]])
humanized =
Array.wrap(
existing_data[:upcoming_change_humanized_names] ||
[existing_data[:upcoming_change_humanized_name]],
)
merged_names = (names.map(&:to_s) + [new_change_name.to_s]).uniq
merged_humanized = (humanized + [SiteSetting.humanized_name(new_change_name)]).uniq
else
merged_names = [new_change_name.to_s]
merged_humanized = [SiteSetting.humanized_name(new_change_name)]
end
{
upcoming_change_names: merged_names.first(MAX_STORED_NAMES),
upcoming_change_humanized_names: merged_humanized.first(MAX_STORED_NAMES),
count: merged_names.size,
}
end
end