amelioration(dolist): ne log erreurs pas les erreurs dans sentry lorsque le contact chez dolist est injoingable ou hardbounce
This commit is contained in:
parent
a286af8a70
commit
8fa2bbb67d
3 changed files with 73 additions and 11 deletions
|
@ -4,6 +4,7 @@ class Dolist::API
|
|||
CONTACT_URL = "https://apiv9.dolist.net/v1/contacts/read?AccountID=%{account_id}"
|
||||
EMAIL_LOGS_URL = "https://apiv9.dolist.net/v1/statistics/email/sendings/transactional/search?AccountID=%{account_id}"
|
||||
EMAIL_KEY = 7
|
||||
STATUS_KEY = 72
|
||||
DOLIST_WEB_DASHBOARD = "https://campaign.dolist.net/#/%{account_id}/contacts/%{contact_id}/sendings"
|
||||
EMAIL_MESSAGES_ADRESSES_REPLIES = "https://apiv9.dolist.net/v1/email/messages/addresses/replies?AccountID=%{account_id}"
|
||||
EMAIL_MESSAGES_ADRESSES_PACKSENDERS = "https://apiv9.dolist.net/v1/email/messages/addresses/packsenders?AccountID=%{account_id}"
|
||||
|
@ -13,6 +14,18 @@ class Dolist::API
|
|||
|
||||
class_attribute :limit_remaining, :limit_reset_at
|
||||
|
||||
# those code are just undocumented
|
||||
IGNORABLE_API_ERROR_CODE = [
|
||||
"458",
|
||||
"402"
|
||||
]
|
||||
|
||||
# see: https://usercampaign.dolist.net/wp-content/uploads/2022/12/Comprendre-les-Opt-out-tableau-v2.pdf
|
||||
IGNORABLE_CONTACT_STATUSES = [
|
||||
"4", # Le serveur distant n'accepte pas le mail car il identifie que l’adresse e-mail est en erreur.
|
||||
"7" # Suite à un envoi, le serveur distant accepte le mail dans un premier temps mais envoie une erreur définitive car l’adresse e-mail est en erreur. L'adresse e-mail n’existe pas ou n'existe plus.
|
||||
]
|
||||
|
||||
class << self
|
||||
def save_rate_limit_headers(headers)
|
||||
self.limit_remaining = headers["X-Rate-Limit-Remaining"].to_i
|
||||
|
@ -124,6 +137,31 @@ class Dolist::API
|
|||
get format_url(EMAIL_MESSAGES_ADRESSES_REPLIES)
|
||||
end
|
||||
|
||||
# Une adresse e-mail peut ne pas être adressable pour différentes raisons (injoignable, plainte pour spam, blocage d’un FAI).
|
||||
# Dans ce cas l’API d’envoi transactionnel renvoie différentes erreurs.
|
||||
# Pour connaitre exactement le statut d’une adresse, je vous invite à récupérer le champ 72 du contact à partir de son adresse e-mail avec la méthode https://api.dolist.com/documentation/index.html#/40e7751d00dc3-rechercher-un-contact
|
||||
#
|
||||
# La liste des différents statuts est disponible sur https://usercampaign.dolist.net/wp-content/uploads/2022/12/Comprendre-les-Opt-out-tableau-v2.pdf
|
||||
def fetch_contact_status(email_address)
|
||||
url = format(Dolist::API::CONTACT_URL, account_id: account_id)
|
||||
body = {
|
||||
Query: {
|
||||
FieldValueList: [{ ID: 7, Value: email_address }],
|
||||
OutputFieldIDList: [72]
|
||||
}
|
||||
}.to_json
|
||||
|
||||
post(url, body)["FieldList"].find { _1['ID'] == 72 }['Value']
|
||||
end
|
||||
|
||||
def ignorable_api_error_code?(api_error_code)
|
||||
IGNORABLE_API_ERROR_CODE.include?(api_error_code)
|
||||
end
|
||||
|
||||
def ignorable_contact_status?(contact_status)
|
||||
IGNORABLE_CONTACT_STATUSES.include?(contact_status)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def format_url(base)
|
||||
|
|
|
@ -15,12 +15,21 @@ ActiveSupport.on_load(:action_mailer) do
|
|||
def initialize(mail); end
|
||||
|
||||
def deliver!(mail)
|
||||
response = Dolist::API.new.send_email(mail)
|
||||
|
||||
client = Dolist::API.new
|
||||
response = client.send_email(mail)
|
||||
if response&.dig("Result")
|
||||
mail.message_id = response.dig("Result")
|
||||
else
|
||||
fail "DoList delivery error. Body: #{response}"
|
||||
error_code = response&.dig("ResponseStatus", "ErrorCode")
|
||||
|
||||
contact_status = if client.ignorable_api_error_code?(error_code)
|
||||
client.fetch_contact_status(mail.to.first)
|
||||
else
|
||||
nil
|
||||
end
|
||||
if !client.ignorable_contact_status?(contact_status)
|
||||
fail "DoList delivery error. Body: #{response}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,16 +28,31 @@ RSpec.describe ApplicationMailer, type: :mailer do
|
|||
|
||||
describe 'dealing with Dolist API error' do
|
||||
let(:dossier) { create(:dossier, procedure: create(:simple_procedure)) }
|
||||
before do
|
||||
ActionMailer::Base.delivery_method = :dolist_api
|
||||
api_error_response = { "ResponseStatus": { "ErrorCode": "Forbidden", "Message": "Blocked non authorized request", "Errors": [] } }
|
||||
allow_any_instance_of(Dolist::API).to receive(:send_email).and_return(api_error_response)
|
||||
end
|
||||
subject { DossierMailer.with(dossier:).notify_new_draft.deliver_now }
|
||||
context 'not ignored error' do
|
||||
before do
|
||||
ActionMailer::Base.delivery_method = :dolist_api
|
||||
api_error_response = { "ResponseStatus": { "ErrorCode": "Forbidden", "Message": "Blocked non authorized request", "Errors": [] } }
|
||||
allow_any_instance_of(Dolist::API).to receive(:send_email).and_return(api_error_response)
|
||||
end
|
||||
|
||||
it 'raise classic error to retry' do
|
||||
expect { subject }.to raise_error(MailDeliveryError)
|
||||
expect(EmailEvent.dolist_api.dispatch_error.count).to eq(1)
|
||||
it 'raise classic error to retry' do
|
||||
expect { subject }.to raise_error(MailDeliveryError)
|
||||
expect(EmailEvent.dolist_api.dispatch_error.count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context 'ignored error' do
|
||||
before do
|
||||
ActionMailer::Base.delivery_method = :dolist_api
|
||||
api_error_response = { "ResponseStatus" => { "ErrorCode" => "458", "Message" => "The contact is disabled.", "Errors" => [] } }
|
||||
allow_any_instance_of(Dolist::API).to receive(:send_email).and_return(api_error_response)
|
||||
allow_any_instance_of(Dolist::API).to receive(:fetch_contact_status).with(dossier.user.email).and_return("7")
|
||||
end
|
||||
|
||||
it 'does not raise' do
|
||||
expect { subject }.not_to raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue