diff --git a/app/jobs/priorized_mail_delivery_job.rb b/app/jobs/priorized_mail_delivery_job.rb new file mode 100644 index 00000000000..03ccb66ad56 --- /dev/null +++ b/app/jobs/priorized_mail_delivery_job.rb @@ -0,0 +1,14 @@ +class PriorizedMailDeliveryJob < ActionMailer::MailDeliveryJob + def queue_name + mailer, action_name = @arguments + if mailer.constantize.critical_email?(action_name) + super + else + custom_queue + end + end + + def custom_queue + ENV.fetch('BULK_EMAIL_QUEUE') { Rails.application.config.action_mailer.deliver_later_queue_name } + end +end diff --git a/app/mailers/administrateur_mailer.rb b/app/mailers/administrateur_mailer.rb index 24956ded9b3..0ed0d659867 100644 --- a/app/mailers/administrateur_mailer.rb +++ b/app/mailers/administrateur_mailer.rb @@ -39,9 +39,7 @@ def api_token_expiration(user, tokens) reply_to: CONTACT_EMAIL) end - private - - def forced_delivery_for_action? + def self.critical_email?(action_name) action_name == "activate_before_expiration" end end diff --git a/app/mailers/administration_mailer.rb b/app/mailers/administration_mailer.rb index 6af3ea7c7f5..cf800c2e458 100644 --- a/app/mailers/administration_mailer.rb +++ b/app/mailers/administration_mailer.rb @@ -20,4 +20,8 @@ def refuse_admin(admin_email) subject: subject, reply_to: CONTACT_EMAIL) end + + def self.critical_email?(action_name) + false + end end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index adb90e82ddd..692b3f4e0aa 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -2,6 +2,7 @@ class ApplicationMailer < ActionMailer::Base include MailerDolistConcern include MailerMonitoringConcern include BalancedDeliveryConcern + include PriorityDeliveryConcern helper :application # gives access to all helpers defined within `application_helper`. default from: "#{APPLICATION_NAME} <#{CONTACT_EMAIL}>" diff --git a/app/mailers/avis_mailer.rb b/app/mailers/avis_mailer.rb index c24cd7ede7b..49a8b14e087 100644 --- a/app/mailers/avis_mailer.rb +++ b/app/mailers/avis_mailer.rb @@ -19,4 +19,8 @@ def avis_invitation(avis, targeted_user_link = nil) # ensure re-entrance if exis mail(to: email, subject: subject) end end + + def self.critical_email?(action_name) + false + end end diff --git a/app/mailers/concerns/balanced_delivery_concern.rb b/app/mailers/concerns/balanced_delivery_concern.rb index f9f7bd299a2..486aadac44c 100644 --- a/app/mailers/concerns/balanced_delivery_concern.rb +++ b/app/mailers/concerns/balanced_delivery_concern.rb @@ -2,16 +2,16 @@ module BalancedDeliveryConcern extend ActiveSupport::Concern included do - before_action :add_delivery_method, if: :forced_delivery? + before_action :add_delivery_method, if: :forced_delivery_provider? - private - - def forced_delivery_for_action? - false + def critical_email? + self.class.critical_email?(action_name) end - def forced_delivery? - SafeMailer.forced_delivery_method.present? && forced_delivery_for_action? + private + + def forced_delivery_provider? + SafeMailer.forced_delivery_method.present? && critical_email? end def add_delivery_method diff --git a/app/mailers/concerns/priority_delivery_concern.rb b/app/mailers/concerns/priority_delivery_concern.rb new file mode 100644 index 00000000000..f8e68dd6f3e --- /dev/null +++ b/app/mailers/concerns/priority_delivery_concern.rb @@ -0,0 +1,10 @@ +module PriorityDeliveryConcern + extend ActiveSupport::Concern + included do + self.delivery_job = PriorizedMailDeliveryJob + + def self.critical_email?(action_name) + raise NotImplementedError + end + end +end diff --git a/app/mailers/devise_user_mailer.rb b/app/mailers/devise_user_mailer.rb index 13f97cad412..4c910341fd1 100644 --- a/app/mailers/devise_user_mailer.rb +++ b/app/mailers/devise_user_mailer.rb @@ -6,6 +6,8 @@ class DeviseUserMailer < Devise::Mailer include MailerDolistConcern include MailerMonitoringConcern include BalancedDeliveryConcern + include PriorityDeliveryConcern + layout 'mailers/layout' def template_paths @@ -19,7 +21,7 @@ def confirmation_instructions(record, token, opts = {}) super end - def forced_delivery_for_action? + def self.critical_email?(action_name) true end end diff --git a/app/mailers/dossier_mailer.rb b/app/mailers/dossier_mailer.rb index d9081708ee6..adad878a258 100644 --- a/app/mailers/dossier_mailer.rb +++ b/app/mailers/dossier_mailer.rb @@ -184,6 +184,10 @@ def notify_transfer(transfer) end end + def self.critical_email?(action_name) + false + end + protected def prevent_perform_deliveries diff --git a/app/mailers/expert_mailer.rb b/app/mailers/expert_mailer.rb index e9777159edb..de871bea492 100644 --- a/app/mailers/expert_mailer.rb +++ b/app/mailers/expert_mailer.rb @@ -22,6 +22,10 @@ def send_dossier_decision_v2(avis) mail(template_name: 'send_dossier_decision', to: email, subject: subject) end + + def self.critical_email?(action_name) + false + end end def decision_dossier(dossier) diff --git a/app/mailers/groupe_gestionnaire_mailer.rb b/app/mailers/groupe_gestionnaire_mailer.rb index b5c4debac6f..cecde91afe8 100644 --- a/app/mailers/groupe_gestionnaire_mailer.rb +++ b/app/mailers/groupe_gestionnaire_mailer.rb @@ -18,4 +18,8 @@ def notify_added_gestionnaires(groupe_gestionnaire, added_gestionnaires, current mail(bcc: added_gestionnaire_emails, subject: subject) end + + def self.critical_email?(action_name) + false + end end diff --git a/app/mailers/groupe_instructeur_mailer.rb b/app/mailers/groupe_instructeur_mailer.rb index eb0c6ceb816..76f0e917c59 100644 --- a/app/mailers/groupe_instructeur_mailer.rb +++ b/app/mailers/groupe_instructeur_mailer.rb @@ -27,4 +27,8 @@ def notify_added_instructeurs(group, added_instructeurs, current_instructeur_ema mail(bcc: added_instructeur_emails, subject: subject) end + + def self.critical_email?(action_name) + false + end end diff --git a/app/mailers/instructeur_mailer.rb b/app/mailers/instructeur_mailer.rb index af175ff8627..0a258485027 100644 --- a/app/mailers/instructeur_mailer.rb +++ b/app/mailers/instructeur_mailer.rb @@ -45,9 +45,7 @@ def send_notifications(instructeur, data) mail(to: instructeur.email, subject: subject) end - private - - def forced_delivery_for_action? + def self.critical_email?(action_name) action_name == "send_login_token" end end diff --git a/app/mailers/invite_mailer.rb b/app/mailers/invite_mailer.rb index a9d0b38b3c7..d9c892b91e3 100644 --- a/app/mailers/invite_mailer.rb +++ b/app/mailers/invite_mailer.rb @@ -33,7 +33,7 @@ def send_mail(invite, subject, reply_to) reply_to: reply_to) end - def forced_delivery_for_action? + def self.critical_email?(action_name) true end end diff --git a/app/mailers/notification_mailer.rb b/app/mailers/notification_mailer.rb index 23b9a605711..3b9e5a80fd2 100644 --- a/app/mailers/notification_mailer.rb +++ b/app/mailers/notification_mailer.rb @@ -48,6 +48,10 @@ def self.send_pending_correction(dossier) with(dossier: dossier).send_notification end + def self.critical_email?(action_name) + false + end + private def set_services_publics_plus diff --git a/app/mailers/preactivate_users_mailer.rb b/app/mailers/preactivate_users_mailer.rb index c3c9eb25f70..d53555218c0 100644 --- a/app/mailers/preactivate_users_mailer.rb +++ b/app/mailers/preactivate_users_mailer.rb @@ -30,4 +30,8 @@ def reinvite(model, model_name) reply_to: CONTACT_EMAIL, body: body) end + + def self.critical_email?(action_name) + false + end end diff --git a/app/mailers/resend_attestation_mailer.rb b/app/mailers/resend_attestation_mailer.rb index df3f627bf89..c67fcd3b85f 100644 --- a/app/mailers/resend_attestation_mailer.rb +++ b/app/mailers/resend_attestation_mailer.rb @@ -8,6 +8,10 @@ def resend_attestation(dossier) mail(to: to, subject: subject, body: body(dossier)) end + def self.critical_email?(action_name) + false + end + private def body(dossier) diff --git a/app/mailers/super_admin_mailer.rb b/app/mailers/super_admin_mailer.rb index 75c838652c7..edbb050b604 100644 --- a/app/mailers/super_admin_mailer.rb +++ b/app/mailers/super_admin_mailer.rb @@ -4,4 +4,8 @@ def dolist_report(to, csv_path) mail(to: to, subject: "Dolist report", body: "Ci-joint le rapport d'emails récents envoyés via Dolist.") end + + def self.critical_email?(action_name) + false + end end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 4e8f97db9d6..85b1f3ca609 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -67,7 +67,7 @@ def send_archive(administrateur_or_instructeur, procedure, archive) mail(to: administrateur_or_instructeur.email, subject: subject) end - def forced_delivery_for_action? + def self.critical_email?(action_name) ['france_connect_merge_confirmation', "new_account_warning", "ask_for_merge", "invite_instructeur"].include?(action_name) end end diff --git a/config/env.example.optional b/config/env.example.optional index e177bf40dc2..d2448480948 100644 --- a/config/env.example.optional +++ b/config/env.example.optional @@ -236,3 +236,4 @@ DS_LOG_LEVEL='info' # Admins group usage (gestionnaire de groupes d'administrateurs) # can be removed if needed when EVERY PARTS of the feature will be merged / only used in routes.rb ADMINS_GROUP_ENABLED="disabled" +BULK_EMAIL_QUEUE="low_priority" diff --git a/config/environments/development.rb b/config/environments/development.rb index 485fbc0c5b5..0749590943b 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -86,7 +86,7 @@ helo: ENV['HELO_ENABLED'] == 'enabled' ? 100 : 0, letter_opener: ENV['HELO_ENABLED'] == 'enabled' ? 0 : 100 } - config.action_mailer.delivery_method = :balancer + config.action_mailer.delivery_method = :letter_opener_web config.action_mailer.default_url_options = { host: ENV.fetch("APP_HOST") } config.action_mailer.asset_host = "http://" + ENV.fetch("APP_HOST") diff --git a/spec/mailers/administrateur_mailer_spec.rb b/spec/mailers/administrateur_mailer_spec.rb index 64b7fffb7b7..a42edb2b3b0 100644 --- a/spec/mailers/administrateur_mailer_spec.rb +++ b/spec/mailers/administrateur_mailer_spec.rb @@ -1,32 +1,57 @@ RSpec.describe AdministrateurMailer, type: :mailer do let(:procedure) { create(:procedure) } let(:admin_email) { 'administrateur@email.fr' } + describe '.notify_procedure_expires_when_termine_forced' do subject { described_class.notify_procedure_expires_when_termine_forced(admin_email, procedure) } + it { expect(subject.to).to eq([admin_email]) } it { expect(subject.subject).to include("La suppression automatique des dossiers a été activée sur la démarche") } + + context 'when perform_later is called' do + let(:custom_queue) { 'low_priority' } + before { ENV['BULK_EMAIL_QUEUE'] = custom_queue } + it 'enqueues email is custom queue for low priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(custom_queue) + end + end end + describe '.activate_before_expiration' do let(:user) { create(:user, reset_password_sent_at: 2.days.ago) } let(:token) { SecureRandom.hex } + subject { described_class.activate_before_expiration(user, token) } context 'without SafeMailer configured' do - subject { described_class.activate_before_expiration(user, token) } it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(nil) } end context 'with SafeMailer configured' do let(:forced_delivery_method) { :kikoo } before { allow(SafeMailer).to receive(:forced_delivery_method).and_return(forced_delivery_method) } - subject { described_class.activate_before_expiration(user, token) } it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(forced_delivery_method.to_s) } end + + context 'when perform_later is called' do + it 'enqueues email in default queue for high priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(Rails.application.config.action_mailer.deliver_later_queue_name) +end + end end describe '.notify_service_without_siret' do subject { described_class.notify_service_without_siret(admin_email) } + it { expect(subject.to).to eq([admin_email]) } it { expect(subject.subject).to eq("Siret manquant sur un de vos services") } it { expect(subject.body).to include("un de vos services n'a pas son siret renseigné") } + + context 'when perform_later is called' do + let(:custom_queue) { 'low_priority' } + before { ENV['BULK_EMAIL_QUEUE'] = custom_queue } + it 'enqueues email is custom queue for low priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(custom_queue) + end + end end end diff --git a/spec/mailers/devise_user_mailer_spec.rb b/spec/mailers/devise_user_mailer_spec.rb index f3c82701f98..e0093028c1a 100644 --- a/spec/mailers/devise_user_mailer_spec.rb +++ b/spec/mailers/devise_user_mailer_spec.rb @@ -2,15 +2,22 @@ let(:user) { create(:user) } let(:token) { SecureRandom.hex } describe '.confirmation_instructions' do + subject { described_class.confirmation_instructions(user, token, opts = {}) } + context 'without SafeMailer configured' do - subject { described_class.confirmation_instructions(user, token, opts = {}) } it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(nil) } end + context 'with SafeMailer configured' do let(:forced_delivery_method) { :kikoo } before { allow(SafeMailer).to receive(:forced_delivery_method).and_return(forced_delivery_method) } - subject { described_class.confirmation_instructions(user, token, opts = {}) } it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(forced_delivery_method.to_s) } end + + context 'when perform_later is called' do + it 'enqueues email in default queue for high priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(Rails.application.config.action_mailer.deliver_later_queue_name) +end + end end end diff --git a/spec/mailers/instructeur_mailer_spec.rb b/spec/mailers/instructeur_mailer_spec.rb index ce390bb64bc..e84dfde82cb 100644 --- a/spec/mailers/instructeur_mailer_spec.rb +++ b/spec/mailers/instructeur_mailer_spec.rb @@ -3,27 +3,40 @@ let(:sender) { create(:instructeur) } let(:recipient) { create(:instructeur) } let(:dossier) { create(:dossier) } - subject { described_class.send_dossier(sender, dossier, recipient) } it { expect(subject.body).to include('Bonjour') } + + context 'when perform_later is called' do + let(:custom_queue) { 'low_priority' } + before { ENV['BULK_EMAIL_QUEUE'] = custom_queue } + + it 'enqueues email is custom queue for low priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(custom_queue) + end + end end describe '#send_login_token' do let(:user) { create(:instructeur) } let(:token) { SecureRandom.hex } + subject { described_class.send_login_token(user, token) } context 'without SafeMailer configured' do - subject { described_class.send_login_token(user, token) } it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(nil) } end context 'with SafeMailer configured' do let(:forced_delivery_method) { :kikoo } before { allow(SafeMailer).to receive(:forced_delivery_method).and_return(forced_delivery_method) } - subject { described_class.send_login_token(user, token) } it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(forced_delivery_method.to_s) } end + + context 'when perform_later is called' do + it 'enqueues email in default queue for high priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(Rails.application.config.action_mailer.deliver_later_queue_name) + end + end end describe '#last_week_overview' do @@ -62,5 +75,14 @@ expect(subject.body).to be_blank end end + + context 'when perform_later is called' do + let(:custom_queue) { 'low_priority' } + before { ENV['BULK_EMAIL_QUEUE'] = custom_queue } + + it 'enqueues email is custom queue for low priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(custom_queue) + end + end end end diff --git a/spec/mailers/invite_mailer_spec.rb b/spec/mailers/invite_mailer_spec.rb index 0e5102dc9f0..9500e5136c7 100644 --- a/spec/mailers/invite_mailer_spec.rb +++ b/spec/mailers/invite_mailer_spec.rb @@ -1,9 +1,8 @@ RSpec.describe InviteMailer, type: :mailer do - let(:deliver) { mailer.deliver_now } + let(:deliver) { subject.deliver_now } + subject { InviteMailer.invite_user(invite) } describe '.invite_user' do - let(:mailer) { InviteMailer.invite_user(invite) } - let(:invite) { create(:invite, user: create(:user)) } it 'creates a target_user_link' do expect { deliver } @@ -20,29 +19,34 @@ end begin - mailer.body + subject.body rescue => e nil end - mailer.body + subject.body expect(TargetedUserLink.where(target_model: invite, user: invite.user).count).to eq(1) end end context 'without SafeMailer configured' do - it { expect(mailer[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(nil) } + it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(nil) } end context 'with SafeMailer configured' do let(:forced_delivery_method) { :kikoo } before { allow(SafeMailer).to receive(:forced_delivery_method).and_return(forced_delivery_method) } - it { expect(mailer[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(forced_delivery_method.to_s) } + it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(forced_delivery_method.to_s) } + end + + context 'when perform_later is called' do + it 'enqueues email in default queue for high priority delivery' do + expect { invite }.to have_enqueued_job.on_queue(Rails.application.config.action_mailer.deliver_later_queue_name) + end end end describe '.invite_guest' do - let(:mailer) { InviteMailer.invite_guest(invite) } let(:invite) { create(:invite, user: nil, email: 'kikoo@lol.fr') } it 'creates a target_user_link' do @@ -69,14 +73,20 @@ end begin - mailer.body + subject.body rescue => e nil end - mailer.body + subject.body expect(TargetedUserLink.where(target_model: invite, user: invite.user).count).to eq(1) end end + + context 'when perform_later is called' do + it 'enqueues email in default queue for high priority delivery' do + expect { invite }.to have_enqueued_job.on_queue(Rails.application.config.action_mailer.deliver_later_queue_name) + end + end end end diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index b741baf2ead..507b9d8b09a 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -1,5 +1,5 @@ RSpec.describe UserMailer, type: :mailer do - let(:user) { build(:user) } + let(:user) { create(:user) } describe '.new_account_warning' do subject { described_class.new_account_warning(user) } @@ -25,6 +25,12 @@ before { allow(SafeMailer).to receive(:forced_delivery_method).and_return(forced_delivery_method) } it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(forced_delivery_method.to_s) } end + + context 'when perform_later is called' do + it 'enqueues email in default queue for high priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(Rails.application.config.action_mailer.deliver_later_queue_name) + end + end end describe '.ask_for_merge' do @@ -44,6 +50,12 @@ before { allow(SafeMailer).to receive(:forced_delivery_method).and_return(forced_delivery_method) } it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(forced_delivery_method.to_s) } end + + context 'when perform_later is called' do + it 'enqueues email in default queue for high priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(Rails.application.config.action_mailer.deliver_later_queue_name) + end + end end describe '.france_connect_merge_confirmation' do @@ -64,6 +76,12 @@ before { allow(SafeMailer).to receive(:forced_delivery_method).and_return(forced_delivery_method) } it { expect(subject[BalancerDeliveryMethod::FORCE_DELIVERY_METHOD_HEADER]&.value).to eq(forced_delivery_method.to_s) } end + + context 'when perform_later is called' do + it 'enqueues email in default queue for high priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(Rails.application.config.action_mailer.deliver_later_queue_name) + end + end end describe '.send_archive' do @@ -84,5 +102,14 @@ it { expect(subject.body).to have_link('Consulter mes archives', href: admin_procedure_archives_url(procedure)) } it { expect(subject.body).to have_link("#{procedure.id} − #{procedure.libelle}", href: admin_procedure_url(procedure)) } end + + context 'when perform_later is called' do + let(:role) { create(:administrateur) } + let(:custom_queue) { 'low_priority' } + before { ENV['BULK_EMAIL_QUEUE'] = custom_queue } + it 'enqueues email is custom queue for low priority delivery' do + expect { subject.deliver_later }.to have_enqueued_job.on_queue(custom_queue) + end + end end end