From 4e9290ad76df26862358f0fb25ebaf511f358ce9 Mon Sep 17 00:00:00 2001 From: Anto Dominic Date: Wed, 25 Dec 2019 03:03:02 +0530 Subject: [PATCH] Send emails via sidekiq (#380) * add sidekiq web view if the user is an administrator * add sidekiq setup configuration and support * update devise to use delivery_later method and update test * update conversation to use deliver_later instead of deliver * Update Routes * Add Procfile for Heroku One-Click Start * updating docs * update concurrency and Procfile for supporting Heroku Free Dyno * update Procfile.dev --- Procfile | 2 ++ Procfile.dev | 3 ++- app/models/conversation.rb | 2 +- app/models/user.rb | 4 ++++ config/environments/development.rb | 2 ++ config/environments/production.rb | 1 + config/environments/staging.rb | 1 + config/environments/test.rb | 1 + config/routes.rb | 6 +++++ config/sidekiq.yml | 23 +++++++++++++++++++ docs/development/environment-setup/docker.md | 7 ++++++ .../mailers/confirmation_instructions_spec.rb | 2 +- spec/models/conversation_spec.rb | 4 ++-- 13 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 Procfile create mode 100644 config/sidekiq.yml diff --git a/Procfile b/Procfile new file mode 100644 index 000000000..16a5cc0c1 --- /dev/null +++ b/Procfile @@ -0,0 +1,2 @@ +web: bin/rails server -p $PORT -e $RAILS_ENV +worker: bundle exec sidekiq -C config/sidekiq.yml \ No newline at end of file diff --git a/Procfile.dev b/Procfile.dev index 88a1c79b1..90f69c8b3 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,2 +1,3 @@ backend: bin/rails s -p 3000 -frontend: bin/webpack-dev-server \ No newline at end of file +frontend: bin/webpack-dev-server +worker: bundle exec sidekiq \ No newline at end of file diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 88e4cbe16..e86b0124b 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -99,7 +99,7 @@ class Conversation < ApplicationRecord def send_email_notification_to_assignee return if self_assign?(assignee_id) - AssignmentMailer.conversation_assigned(self, assignee).deliver if saved_change_to_assignee_id? && assignee_id.present? + AssignmentMailer.conversation_assigned(self, assignee).deliver_later if saved_change_to_assignee_id? && assignee_id.present? end def self_assign?(assignee_id) diff --git a/app/models/user.rb b/app/models/user.rb index 75a73a479..2b5b7dbd1 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -86,6 +86,10 @@ class User < ApplicationRecord after_create :notify_creation after_destroy :notify_deletion + def send_devise_notification(notification, *args) + devise_mailer.send(notification, self, *args).deliver_later + end + def set_password_and_uid self.uid = email end diff --git a/config/environments/development.rb b/config/environments/development.rb index eb6b44994..3021513c4 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -33,6 +33,8 @@ Rails.application.configure do config.active_storage.service = :local # Don't care if the mailer can't send. + config.active_job.queue_adapter = :sidekiq + config.action_mailer.raise_delivery_errors = false config.action_mailer.perform_caching = false diff --git a/config/environments/production.rb b/config/environments/production.rb index d88c78c1b..2a0911e1c 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -75,6 +75,7 @@ Rails.application.configure do # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true + config.active_job.queue_adapter = :sidekiq # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify diff --git a/config/environments/staging.rb b/config/environments/staging.rb index 61e262c32..db4b952cb 100644 --- a/config/environments/staging.rb +++ b/config/environments/staging.rb @@ -75,6 +75,7 @@ Rails.application.configure do # Enable locale fallbacks for I18n (makes lookups for any locale fall back to # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true + config.active_job.queue_adapter = :sidekiq # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify diff --git a/config/environments/test.rb b/config/environments/test.rb index d34f78668..16bfd6192 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -43,6 +43,7 @@ Rails.application.configure do # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + config.active_job.queue_adapter = :test # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr diff --git a/config/routes.rb b/config/routes.rb index 74f36d59a..8a325a35a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -90,6 +90,12 @@ Rails.application.routes.draw do end end + # Sidekiq Web UI + require 'sidekiq/web' + authenticate :user, lambda { |u| u.administrator? } do + mount Sidekiq::Web => '/sidekiq' + end + # Used in mailer templates resource :app, only: [:index] do resources :conversations, only: [:show] diff --git a/config/sidekiq.yml b/config/sidekiq.yml new file mode 100644 index 000000000..57abb9933 --- /dev/null +++ b/config/sidekiq.yml @@ -0,0 +1,23 @@ +# Sample configuration file for Sidekiq. +# Options here can still be overridden by cmd line args. +# Place this file at config/sidekiq.yml and Sidekiq will +# pick it up automatically. +--- +:verbose: false +:concurrency: 10 +:timeout: 25 + +# Sidekiq will run this file through ERB when reading it so you can +# even put in dynamic logic, like a host-specific queue. +# http://www.mikeperham.com/2013/11/13/advanced-sidekiq-host-specific-queues/ +:queues: + - critical + - default + - low + - mailers + +# you can override concurrency based on environment +production: + :concurrency: 3 +staging: + :concurrency: 15 \ No newline at end of file diff --git a/docs/development/environment-setup/docker.md b/docs/development/environment-setup/docker.md index 28f87b185..fef84f37d 100644 --- a/docs/development/environment-setup/docker.md +++ b/docs/development/environment-setup/docker.md @@ -23,8 +23,15 @@ docker-compose run rails bundle exec rails db:reset docker-compose run --service-port rails ``` +open another terminal and also run below command to run sidekiq in a separate service + +``` +docker-compose run rails bundle exec sidekiq +``` + * Access the rails app frontend by visiting `http://0.0.0.0:3000/` * Access Mailhog inbox by visiting `http://0.0.0.0:8025/` (You will receive all emails going out of the application here) +* Access Sidekiq Web UI by visiting `http://0.0.0.0:3000/sidekiq` (You need to login with administrator account to access sidekiq) you can also use the below command instead to run the app and see the full logs. diff --git a/spec/mailers/confirmation_instructions_spec.rb b/spec/mailers/confirmation_instructions_spec.rb index 32e5aa915..2c217f911 100644 --- a/spec/mailers/confirmation_instructions_spec.rb +++ b/spec/mailers/confirmation_instructions_spec.rb @@ -6,7 +6,7 @@ RSpec.describe 'Confirmation Instructions', type: :mailer do describe :notify do let(:confirmable_user) { FactoryBot.build(:user, inviter: inviter_val) } let(:inviter_val) { nil } - let(:mail) { confirmable_user.send_confirmation_instructions } + let(:mail) { Devise::Mailer.confirmation_instructions(confirmable_user, nil, {}) } it 'has the correct header data' do expect(mail.reply_to).to contain_exactly('accounts@chatwoot.com') diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb index e5ddbeb96..376ee6f25 100644 --- a/spec/models/conversation_spec.rb +++ b/spec/models/conversation_spec.rb @@ -35,7 +35,7 @@ RSpec.describe Conversation, type: :model do allow(Rails.configuration.dispatcher).to receive(:dispatch) allow(AssignmentMailer).to receive(:conversation_assigned).and_return(assignment_mailer) - allow(assignment_mailer).to receive(:deliver) + allow(assignment_mailer).to receive(:deliver_later) Current.user = old_assignee conversation.update( @@ -68,7 +68,7 @@ RSpec.describe Conversation, type: :model do # send_email_notification_to_assignee expect(AssignmentMailer).to have_received(:conversation_assigned).with(conversation, new_assignee) - expect(assignment_mailer).to have_received(:deliver) if ENV.fetch('SMTP_ADDRESS', nil).present? + expect(assignment_mailer).to have_received(:deliver_later) if ENV.fetch('SMTP_ADDRESS', nil).present? end end