mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: Log the SMTP response in EmailLog (#17056)
When sending emails with delivery_method_options -> return_response set to true, the SMTP sending code inside Mail will return the SMTP response when calling deliver! for mail within the app. This commit ensures that Email::Sender captures this response if it is returned and stores it against the EmailLog created for the sent email. A follow up PR will make this visible within the admin email UI.
This commit is contained in:
@@ -16,7 +16,8 @@ class GroupSmtpMailer < ActionMailer::Base
|
|||||||
user_name: from_group.email_username,
|
user_name: from_group.email_username,
|
||||||
password: from_group.email_password,
|
password: from_group.email_password,
|
||||||
authentication: GlobalSetting.smtp_authentication,
|
authentication: GlobalSetting.smtp_authentication,
|
||||||
enable_starttls_auto: from_group.smtp_ssl
|
enable_starttls_auto: from_group.smtp_ssl,
|
||||||
|
return_response: true
|
||||||
}
|
}
|
||||||
|
|
||||||
group_name = from_group.name_full_preferred
|
group_name = from_group.name_full_preferred
|
||||||
|
|||||||
@@ -127,22 +127,23 @@ end
|
|||||||
#
|
#
|
||||||
# Table name: email_logs
|
# Table name: email_logs
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# to_address :string not null
|
# to_address :string not null
|
||||||
# email_type :string not null
|
# email_type :string not null
|
||||||
# user_id :integer
|
# user_id :integer
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# post_id :integer
|
# post_id :integer
|
||||||
# bounce_key :uuid
|
# bounce_key :uuid
|
||||||
# bounced :boolean default(FALSE), not null
|
# bounced :boolean default(FALSE), not null
|
||||||
# message_id :string
|
# message_id :string
|
||||||
# smtp_group_id :integer
|
# smtp_group_id :integer
|
||||||
# cc_addresses :text
|
# cc_addresses :text
|
||||||
# cc_user_ids :integer is an Array
|
# cc_user_ids :integer is an Array
|
||||||
# raw :text
|
# raw :text
|
||||||
# topic_id :integer
|
# topic_id :integer
|
||||||
# bounce_error_code :string
|
# bounce_error_code :string
|
||||||
|
# smtp_transaction_response :string(500)
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddSmtpTransactionResponseToEmailLogs < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
add_column :email_logs, :smtp_transaction_response, :string, null: true, limit: 500
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -145,6 +145,7 @@ module Email
|
|||||||
}
|
}
|
||||||
|
|
||||||
args[:delivery_method_options] = @opts[:delivery_method_options] if @opts[:delivery_method_options]
|
args[:delivery_method_options] = @opts[:delivery_method_options] if @opts[:delivery_method_options]
|
||||||
|
args[:delivery_method_options] = (args[:delivery_method_options] || {}).merge(return_response: true)
|
||||||
|
|
||||||
args
|
args
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -315,7 +315,13 @@ module Email
|
|||||||
DiscourseEvent.trigger(:before_email_send, @message, @email_type)
|
DiscourseEvent.trigger(:before_email_send, @message, @email_type)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@message.deliver_now
|
message_response = @message.deliver!
|
||||||
|
|
||||||
|
# TestMailer from the Mail gem does not return a real response, it
|
||||||
|
# returns an array containing @message, so we have to have this workaround.
|
||||||
|
if message_response.kind_of?(Net::SMTP::Response)
|
||||||
|
email_log.smtp_transaction_response = message_response.message&.chomp
|
||||||
|
end
|
||||||
rescue *SMTP_CLIENT_ERRORS => e
|
rescue *SMTP_CLIENT_ERRORS => e
|
||||||
return skip(SkippedEmailLog.reason_types[:custom], custom_reason: e.message)
|
return skip(SkippedEmailLog.reason_types[:custom], custom_reason: e.message)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -7,6 +7,13 @@ describe Email::Sender do
|
|||||||
SiteSetting.secure_media_allow_embed_images_in_emails = false
|
SiteSetting.secure_media_allow_embed_images_in_emails = false
|
||||||
end
|
end
|
||||||
fab!(:post) { Fabricate(:post) }
|
fab!(:post) { Fabricate(:post) }
|
||||||
|
let(:mock_smtp_transaction_response) { "250 Ok: queued as 2l3Md07BObzB8kRyHZeoN0baSUAhzc7A-NviRioOr80=@mailhog.example" }
|
||||||
|
|
||||||
|
def stub_deliver_response(message)
|
||||||
|
message.stubs(:deliver!).returns(
|
||||||
|
Net::SMTP::Response.new("250", mock_smtp_transaction_response)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
context "disable_emails is enabled" do
|
context "disable_emails is enabled" do
|
||||||
fab!(:user) { Fabricate(:user) }
|
fab!(:user) { Fabricate(:user) }
|
||||||
@@ -41,19 +48,20 @@ describe Email::Sender do
|
|||||||
before { SiteSetting.disable_emails = "non-staff" }
|
before { SiteSetting.disable_emails = "non-staff" }
|
||||||
|
|
||||||
it "doesn't deliver mail to normal user" do
|
it "doesn't deliver mail to normal user" do
|
||||||
Mail::Message.any_instance.expects(:deliver_now).never
|
Mail::Message.any_instance.expects(:deliver!).never
|
||||||
message = Mail::Message.new(to: user.email, body: "hello")
|
message = Mail::Message.new(to: user.email, body: "hello")
|
||||||
|
stub_deliver_response(message)
|
||||||
expect(Email::Sender.new(message, :hello).send).to eq(nil)
|
expect(Email::Sender.new(message, :hello).send).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "delivers mail to staff user" do
|
it "delivers mail to staff user" do
|
||||||
Mail::Message.any_instance.expects(:deliver_now).once
|
Mail::Message.any_instance.expects(:deliver!).once
|
||||||
message = Mail::Message.new(to: moderator.email, body: "hello")
|
message = Mail::Message.new(to: moderator.email, body: "hello")
|
||||||
Email::Sender.new(message, :hello).send
|
Email::Sender.new(message, :hello).send
|
||||||
end
|
end
|
||||||
|
|
||||||
it "delivers mail to staff user when confirming new email if user is provided" do
|
it "delivers mail to staff user when confirming new email if user is provided" do
|
||||||
Mail::Message.any_instance.expects(:deliver_now).once
|
Mail::Message.any_instance.expects(:deliver!).once
|
||||||
Fabricate(:email_change_request, {
|
Fabricate(:email_change_request, {
|
||||||
user: moderator,
|
user: moderator,
|
||||||
new_email: "newemail@testmoderator.com",
|
new_email: "newemail@testmoderator.com",
|
||||||
@@ -69,32 +77,32 @@ describe Email::Sender do
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't deliver mail when the message is of type NullMail" do
|
it "doesn't deliver mail when the message is of type NullMail" do
|
||||||
Mail::Message.any_instance.expects(:deliver_now).never
|
Mail::Message.any_instance.expects(:deliver!).never
|
||||||
message = ActionMailer::Base::NullMail.new
|
message = ActionMailer::Base::NullMail.new
|
||||||
expect(Email::Sender.new(message, :hello).send).to eq(nil)
|
expect(Email::Sender.new(message, :hello).send).to eq(nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't deliver mail when the message is nil" do
|
it "doesn't deliver mail when the message is nil" do
|
||||||
Mail::Message.any_instance.expects(:deliver_now).never
|
Mail::Message.any_instance.expects(:deliver!).never
|
||||||
Email::Sender.new(nil, :hello).send
|
Email::Sender.new(nil, :hello).send
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't deliver when the to address is nil" do
|
it "doesn't deliver when the to address is nil" do
|
||||||
message = Mail::Message.new(body: 'hello')
|
message = Mail::Message.new(body: 'hello')
|
||||||
message.expects(:deliver_now).never
|
message.expects(:deliver!).never
|
||||||
Email::Sender.new(message, :hello).send
|
Email::Sender.new(message, :hello).send
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't deliver when the to address uses the .invalid tld" do
|
it "doesn't deliver when the to address uses the .invalid tld" do
|
||||||
message = Mail::Message.new(body: 'hello', to: 'myemail@example.invalid')
|
message = Mail::Message.new(body: 'hello', to: 'myemail@example.invalid')
|
||||||
message.expects(:deliver_now).never
|
message.expects(:deliver!).never
|
||||||
expect { Email::Sender.new(message, :hello).send }.
|
expect { Email::Sender.new(message, :hello).send }.
|
||||||
to change { SkippedEmailLog.where(reason_type: SkippedEmailLog.reason_types[:sender_message_to_invalid]).count }.by(1)
|
to change { SkippedEmailLog.where(reason_type: SkippedEmailLog.reason_types[:sender_message_to_invalid]).count }.by(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't deliver when the body is nil" do
|
it "doesn't deliver when the body is nil" do
|
||||||
message = Mail::Message.new(to: 'eviltrout@test.domain')
|
message = Mail::Message.new(to: 'eviltrout@test.domain')
|
||||||
message.expects(:deliver_now).never
|
message.expects(:deliver!).never
|
||||||
Email::Sender.new(message, :hello).send
|
Email::Sender.new(message, :hello).send
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -126,14 +134,14 @@ describe Email::Sender do
|
|||||||
to: 'eviltrout@test.domain',
|
to: 'eviltrout@test.domain',
|
||||||
body: '**hello**'
|
body: '**hello**'
|
||||||
)
|
)
|
||||||
message.stubs(:deliver_now)
|
stub_deliver_response(message)
|
||||||
message
|
message
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:email_sender) { Email::Sender.new(message, :valid_type) }
|
let(:email_sender) { Email::Sender.new(message, :valid_type) }
|
||||||
|
|
||||||
it 'calls deliver' do
|
it 'calls deliver' do
|
||||||
message.expects(:deliver_now).once
|
message.expects(:deliver!).once
|
||||||
email_sender.send
|
email_sender.send
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -414,6 +422,7 @@ describe Email::Sender do
|
|||||||
|
|
||||||
before do
|
before do
|
||||||
SiteSetting.enable_smtp = true
|
SiteSetting.enable_smtp = true
|
||||||
|
stub_deliver_response(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'adds the group id and raw content to the email log' do
|
it 'adds the group id and raw content to the email log' do
|
||||||
@@ -684,7 +693,7 @@ describe Email::Sender do
|
|||||||
message = Mail::Message.new to: 'disc@ourse.org', body: 'some content'
|
message = Mail::Message.new to: 'disc@ourse.org', body: 'some content'
|
||||||
message.header['X-Discourse-Post-Id'] = post.id
|
message.header['X-Discourse-Post-Id'] = post.id
|
||||||
message.header['X-Discourse-Topic-Id'] = post.topic_id
|
message.header['X-Discourse-Topic-Id'] = post.topic_id
|
||||||
message.expects(:deliver_now).never
|
message.expects(:deliver!).never
|
||||||
|
|
||||||
email_sender = Email::Sender.new(message, :valid_type)
|
email_sender = Email::Sender.new(message, :valid_type)
|
||||||
expect { email_sender.send }.to change { SkippedEmailLog.count }
|
expect { email_sender.send }.to change { SkippedEmailLog.count }
|
||||||
@@ -703,7 +712,7 @@ describe Email::Sender do
|
|||||||
message = Mail::Message.new to: 'disc@ourse.org', body: 'some content'
|
message = Mail::Message.new to: 'disc@ourse.org', body: 'some content'
|
||||||
message.header['X-Discourse-Post-Id'] = post.id
|
message.header['X-Discourse-Post-Id'] = post.id
|
||||||
message.header['X-Discourse-Topic-Id'] = post.topic_id
|
message.header['X-Discourse-Topic-Id'] = post.topic_id
|
||||||
message.expects(:deliver_now).never
|
message.expects(:deliver!).never
|
||||||
|
|
||||||
email_sender = Email::Sender.new(message, :valid_type)
|
email_sender = Email::Sender.new(message, :valid_type)
|
||||||
expect { email_sender.send }.to change { SkippedEmailLog.count }
|
expect { email_sender.send }.to change { SkippedEmailLog.count }
|
||||||
@@ -717,7 +726,7 @@ describe Email::Sender do
|
|||||||
context 'with a user' do
|
context 'with a user' do
|
||||||
let(:message) do
|
let(:message) do
|
||||||
message = Mail::Message.new to: 'eviltrout@test.domain', body: 'test body'
|
message = Mail::Message.new to: 'eviltrout@test.domain', body: 'test body'
|
||||||
message.stubs(:deliver_now)
|
stub_deliver_response(message)
|
||||||
message
|
message
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -733,6 +742,10 @@ describe Email::Sender do
|
|||||||
expect(@email_log.user_id).to eq(user.id)
|
expect(@email_log.user_id).to eq(user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'should have the smtp_transaction_response message' do
|
||||||
|
expect(@email_log.smtp_transaction_response).to eq(mock_smtp_transaction_response)
|
||||||
|
end
|
||||||
|
|
||||||
describe "post reply keys" do
|
describe "post reply keys" do
|
||||||
fab!(:post) { Fabricate(:post) }
|
fab!(:post) { Fabricate(:post) }
|
||||||
|
|
||||||
@@ -782,7 +795,7 @@ describe Email::Sender do
|
|||||||
context "with cc addresses" do
|
context "with cc addresses" do
|
||||||
let(:message) do
|
let(:message) do
|
||||||
message = Mail::Message.new to: 'eviltrout@test.domain', body: 'test body', cc: 'someguy@test.com;otherguy@xyz.com'
|
message = Mail::Message.new to: 'eviltrout@test.domain', body: 'test body', cc: 'someguy@test.com;otherguy@xyz.com'
|
||||||
message.stubs(:deliver_now)
|
stub_deliver_response(message)
|
||||||
message
|
message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user