From e3276b5bc2f8ae82a299db73b302f7fda37f46d4 Mon Sep 17 00:00:00 2001 From: David Zuckerman Date: Fri, 6 Feb 2026 16:35:38 -0800 Subject: [PATCH 1/3] Calling deliver_later for forms instead of deliver_now added struct file for affiliate borrow request moved struct into model for borrow_request removed borrow_request.rb struct file Changing doemoff_patron_email_form to use struct similar to affiliate_borrower Changing stack pass for to use deliver_later Changed deliver_now to deliver_later for more forms --- app/mailers/request_mailer.rb | 25 ++-- app/models/affiliate_borrow_request_form.rb | 40 +++-- app/models/doemoff_patron_email_form.rb | 26 ++-- app/models/efees_invoice.rb | 2 +- app/models/proxy_borrower_requests.rb | 4 +- app/models/reference_card_form.rb | 6 +- app/models/security_incident_report_form.rb | 140 +++++++++--------- app/models/stack_pass_form.rb | 6 +- .../affiliate_borrow_request_form_spec.rb | 11 +- spec/models/doemoff_patron_email_form_spec.rb | 12 +- spec/models/efees_invoice_spec.rb | 88 ++++++++--- .../security_incident_report_form_spec.rb | 16 +- 12 files changed, 241 insertions(+), 135 deletions(-) diff --git a/app/mailers/request_mailer.rb b/app/mailers/request_mailer.rb index 31d8c3d8..1fc3e192 100644 --- a/app/mailers/request_mailer.rb +++ b/app/mailers/request_mailer.rb @@ -7,10 +7,10 @@ class RequestMailer < ApplicationMailer helper :mail # Sends the AffiliateBorrowRequestForm - def affiliate_borrow_request_form_email(borrow_request) - @borrow_request = borrow_request + def affiliate_borrow_request_form_email(borrow_request_hash) + @borrow_request = AffiliateBorrowRequestForm::BorrowRequest.new(**borrow_request_hash) - mail(to: borrow_request.department_head_email) + mail(to: @borrow_request.department_head_email) end # Send LibstaffEdevicesLoanRequest confirmation email to user @@ -40,16 +40,21 @@ def doemoff_room_failure_email(empid, displayname, note) mail(to: admin_to) end - # Sends doemoff patron email - def doemoff_patron_email(email_form) - @email_form = email_form - mail(to: @email_form.patron_email, bcc: @email_form.recipient_email, reply_to: @email_form.recipient_email, - subject: 'Message from Doe/Moffitt Libraries Staff') + def doemoff_patron_email(payload) + @email_form = DoemoffPatronEmailForm::PatronEmail.new(**payload) + + mail( + to: @email_form.patron_email, + bcc: @email_form.recipient_email, + reply_to: @email_form.recipient_email, + subject: 'Message from Doe/Moffitt Libraries Staff' + ) end # Sends the Security Incident form email - def security_incident_email(security_incident) - @security_incident = security_incident + def security_incident_email(payload) + @security_incident = SecurityIncidentReportForm::SecurityIncident.new(**payload) + if @security_incident.sup_email.nil? mail(to: security_to, subject: 'Incident Report Form email') else diff --git a/app/models/affiliate_borrow_request_form.rb b/app/models/affiliate_borrow_request_form.rb index 331525ff..fb6a68ee 100644 --- a/app/models/affiliate_borrow_request_form.rb +++ b/app/models/affiliate_borrow_request_form.rb @@ -1,16 +1,21 @@ +# app/models/affiliate_borrow_request_form.rb class AffiliateBorrowRequestForm < Form - attr_accessor( - :department_head_email, - :department_head_name, - :department_name, - :employee_email, - :employee_id, - :employee_name, - :employee_personal_email, - :employee_phone, - :employee_preferred_name, - :employee_address - ) + ATTRIBUTES = %i[ + department_head_email + department_head_name + department_name + employee_email + employee_id + employee_name + employee_personal_email + employee_phone + employee_preferred_name + employee_address + ].freeze + + BorrowRequest = Struct.new(*ATTRIBUTES, keyword_init: true) + + attr_accessor(*ATTRIBUTES) validates :department_name, presence: true @@ -39,9 +44,14 @@ class AffiliateBorrowRequestForm < Form validates :employee_address, presence: true - private + # Return a serializable hash for deliver_later + def to_h + ATTRIBUTES.index_with { |attr| public_send(attr) } + end + + def submit! + raise ActiveModel::ValidationError, self unless valid? - def submit - RequestMailer.affiliate_borrow_request_form_email(self).deliver_now + RequestMailer.affiliate_borrow_request_form_email(to_h).deliver_later end end diff --git a/app/models/doemoff_patron_email_form.rb b/app/models/doemoff_patron_email_form.rb index c93f6d17..7924b361 100644 --- a/app/models/doemoff_patron_email_form.rb +++ b/app/models/doemoff_patron_email_form.rb @@ -1,10 +1,14 @@ class DoemoffPatronEmailForm < Form - attr_accessor( - :patron_email, - :patron_message, - :sender, - :recipient_email - ) + ATTRIBUTES = %i[ + patron_email + patron_message + sender + recipient_email + ].freeze + + PatronEmail = Struct.new(*ATTRIBUTES, keyword_init: true) + + attr_accessor(*ATTRIBUTES) validates :patron_email, email: true, @@ -12,9 +16,13 @@ class DoemoffPatronEmailForm < Form validates :patron_message, :sender, :recipient_email, presence: true - private + def to_h + ATTRIBUTES.index_with { |attr| public_send(attr) } + end + + def submit! + raise ActiveModel::ValidationError, self unless valid? - def submit - RequestMailer.doemoff_patron_email(self).deliver_now + RequestMailer.doemoff_patron_email(to_h).deliver_later end end diff --git a/app/models/efees_invoice.rb b/app/models/efees_invoice.rb index bfdda793..e5f9e10d 100644 --- a/app/models/efees_invoice.rb +++ b/app/models/efees_invoice.rb @@ -41,7 +41,7 @@ def encode def submit! # Send the email with the link to the user! - RequestMailer.efee_invoice_email(self).deliver_now + RequestMailer.efee_invoice_email(alma_id).deliver_later end private diff --git a/app/models/proxy_borrower_requests.rb b/app/models/proxy_borrower_requests.rb index dc70b233..9d98b5d6 100644 --- a/app/models/proxy_borrower_requests.rb +++ b/app/models/proxy_borrower_requests.rb @@ -27,8 +27,8 @@ class ProxyBorrowerRequests < ActiveRecord::Base validate :date_limit def submit! - RequestMailer.proxy_borrower_request_email(self).deliver_now - RequestMailer.proxy_borrower_alert_email(self).deliver_now + RequestMailer.proxy_borrower_request_email(self).deliver_later + RequestMailer.proxy_borrower_alert_email(self).deliver_later end def full_name diff --git a/app/models/reference_card_form.rb b/app/models/reference_card_form.rb index 3fd9999d..a7b6c802 100644 --- a/app/models/reference_card_form.rb +++ b/app/models/reference_card_form.rb @@ -4,15 +4,15 @@ class ReferenceCardForm < StackRequest MAX_LENGTH = 91 def submit! - RequestMailer.reference_card_email(self).deliver_now + RequestMailer.reference_card_email(self).deliver_later end def approve! - RequestMailer.reference_card_approved(self).deliver_now + RequestMailer.reference_card_approved(self).deliver_later end def deny! - RequestMailer.reference_card_denied(self).deliver_now + RequestMailer.reference_card_denied(self).deliver_later end # Add up and return the total number of days this requester diff --git a/app/models/security_incident_report_form.rb b/app/models/security_incident_report_form.rb index 5207d379..c8ba5daa 100644 --- a/app/models/security_incident_report_form.rb +++ b/app/models/security_incident_report_form.rb @@ -1,67 +1,71 @@ class SecurityIncidentReportForm < Form - attr_accessor( - :incident_location, - :incident_date, - :incident_time, - :reporter_name, - :email, - :unit, - :phone, - :incident_description, - :theft_personal, - :theft_library, - :vandalism, - :assault, - :criminal_other, - :rules_violation, - :irate_abusive, - :physical_altercation, - :disruptive_other, - :power_outage, - :flooding, - :elevator, - :fire, - :facility_other, - :library_employee, - :student_patron, - :campus_employee, - :visitor, - :injury_other, - :injury_description, - :subject_affiliation, - :race, - :sex, - :build, - :height, - :weight, - :hair, - :eyes, - :clothing, - :subject_affiliation_1, - :race_1, - :sex_1, - :build_1, - :height_1, - :weight_1, - :hair_1, - :eyes_1, - :clothing_1, - :subject_affiliation_2, - :race_2, - :sex_2, - :build_2, - :height_2, - :weight_2, - :hair_2, - :eyes_2, - :clothing_2, - :property_description, - :police_report_number, - :officer_name_badge, - :fire_department, - :sup_email, - :police_notified - ) + ATTRIBUTES = %i[ + incident_location + incident_date + incident_time + reporter_name + email + unit + phone + incident_description + theft_personal + theft_library + vandalism + assault + criminal_other + rules_violation + irate_abusive + physical_altercation + disruptive_other + power_outage + flooding + elevator + fire + facility_other + library_employee + student_patron + campus_employee + visitor + injury_other + injury_description + subject_affiliation + race + sex + build + height + weight + hair + eyes + clothing + subject_affiliation_1 + race_1 + sex_1 + build_1 + height_1 + weight_1 + hair_1 + eyes_1 + clothing_1 + subject_affiliation_2 + race_2 + sex_2 + build_2 + height_2 + weight_2 + hair_2 + eyes_2 + clothing_2 + property_description + police_report_number + officer_name_badge + fire_department + sup_email + police_notified + ].freeze + + SecurityIncident = Struct.new(*ATTRIBUTES, keyword_init: true) + + attr_accessor(*ATTRIBUTES) validates :email, :sup_email, email: true, @@ -70,9 +74,13 @@ class SecurityIncidentReportForm < Form validates :incident_location, :incident_date, :incident_time, :reporter_name, :unit, :phone, :incident_description, presence: true - private + def to_h + ATTRIBUTES.index_with { |attr| public_send(attr) } + end + + def submit! + raise ActiveModel::ValidationError, self unless valid? - def submit - RequestMailer.security_incident_email(self).deliver_now + RequestMailer.security_incident_email(to_h).deliver_later end end diff --git a/app/models/stack_pass_form.rb b/app/models/stack_pass_form.rb index ad9f67e7..413ef66e 100644 --- a/app/models/stack_pass_form.rb +++ b/app/models/stack_pass_form.rb @@ -3,15 +3,15 @@ class StackPassForm < StackRequest validates :main_stack, acceptance: { message: 'Item must be located in the Main (Gardner) stacks.' } def submit! - RequestMailer.stack_pass_email(self).deliver_now + RequestMailer.stack_pass_email(self).deliver_later end def approve! - RequestMailer.stack_pass_approved(self).deliver_now + RequestMailer.stack_pass_approved(self).deliver_later end def deny! - RequestMailer.stack_pass_denied(self).deliver_now + RequestMailer.stack_pass_denied(self).deliver_later end def approval_count diff --git a/spec/models/affiliate_borrow_request_form_spec.rb b/spec/models/affiliate_borrow_request_form_spec.rb index 7031bea7..4c0d8c47 100644 --- a/spec/models/affiliate_borrow_request_form_spec.rb +++ b/spec/models/affiliate_borrow_request_form_spec.rb @@ -1,6 +1,11 @@ require 'rails_helper' +ActiveJob::Base.queue_adapter = :test + describe AffiliateBorrowRequestForm do + include ActiveJob::TestHelper + include ActionMailer::TestHelper + it 'validates the form' do tests = [ { @@ -90,10 +95,8 @@ employee_address: '123 North St, Berkeley, CA 94707' ) - expect { form.submit! }.to(change { ActionMailer::Base.deliveries.count }.by(1)) - last_email = ActionMailer::Base.deliveries.last - expect(last_email.subject).to eq('UC Berkeley Borrowing Card Requested') - expect(last_email.to).to include('jeff@wilco.com') + expect { form.submit! }.to have_enqueued_job(ActionMailer::MailDeliveryJob) + end describe :model_name do diff --git a/spec/models/doemoff_patron_email_form_spec.rb b/spec/models/doemoff_patron_email_form_spec.rb index f865532d..f1cc73b2 100644 --- a/spec/models/doemoff_patron_email_form_spec.rb +++ b/spec/models/doemoff_patron_email_form_spec.rb @@ -4,7 +4,7 @@ attr_reader :form - # verify params are valid + # verify params are valid and it sends an email describe 'is valid' do describe 'form' do before do @@ -20,8 +20,16 @@ end it 'sends an email' do - expect { @form.submit! }.to change { ActionMailer::Base.deliveries.count }.by(1) + expect do + @form.submit! + end.to have_enqueued_job(ActionMailer::MailDeliveryJob).with( + 'RequestMailer', + 'doemoff_patron_email', + 'deliver_now', + any_args + ) end + end end diff --git a/spec/models/efees_invoice_spec.rb b/spec/models/efees_invoice_spec.rb index eaf5e00b..8724a6cf 100644 --- a/spec/models/efees_invoice_spec.rb +++ b/spec/models/efees_invoice_spec.rb @@ -1,32 +1,84 @@ require 'rails_helper' describe EfeesInvoice do + include ActiveJob::TestHelper + let(:alma_api_key) { 'fake-api-key' } - let(:request_headers) { { 'Accept' => 'application/json', 'Authorization' => "apikey #{alma_api_key}" } } + let(:alma_id) { '10335026' } + let(:request_headers) do + { + 'Accept' => 'application/json', + 'Authorization' => "apikey #{alma_api_key}" + } + end - before do - alma_id = '10335026' + context 'JWT generation' do + before do + allow(Rails.application.config) + .to receive(:alma_api_key) + .and_return(alma_api_key) - allow(Rails.application.config).to receive(:alma_api_key).and_return(alma_api_key) + stub_request( + :get, + "https://api-na.hosted.exlibrisgroup.com/almaws/v1/users/#{alma_id}?expand=fees&view=full" + ) + .with(headers: request_headers) + .to_return( + status: 200, + body: File.new('spec/data/fees/efee-lookup-data.json') + ) - stub_request(:get, "https://api-na.hosted.exlibrisgroup.com/almaws/v1/users/#{alma_id}?expand=fees&view=full") - .with(headers: request_headers) - .to_return(status: 200, body: File.new('spec/data/fees/efee-lookup-data.json')) + stub_request( + :get, + "https://api-na.hosted.exlibrisgroup.com/almaws/v1/users/#{alma_id}/fees" + ) + .with(headers: request_headers) + .to_return( + status: 200, + body: File.new('spec/data/fees/efee-lookup-fees.json') + ) + end - stub_request(:get, "https://api-na.hosted.exlibrisgroup.com/almaws/v1/users/#{alma_id}/fees") - .with(headers: request_headers) - .to_return(status: 200, body: File.new('spec/data/fees/efee-lookup-fees.json')) + it 'encodes the user info into a jwt' do + invoice = EfeesInvoice.new(alma_id) + decoded_token = EfeesInvoice.decode(invoice.jwt) - @invoice = EfeesInvoice.new(alma_id) + expect(decoded_token[0]).to have_key('userName') + expect(decoded_token[0]['userName']).to eq(alma_id) + end end - it 'encodes the user info into a jwt' do - decoded_token = EfeesInvoice.decode(@invoice.jwt) - expect(decoded_token[0] { userName }).to have_key('userName') - expect(decoded_token[0] { userName }).to have_value('10335026') - end + describe '#submit!' do + let(:alma_id) { '123456' } + + let(:user_double) do + double( + id: alma_id, + email: 'test@example.com', + name: 'Test User', + fees: 100.0 + ) + end + + before do + allow(Alma::User) + .to receive(:find_if_exists) + .with(alma_id) + .and_return(user_double) + end + + it 'enqueues the efee invoice email' do + invoice = EfeesInvoice.new(alma_id) - it 'submits the request mailer' do # rubocop:disable RSpec/NoExpectationExample - @invoice.submit! + expect do + invoice.submit! + end.to have_enqueued_job(ActionMailer::MailDeliveryJob) + .with( + 'RequestMailer', + 'efee_invoice_email', + 'deliver_now', + { args: [alma_id] } + ) + end end end diff --git a/spec/models/security_incident_report_form_spec.rb b/spec/models/security_incident_report_form_spec.rb index 343a79e5..a175d791 100644 --- a/spec/models/security_incident_report_form_spec.rb +++ b/spec/models/security_incident_report_form_spec.rb @@ -78,8 +78,20 @@ expect(@form.valid?).to eq(true) end - it 'sends an email' do - expect { @form.submit! }.to change { ActionMailer::Base.deliveries.count }.by(1) + it 'enqueues the security incident email' do + form = SecurityIncidentReportForm.new( + incident_location: 'Doe Library', + incident_date: Date.current, + incident_time: '14:30', + reporter_name: 'Lucas Van Donnelay', + email: 'lvandonnelay@berkeley.edu', + sup_email: 'sup@berkeley.edu', + unit: 'LIT', + phone: '510-555-1234', + incident_description: 'Put book back on shelf in incorrect call number order' + ) + + expect { form.submit! }.to have_enqueued_job(ActionMailer::MailDeliveryJob) end end end From 9cdc963a435c2d731aac10ea0ad9d466d656b68c Mon Sep 17 00:00:00 2001 From: David Zuckerman Date: Tue, 10 Feb 2026 12:10:21 -0800 Subject: [PATCH 2/3] Update request_mailer to work with update to efees which uses deliver_later now --- app/mailers/request_mailer.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/mailers/request_mailer.rb b/app/mailers/request_mailer.rb index 1fc3e192..17ec7384 100644 --- a/app/mailers/request_mailer.rb +++ b/app/mailers/request_mailer.rb @@ -232,7 +232,8 @@ def bibliographic_email(email, attachment_contents, subject, body) mail(to: email, subject:, body:) end - def efee_invoice_email(efee) + def efee_invoice_email(alma_id) + efee = EfeesInvoice.new(alma_id) # type probably isn't needed now that I spun this off to a separate url params = { type: 'efee', @@ -244,7 +245,7 @@ def efee_invoice_email(efee) @name = efee.name @link = invoice_link.to_s - mail(to: efee.email) + mail(to: efee.email, subject: 'Library Fees') end # Departmental Card Request : alerts to privdesk, Mark Marrow and Supervisor From c28068090cbc7df50a83248208afc521e5117f1c Mon Sep 17 00:00:00 2001 From: David Zuckerman Date: Tue, 10 Feb 2026 14:47:17 -0800 Subject: [PATCH 3/3] removed ActiveJob::Base.queue_adapter = :test from affiliate_borrow_request_form, already a default --- spec/models/affiliate_borrow_request_form_spec.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/spec/models/affiliate_borrow_request_form_spec.rb b/spec/models/affiliate_borrow_request_form_spec.rb index 4c0d8c47..7f5d74e5 100644 --- a/spec/models/affiliate_borrow_request_form_spec.rb +++ b/spec/models/affiliate_borrow_request_form_spec.rb @@ -1,7 +1,5 @@ require 'rails_helper' -ActiveJob::Base.queue_adapter = :test - describe AffiliateBorrowRequestForm do include ActiveJob::TestHelper include ActionMailer::TestHelper