[IMP] mail_tracking_mailgun: validation auto check

- Configurable partner email auto check.
This commit is contained in:
David 2018-02-16 13:15:32 +01:00 committed by nicolas
parent bf01a3358d
commit 88f0f2d292
4 changed files with 51 additions and 74 deletions

View File

@ -40,6 +40,10 @@ parameters:
validity you must config this parameter with your account Public Validation validity you must config this parameter with your account Public Validation
Key. Key.
You can also config partner email autocheck with this system parameter:
- `mailgun.auto_check_partner_email`: Set it to True.
Usage Usage
===== =====

View File

@ -242,10 +242,10 @@ class MailTrackingEmail(models.Model):
if "items" not in content: if "items" not in content:
raise ValidationError(_("Event information not longer stored")) raise ValidationError(_("Event information not longer stored"))
for item in content["items"]: for item in content["items"]:
if not self.env['mail.tracking.event'].search(
# mailgun event hasn't been synced and recipient is the same as # mailgun event hasn't been synced and recipient is the same as
# in the evaluated tracking. We use email_split since tracking # in the evaluated tracking. We use email_split since tracking
# recipient could come in format: "example" <to@dest.com> # recipient could come in format: "example" <to@dest.com>
if not self.env['mail.tracking.event'].search(
[('mailgun_id', '=', item["id"])]) and ( [('mailgun_id', '=', item["id"])]) and (
item.get("recipient", "") == item.get("recipient", "") ==
email_split(tracking.recipient)[0]): email_split(tracking.recipient)[0]):
@ -254,59 +254,3 @@ class MailTrackingEmail(models.Model):
metadata = self._mailgun_metadata( metadata = self._mailgun_metadata(
mapped_event_type, item, {}) mapped_event_type, item, {})
tracking.event_create(mapped_event_type, metadata) tracking.event_create(mapped_event_type, metadata)
@api.multi
def check_email_list_validity(self, email_list):
"""
Checks email list validity with Mailgun's API
API documentation:
https://documentation.mailgun.com/en/latest/api-email-validation.html
"""
api_key, api_url, domain, validation_key = self.env[
'mail.tracking.email']._mailgun_values()
if not validation_key:
raise UserError(_('You need to configure mailgun.validation_key'
' in order to be able to check mails validity'))
result = {}
for email in email_list:
res = requests.get(
"%s/address/validate" % api_url,
auth=("api", validation_key), params={
"address": email,
"mailbox_verification": True,
})
if not res or res.status_code != 200:
result[email] = {'result': (_(
'Error %s trying to '
'check mail' % res.status_code or 'of connection'))}
continue
content = json.loads(res.content, res.apparent_encoding)
if 'mailbox_verification' not in content:
result[email] = {'result': (
_("Mailgun Error. Mailbox verification value wasn't"
" returned"))}
continue
# Not a valid address: API sets 'is_valid' as False
# and 'mailbox_verification' as None
if not content['is_valid']:
result[email] = {'result': (
_('%s is not a valid email address. Please check it '
'in order to avoid sending issues') % (email))}
continue
# If the mailbox is not valid API returns 'mailbox_verification'
# as a string with value 'false'
if content['mailbox_verification'] == 'false':
result[email] = {'result': (
_('%s failed the mailbox verification. Please check it '
'in order to avoid sending issues') % (email))}
continue
# If Mailgun can't complete the validation request the API returns
# 'mailbox_verification' as a string set to 'unknown'
if content['mailbox_verification'] == 'unknown':
result[email] = {'result': (
_("%s couldn't be verified. Either the request couln't be "
"completed or the mailbox provider doesn't support "
"email verification") % (email))}
continue
result[email] = {'result': _("The mailbox is correct")}
return result

View File

