Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batch Email Notifications #2302

Merged
merged 9 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/jobs/batch_email_notification_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def perform
end
end

user.update(last_emailed_at: Time.current)
user.last_emailed_at = Time.current
end
BatchEmailNotificationJob.set(wait_until: Date.tomorrow.midnight).perform_later
end
Expand Down
2 changes: 2 additions & 0 deletions app/models/sushi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,9 @@ def coerce_dates(params = {})
raise Sushi::Error::UsageNotReadyForRequestedDatesError.new(data: "Unable to complete the request because the end_date of #{params[:end_date]} is for a month that has incomplete data. That month's data ends on #{latest_date.iso8601}.")
# rubocop:enable Metrics/MethodLength
end
# rubocop:disable Lint/ShadowedException
rescue ActionController::ParameterMissing, KeyError => e
# rubocop:enable Lint/ShadowedException
raise Sushi::Error::InsufficientInformationToProcessRequestError.new(data: e.message)
end
end
Expand Down
10 changes: 10 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

# rubocop:disable Metrics/ClassLength
class User < ApplicationRecord
has_one :user_batch_email, dependent: :destroy

# Includes lib/rolify from the rolify gem
rolify
# Connects this user object to Hydra behaviors.
Expand Down Expand Up @@ -189,5 +191,13 @@ def statistics_for(start_date: (Time.zone.now - 1.month).beginning_of_month, end
total_work_views:
}
end

def last_emailed_at
UserBatchEmail.find_or_create_by(user: self).last_emailed_at
end

def last_emailed_at=(value)
UserBatchEmail.find_or_create_by(user: self).update(last_emailed_at: value)
end
end
# rubocop:enable Metrics/ClassLength
6 changes: 6 additions & 0 deletions app/models/user_batch_email.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal:true

class UserBatchEmail < ApplicationRecord
self.table_name = 'user_batch_emails'
belongs_to :user, class_name: '::User'
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class RemoveLastEmailedAtFromUsers < ActiveRecord::Migration[6.1]
def change
remove_column :users, :last_emailed_at, :datetime if column_exists?(:users, :last_emailed_at)
end
end
10 changes: 10 additions & 0 deletions db/migrate/20240820200440_create_user_batch_emails.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class CreateUserBatchEmails < ActiveRecord::Migration[6.1]
def change
create_table :user_batch_emails do |t|
t.references :user, unique: true, index: true
t.datetime :last_emailed_at

t.timestamps
end
end
end
11 changes: 9 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_08_06_161142) do
ActiveRecord::Schema.define(version: 2024_08_20_200440) do

# These are extensions that must be enabled in order to support this database
enable_extension "hstore"
Expand Down Expand Up @@ -856,6 +856,14 @@
t.index ["user_id"], name: "index_uploaded_files_on_user_id"
end

create_table "user_batch_emails", force: :cascade do |t|
t.bigint "user_id"
t.datetime "last_emailed_at"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["user_id"], name: "index_user_batch_emails_on_user_id"
end

create_table "user_stats", id: :serial, force: :cascade do |t|
t.integer "user_id"
t.datetime "date"
Expand Down Expand Up @@ -917,7 +925,6 @@
t.string "provider"
t.string "uid"
t.string "batch_email_frequency", default: "never"
t.datetime "last_emailed_at"
t.string "api_key"
t.index ["api_key"], name: "index_users_on_api_key"
t.index ["email"], name: "index_users_on_email", unique: true
Expand Down
43 changes: 26 additions & 17 deletions spec/jobs/batch_email_notification_job_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

RSpec.describe BatchEmailNotificationJob do
let(:subject) { BatchEmailNotificationJob.perform_now }
let(:account) { create(:account_with_public_schema) }
let(:receipt) { FactoryBot.create(:mailboxer_receipt, receiver: user) }
let!(:message) { receipt.notification }
Expand All @@ -17,62 +18,70 @@
end

describe '#perform' do
let(:frequency) { 'daily' }

it 'marks the message as delivered' do
expect { BatchEmailNotificationJob.perform_now }.to change { message.receipts.first.is_delivered }.from(false).to(true)
before do
UserBatchEmail.find_or_create_by(user: user).update(last_emailed_at: last_emailed)
end

it 're-enqueues the job' do
expect { BatchEmailNotificationJob.perform_now }.to have_enqueued_job(BatchEmailNotificationJob)
context 'basic job behavior' do
let(:frequency) { 'daily' }
let(:last_emailed) { nil }

it 'marks the message as delivered' do
expect { subject }.to change { message.receipts.first.is_delivered }.from(false).to(true)
end

it 're-enqueues the job' do
expect { subject }.to have_enqueued_job(BatchEmailNotificationJob)
end
end

context 'when the user has a daily frequency' do
let(:frequency) { 'daily' }
let(:last_emailed) { 1.day.ago }

it 'sends email to users with batch_email_frequency set' do
expect { BatchEmailNotificationJob.perform_now }.to change { ActionMailer::Base.deliveries.count }.by(1)
expect { subject }.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end

context 'when the user has a weekly frequency' do
let(:frequency) { 'weekly' }
let(:user) { FactoryBot.create(:user, batch_email_frequency: frequency, last_emailed_at:) }
let(:user) { FactoryBot.create(:user, batch_email_frequency: frequency) }

context 'when the user was last emailed less than a week ago' do
let(:last_emailed_at) { 5.days.ago }
let(:last_emailed) { 5.days.ago }

it 'does not send an email to users with batch_email_frequency set' do
expect { BatchEmailNotificationJob.perform_now }.to_not change { ActionMailer::Base.deliveries.count }
expect { subject }.to_not change { ActionMailer::Base.deliveries.count }
end
end

context 'when the user was last emailed more than a week ago' do
let(:last_emailed_at) { 8.days.ago }
let(:last_emailed) { 8.days.ago }

it 'sends email to users with batch_email_frequency set' do
expect { BatchEmailNotificationJob.perform_now }.to change { ActionMailer::Base.deliveries.count }.by(1)
expect { subject }.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end
end

context 'when the user has a monthly frequency' do
let(:frequency) { 'monthly' }
let(:user) { FactoryBot.create(:user, batch_email_frequency: frequency, last_emailed_at:) }
let(:user) { FactoryBot.create(:user, batch_email_frequency: frequency) }

context 'when the user was last emailed less than a month ago' do
let(:last_emailed_at) { 20.days.ago }
let(:last_emailed) { 20.days.ago }

it 'does not send an email to users with batch_email_frequency set' do
expect { BatchEmailNotificationJob.perform_now(account) }.to_not change { ActionMailer::Base.deliveries.count }
expect { subject }.to_not change { ActionMailer::Base.deliveries.count }
end
end

context 'when the user was last emailed more than a month ago' do
let(:last_emailed_at) { 40.days.ago }
let(:last_emailed) { 40.days.ago }

it 'sends email to users with batch_email_frequency set' do
expect { BatchEmailNotificationJob.perform_now }.to change { ActionMailer::Base.deliveries.count }.by(1)
expect { subject }.to change { ActionMailer::Base.deliveries.count }.by(1)
end
end
end
Expand Down
Loading