From ddb1c3bf6335621f234041cab3a887f7794118d5 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 15 Feb 2018 19:46:21 +0100 Subject: [PATCH 1/2] [FIX] mail_tracking_mailgun: manual sync gets events from other recipients --- mail_tracking_mailgun/__manifest__.py | 2 +- .../models/mail_tracking_email.py | 64 ++++++++++++++++++- .../models/mail_tracking_event.py | 2 +- mail_tracking_mailgun/tests/test_mailgun.py | 3 +- 4 files changed, 67 insertions(+), 4 deletions(-) diff --git a/mail_tracking_mailgun/__manifest__.py b/mail_tracking_mailgun/__manifest__.py index 0ebb5b20..419b1ea6 100644 --- a/mail_tracking_mailgun/__manifest__.py +++ b/mail_tracking_mailgun/__manifest__.py @@ -7,7 +7,7 @@ { "name": "Mail tracking for Mailgun", "summary": "Mail tracking and Mailgun webhooks integration", - "version": "10.0.1.1.2", + "version": "10.0.1.1.3", "category": "Social Network", "website": "https://odoo-community.org/", "author": "Tecnativa, " diff --git a/mail_tracking_mailgun/models/mail_tracking_email.py b/mail_tracking_mailgun/models/mail_tracking_email.py index b017d4e9..1f993872 100644 --- a/mail_tracking_mailgun/models/mail_tracking_email.py +++ b/mail_tracking_mailgun/models/mail_tracking_email.py @@ -10,6 +10,7 @@ import requests from datetime import datetime from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError +from odoo.tools import email_split import logging _logger = logging.getLogger(__name__) @@ -242,9 +243,70 @@ class MailTrackingEmail(models.Model): raise ValidationError(_("Event information not longer stored")) for item in content["items"]: if not self.env['mail.tracking.event'].search( - [('mailgun_id', '=', item["id"])]): + # mailgun event hasn't been synced and recipient is the same as + # in the evaluated tracking. We use email_split since tracking + # recipient could come in format: "example" + [('mailgun_id', '=', item["id"])]) and ( + item.get("recipient", "") == + email_split(tracking.recipient)[0]): mapped_event_type = self._mailgun_event_type_mapping.get( item["event"], item["event"]) metadata = self._mailgun_metadata( mapped_event_type, item, {}) 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 diff --git a/mail_tracking_mailgun/models/mail_tracking_event.py b/mail_tracking_mailgun/models/mail_tracking_event.py index de3fe3ed..278210e9 100644 --- a/mail_tracking_mailgun/models/mail_tracking_event.py +++ b/mail_tracking_mailgun/models/mail_tracking_event.py @@ -2,7 +2,7 @@ # Copyright 2017 Tecnativa - David Vidal # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp import models, fields +from odoo import models, fields class MailTrackingEvent(models.Model): diff --git a/mail_tracking_mailgun/tests/test_mailgun.py b/mail_tracking_mailgun/tests/test_mailgun.py index d15888c9..c2dc7212 100644 --- a/mail_tracking_mailgun/tests/test_mailgun.py +++ b/mail_tracking_mailgun/tests/test_mailgun.py @@ -79,7 +79,8 @@ class TestMailgun(TransactionCase): "subject": "This is a test" }, }, - "event": "delivered" + "event": "delivered", + "recipient": "to@example.com", }] } From e20daefaceae9c11bc88db79cbb385e555448ea8 Mon Sep 17 00:00:00 2001 From: David Date: Fri, 16 Feb 2018 13:15:32 +0100 Subject: [PATCH 2/2] [IMP] mail_tracking_mailgun: validation auto check - Configurable partner email auto check. --- mail_tracking_mailgun/README.rst | 4 ++ .../models/mail_tracking_email.py | 58 +------------------ mail_tracking_mailgun/models/res_partner.py | 55 +++++++++++++----- mail_tracking_mailgun/tests/test_mailgun.py | 8 ++- 4 files changed, 51 insertions(+), 74 deletions(-) diff --git a/mail_tracking_mailgun/README.rst b/mail_tracking_mailgun/README.rst index 7f377add..eb0ec7e4 100644 --- a/mail_tracking_mailgun/README.rst +++ b/mail_tracking_mailgun/README.rst @@ -40,6 +40,10 @@ parameters: validity you must config this parameter with your account Public Validation Key. +You can also config partner email autocheck with this system parameter: + +- `mailgun.auto_check_partner_email`: Set it to True. + Usage ===== diff --git a/mail_tracking_mailgun/models/mail_tracking_email.py b/mail_tracking_mailgun/models/mail_tracking_email.py index 1f993872..fa431dd2 100644 --- a/mail_tracking_mailgun/models/mail_tracking_email.py +++ b/mail_tracking_mailgun/models/mail_tracking_email.py @@ -242,10 +242,10 @@ class MailTrackingEmail(models.Model): if "items" not in content: raise ValidationError(_("Event information not longer stored")) 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 # in the evaluated tracking. We use email_split since tracking # recipient could come in format: "example" + if not self.env['mail.tracking.event'].search( [('mailgun_id', '=', item["id"])]) and ( item.get("recipient", "") == email_split(tracking.recipient)[0]): @@ -254,59 +254,3 @@ class MailTrackingEmail(models.Model): metadata = self._mailgun_metadata( mapped_event_type, item, {}) 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 diff --git a/mail_tracking_mailgun/models/res_partner.py b/mail_tracking_mailgun/models/res_partner.py index 77f284e2..c5f531b3 100644 --- a/mail_tracking_mailgun/models/res_partner.py +++ b/mail_tracking_mailgun/models/res_partner.py @@ -45,43 +45,50 @@ class ResPartner(models.Model): if not validation_key: raise UserError(_('You need to configure mailgun.validation_key' ' in order to be able to check mails validity')) - for partner in self: + for partner in self.filtered('email'): res = requests.get( "%s/address/validate" % api_url, auth=("api", validation_key), params={ "address": partner.email, "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(_( 'Error %s trying to ' 'check mail' % res.status_code or 'of connection')) content = json.loads(res.content, res.apparent_encoding) if 'mailbox_verification' not in content: - raise UserError( - _("Mailgun Error. Mailbox verification value wasn't" - " returned")) + if not self.env.context.get('mailgun_auto_check'): + raise UserError( + _("Mailgun Error. Mailbox verification value wasn't" + " returned")) # Not a valid address: API sets 'is_valid' as False # and 'mailbox_verification' as None if not content['is_valid']: partner.email_bounced = True - raise UserError( - _('%s is not a valid email address. Please check it ' - 'in order to avoid sending issues') % (partner.email)) + body = _('%s is not a valid email address. Please check it' + ' 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' # as a string with value 'false' if content['mailbox_verification'] == 'false': partner.email_bounced = True - raise UserError( - _('%s failed the mailbox verification. Please check it ' - 'in order to avoid sending issues') % (partner.email)) + body = _('%s failed the mailbox verification. Please check it' + ' 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 # 'mailbox_verification' as a string set to 'unknown' if content['mailbox_verification'] == 'unknown': - raise UserError( - _("%s couldn't be verified. Either the request couln't be " - "completed or the mailbox provider doesn't support " - "email verification") % (partner.email)) + if not self.env.context.get('mailgun_auto_check'): + raise UserError( + _("%s couldn't be verified. Either the request couln't" + " be completed or the mailbox provider doesn't " + "support email verification") % (partner.email)) @api.multi def check_email_bounced(self): @@ -133,3 +140,21 @@ class ResPartner(models.Model): auth=("api", api_key)) if res.status_code in (200, 404) and partner.email_bounced: 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) diff --git a/mail_tracking_mailgun/tests/test_mailgun.py b/mail_tracking_mailgun/tests/test_mailgun.py index c2dc7212..45cecd65 100644 --- a/mail_tracking_mailgun/tests/test_mailgun.py +++ b/mail_tracking_mailgun/tests/test_mailgun.py @@ -43,6 +43,8 @@ class TestMailgun(TransactionCase): 'mail.catchall.domain', self.domain) self.env['ir.config_parameter'].set_param( 'mailgun.validation_key', self.api_key) + self.env['ir.config_parameter'].set_param( + 'mailgun.auto_check_partner_email', '') self.event = { 'Message-Id': u'', 'X-Mailgun-Sid': u'WyIwNjgxZSIsICJ0b0BleGFtcGxlLmNvbSIsICI3MG' @@ -332,14 +334,16 @@ class TestMailgun(TransactionCase): @mock.patch(_packagepath + '.models.res_partner.requests') def test_email_validity(self, mock_request): self.partner.email_bounced = False - self.partner.email = 'info@tecnativa.com' mock_request.get.return_value.apparent_encoding = 'ascii' mock_request.get.return_value.status_code = 200 mock_request.get.return_value.content = json.dumps({ 'is_valid': True, 'mailbox_verification': '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.partner.email = 'xoxoxoxo@tecnativa.com' # Not a valid mailbox