DEV: Replace 'processed' column on notifications with new table (#11864)

This commit is contained in:
Mark VanLandingham 2021-01-27 10:29:24 -06:00 committed by GitHub
parent 60f10e9067
commit 809274fe0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 61 additions and 46 deletions

View File

@ -25,9 +25,8 @@ class DoNotDisturbController < ApplicationController
def destroy
current_user.active_do_not_disturb_timings.destroy_all
current_user.publish_do_not_disturb(ends_at: nil)
current_user.notifications.unprocessed.each do |notification|
NotificationEmailer.process_notification(notification, no_delay: true)
end
current_user.shelved_notifications.each(&:process)
current_user.shelved_notifications.destroy_all
render json: success_json
end

View File

@ -6,22 +6,17 @@ module Jobs
def execute(args)
sql = <<~SQL
SELECT n.id FROM notifications AS n
INNER JOIN do_not_disturb_timings AS dndt ON n.user_id = dndt.user_id
WHERE n.processed = false
SELECT sn.id FROM shelved_notifications as sn
INNER JOIN notifications AS notification ON sn.notification_id = notification.id
INNER JOIN do_not_disturb_timings AS dndt ON notification.user_id = dndt.user_id
AND dndt.ends_at <= :now
SQL
now = Time.zone.now
notification_ids = DB.query_single(sql, now: now)
Notification.where(id: notification_ids).each do |notification|
begin
NotificationEmailer.process_notification(notification, no_delay: true)
rescue
Rails.logger.warn("Failed to process notification with ID #{notification.id}")
end
end
shelved_notification_ids = DB.query_single(sql, now: now)
shelved_notifications = ShelvedNotification.where(id: shelved_notification_ids)
shelved_notifications.each(&:process)
shelved_notifications.destroy_all
DB.exec("DELETE FROM do_not_disturb_timings WHERE ends_at < :now", now: now)
end

View File

@ -4,13 +4,14 @@ class Notification < ActiveRecord::Base
belongs_to :user
belongs_to :topic
has_one :shelved_notification
MEMBERSHIP_REQUEST_CONSOLIDATION_WINDOW_HOURS = 24
validates_presence_of :data
validates_presence_of :notification_type
scope :unread, lambda { where(read: false) }
scope :unprocessed, lambda { where(processed: false) }
scope :recent, lambda { |n = nil| n ||= 10; order('notifications.created_at desc').limit(n) }
scope :visible , lambda { joins('LEFT JOIN topics ON notifications.topic_id = topics.id')
.where('topics.id IS NULL OR topics.deleted_at IS NULL') }
@ -283,10 +284,11 @@ class Notification < ActiveRecord::Base
end
def send_email
if skip_send_email
return update(processed: true)
end
NotificationEmailer.process_notification(self) unless user.do_not_disturb?
return if skip_send_email
user.do_not_disturb? ?
ShelvedNotification.create(notification_id: self.id) :
NotificationEmailer.process_notification(self)
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class ShelvedNotification < ActiveRecord::Base
belongs_to :notification
def process
NotificationEmailer.process_notification(notification, no_delay: true)
end
end

View File

@ -1391,6 +1391,10 @@ class User < ActiveRecord::Base
active_do_not_disturb_timings.maximum(:ends_at)
end
def shelved_notifications
ShelvedNotification.joins(:notification).where("notifications.user_id = ?", self.id)
end
protected
def badge_grant

View File

@ -125,7 +125,6 @@ class NotificationEmailer
end
def self.process_notification(notification, no_delay: false)
notification.update(processed: true)
return if @disabled
email_user = EmailUser.new(notification, no_delay: no_delay)

View File

@ -1,14 +1,6 @@
# frozen_string_literal: true
class AddProcessedToNotifications < ActiveRecord::Migration[6.0]
def up
add_column :notifications, :processed, :boolean, default: false
execute "UPDATE notifications SET processed = true"
change_column_null(:notifications, :processed, false)
add_index :notifications, [:processed], unique: false
end
def down
remove_column :notifications, :processed
def change
end
end

View File

@ -0,0 +1,9 @@
# frozen_string_literal: true
class CreateShelvedNotifications < ActiveRecord::Migration[6.0]
def change
create_table :shelved_notifications do |t|
t.integer :notification_id, null: false
end
add_index :shelved_notifications, [:notification_id]
end
end

View File

@ -0,0 +1,6 @@
# frozen_string_literal: true
class UndoAddProcessedToNotifications < ActiveRecord::Migration[6.0]
def up
execute "ALTER TABLE notifications DROP COLUMN IF EXISTS processed"
end
end

View File

@ -17,21 +17,21 @@ describe Jobs::ProcessShelvedNotifications do
expect(DoNotDisturbTiming.find_by(id: past.id)).to eq(nil)
end
it "does not process unprocessed notifications when the user is in DND" do
it "does not process shelved_notifications when the user is in DND" do
user.do_not_disturb_timings.create(starts_at: 2.days.ago, ends_at: 2.days.from_now)
notification = Notification.create(read: false, user_id: user.id, topic_id: 2, post_number: 1, data: '{}', notification_type: 1)
expect(notification.reload.processed).to eq(false)
expect(notification.shelved_notification).to be_present
subject.execute({})
expect(notification.reload.processed).to eq(false)
expect(notification.shelved_notification).to be_present
end
it "processes unprocessed notifications when the user leaves DND" do
it "processes and destroys shelved_notifications when the user leaves DND" do
user.do_not_disturb_timings.create(starts_at: 2.days.ago, ends_at: 2.days.from_now)
notification = Notification.create(read: false, user_id: user.id, topic_id: 2, post_number: 1, data: '{}', notification_type: 1)
user.do_not_disturb_timings.last.update(ends_at: 1.days.ago)
expect(notification.reload.processed).to eq(false)
expect(notification.shelved_notification).to be_present
subject.execute({})
expect(notification.reload.processed).to eq(true)
expect { notification.shelved_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end

View File

@ -495,7 +495,6 @@ describe Notification do
expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
notification = Notification.last
expect(notification.processed).to eq(true)
expect(notification.notification_type).to eq(Notification.types[:membership_request_consolidated])
data = notification.data_hash
@ -516,7 +515,7 @@ describe Notification do
notification = Notification.last
expect(notification.notification_type).to eq(Notification.types[:membership_request_consolidated])
expect(notification.processed).to eq(false)
expect(notification.shelved_notification).to be_present
end
end
end
@ -543,18 +542,18 @@ describe Notification do
end
end
describe "processed" do
describe "do not disturb" do
fab!(:user) { Fabricate(:user) }
it "is false after creation when the user is in do not disturb" do
it "creates a shelved_notification record when created while user is in DND" do
user.do_not_disturb_timings.create(starts_at: Time.now, ends_at: 3.days.from_now)
notification = Notification.create(read: false, user_id: user.id, topic_id: 2, post_number: 1, data: '{}', notification_type: 1)
expect(notification.processed).to be(false)
expect(notification.shelved_notification).to be_present
end
it "is true after creation when the user isn't in do not disturb" do
it "doesn't create a shelved_notification record when created while user is isn't DND" do
notification = Notification.create(read: false, user_id: user.id, topic_id: 2, post_number: 1, data: '{}', notification_type: 1)
expect(notification.processed).to be(true)
expect(notification.shelved_notification).to be_nil
end
end
end

View File

@ -44,13 +44,14 @@ describe DoNotDisturbController do
end
describe "#destroy" do
it "process notifications that came in during DND" do
it "process shelved notifications that came in during DND" do
user.do_not_disturb_timings.create(starts_at: 2.days.ago, ends_at: 2.days.from_now)
notification = Notification.create(read: false, user_id: user.id, topic_id: 2, post_number: 1, data: '{}', notification_type: 1)
expect(notification.processed).to eq(false)
expect(notification.shelved_notification).to be_present
delete "/do-not-disturb.json"
expect(notification.reload.processed).to eq(true)
expect { notification.shelved_notification.reload }.to raise_error(ActiveRecord::RecordNotFound)
expect(user.do_not_disturb?).to eq(false)
end
end
end