@ -45,43 +45,50 @@ class ResPartner(models.Model):
if not validation_key: if not validation_key:
raise UserError(_('You need to configure mailgun.validation_key' raise UserError(_('You need to configure mailgun.validation_key'
' in order to be able to check mails validity')) ' in order to be able to check mails validity'))
for partner in self: for partner in self.filtered('email'):
res = requests.get( res = requests.get(
"%s/address/validate" % api_url, "%s/address/validate" % api_url,
auth=("api", validation_key), params={ auth=("api", validation_key), params={
"address": partner.email, "address": partner.email,
"mailbox_verification": True, "mailbox_verification": True,
}) })
if not res or res.status_code != 200: if not res or res.status_code != 200 and not self.env.context.get(
'mailgun_auto_check'):
raise UserError(_( raise UserError(_(
'Error %s trying to ' 'Error %s trying to '
'check mail' % res.status_code or 'of connection')) 'check mail' % res.status_code or 'of connection'))
content = json.loads(res.content, res.apparent_encoding) content = json.loads(res.content, res.apparent_encoding)
if 'mailbox_verification' not in content: if 'mailbox_verification' not in content:
raise UserError( if not self.env.context.get('mailgun_auto_check'):
_("Mailgun Error. Mailbox verification value wasn't" raise UserError(
" returned")) _("Mailgun Error. Mailbox verification value wasn't"
" returned"))
# Not a valid address: API sets 'is_valid' as False # Not a valid address: API sets 'is_valid' as False
# and 'mailbox_verification' as None # and 'mailbox_verification' as None
if not content['is_valid']: if not content['is_valid']:
partner.email_bounced = True partner.email_bounced = True
raise UserError( body = _('%s is not a valid email address. Please check it'
_('%s is not a valid email address. Please check it ' ' in order to avoid sending issues') % partner.email
'in order to avoid sending issues') % (partner.email)) if not self.env.context.get('mailgun_auto_check'):
raise UserError(body)
partner.message_post(body=body)
# If the mailbox is not valid API returns 'mailbox_verification' # If the mailbox is not valid API returns 'mailbox_verification'
# as a string with value 'false' # as a string with value 'false'
if content['mailbox_verification'] == 'false': if content['mailbox_verification'] == 'false':
partner.email_bounced = True partner.email_bounced = True
raise UserError( body = _('%s failed the mailbox verification. Please check it'
_('%s failed the mailbox verification. Please check it ' ' in order to avoid sending issues') % partner.email
'in order to avoid sending issues') % (partner.email)) if not self.env.context.get('mailgun_auto_check'):
raise UserError(body)
partner.message_post(body=body)
# If Mailgun can't complete the validation request the API returns # If Mailgun can't complete the validation request the API returns
# 'mailbox_verification' as a string set to 'unknown' # 'mailbox_verification' as a string set to 'unknown'
if content['mailbox_verification'] == 'unknown': if content['mailbox_verification'] == 'unknown':
raise UserError( if not self.env.context.get('mailgun_auto_check'):
_("%s couldn't be verified. Either the request couln't be " raise UserError(
"completed or the mailbox provider doesn't support " _("%s couldn't be verified. Either the request couln't"
"email verification") % (partner.email)) " be completed or the mailbox provider doesn't "
"support email verification") % (partner.email))
@api.multi @api.multi
def check_email_bounced(self): def check_email_bounced(self):
@ -133,3 +140,21 @@ class ResPartner(models.Model):
auth=("api", api_key)) auth=("api", api_key))
if res.status_code in (200, 404) and partner.email_bounced: if res.status_code in (200, 404) and partner.email_bounced:
partner.email_bounced = False partner.email_bounced = False
def _autocheck_partner_email(self):
for partner in self:
partner.with_context(
mailgun_auto_check=True).check_email_validity()
@api.model
def create(self, vals):
if 'email' in vals and self.env['ir.config_parameter'].get_param(
'mailgun.auto_check_partner_email'):
self._autocheck_partner_email()
return super(ResPartner, self).create(vals)
def write(self, vals):
if 'email' in vals and self.env['ir.config_parameter'].get_param(
'mailgun.auto_check_partner_email'):
self._autocheck_partner_email()
return super(ResPartner, self).write(vals)

View File

@ -43,6 +43,8 @@ class TestMailgun(TransactionCase):
'mail.catchall.domain', self.domain) 'mail.catchall.domain', self.domain)
self.env['ir.config_parameter'].set_param( self.env['ir.config_parameter'].set_param(
'mailgun.validation_key', self.api_key) 'mailgun.validation_key', self.api_key)
self.env['ir.config_parameter'].set_param(
'mailgun.auto_check_partner_email', '')
self.event = { self.event = {
'Message-Id': u'<xxx.xxx.xxx-openerp-xxx-res.partner@test_db>', 'Message-Id': u'<xxx.xxx.xxx-openerp-xxx-res.partner@test_db>',
'X-Mailgun-Sid': u'WyIwNjgxZSIsICJ0b0BleGFtcGxlLmNvbSIsICI3MG' 'X-Mailgun-Sid': u'WyIwNjgxZSIsICJ0b0BleGFtcGxlLmNvbSIsICI3MG'
@ -332,14 +334,16 @@ class TestMailgun(TransactionCase):
@mock.patch(_packagepath + '.models.res_partner.requests') @mock.patch(_packagepath + '.models.res_partner.requests')
def test_email_validity(self, mock_request): def test_email_validity(self, mock_request):
self.partner.email_bounced = False self.partner.email_bounced = False
self.partner.email = 'info@tecnativa.com'
mock_request.get.return_value.apparent_encoding = 'ascii' mock_request.get.return_value.apparent_encoding = 'ascii'
mock_request.get.return_value.status_code = 200 mock_request.get.return_value.status_code = 200
mock_request.get.return_value.content = json.dumps({ mock_request.get.return_value.content = json.dumps({
'is_valid': True, 'is_valid': True,
'mailbox_verification': 'true', 'mailbox_verification': 'true',
}, ensure_ascii=True) }, ensure_ascii=True)
self.partner.check_email_validity() # Trigger email auto validation in partner
self.env['ir.config_parameter'].set_param(
'mailgun.auto_check_partner_email', 'True')
self.partner.email = 'info@tecnativa.com'
self.assertFalse(self.partner.email_bounced) self.assertFalse(self.partner.email_bounced)
self.partner.email = 'xoxoxoxo@tecnativa.com' self.partner.email = 'xoxoxoxo@tecnativa.com'
# Not a valid mailbox # Not a valid mailbox