[MIG] mail_tracking_mailgun: Migration to 11.0

This commit is contained in:
David 2018-05-08 18:24:36 +02:00 committed by nicolas
parent 02d0c30d3c
commit ccee1257d8
9 changed files with 87 additions and 97 deletions

View File

@ -47,11 +47,12 @@ You can also config partner email autocheck with this system parameter:
Usage
=====
In your mail tracking status screens (explained on module *mail_tracking*), you will
see a more accurate information, like the 'Received' or 'Bounced' status, which are
not usually detected by normal SMTP servers.
In your mail tracking status screens (explained on module *mail_tracking*), you
will see a more accurate information, like the 'Received' or 'Bounced' status,
which are not usually detected by normal SMTP servers.
It's also possible to make some checks to the partner's email addresses against the Mailgun API:
It's also possible to make some checks to the partner's email addresses against
the Mailgun API:
- Check if the partner's email is in Mailgun's bounced list.
- Check the validity of the partner's mailbox.
@ -64,7 +65,7 @@ short lifespan, so after 24h they won't be recoverable.
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/205/9.0
:target: https://runbot.odoo-community.org/runbot/205/11.0
Known issues / Roadmap
======================

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import models

View File

@ -1,15 +1,14 @@
# -*- coding: utf-8 -*-
# Copyright 2016 Tecnativa - Antonio Espinosa
# Copyright 2016 Tecnativa - Carlos Dauden
# Copyright 2017 Tecnativa - Pedro M. Baeza
# Copyright 2017 Tecnativa - David Vidal
# Copyright 2017-18 Tecnativa - David Vidal
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Mail tracking for Mailgun",
"summary": "Mail tracking and Mailgun webhooks integration",
"version": "10.0.1.1.3",
"version": "11.0.1.0.2",
"category": "Social Network",
"website": "https://odoo-community.org/",
"website": "https://github.com/OCA/social",
"author": "Tecnativa, "
"Odoo Community Association (OCA)",
"license": "AGPL-3",

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import ir_mail_server

View File

@ -5,7 +5,6 @@
import hashlib
import hmac
import json
import requests
from datetime import datetime
from odoo import _, api, fields, models
@ -60,8 +59,8 @@ class MailTrackingEmail(models.Model):
def _mailgun_signature(self, api_key, timestamp, token):
return hmac.new(
key=str(api_key),
msg='{}{}'.format(str(timestamp), str(token)),
key=bytes(api_key, 'utf-8'),
msg=bytes('{}{}'.format(str(timestamp), str(token)), 'utf-8'),
digestmod=hashlib.sha256).hexdigest()
def _mailgun_values(self):
@ -115,7 +114,7 @@ class MailTrackingEmail(models.Model):
ts = event.get('timestamp', False)
try:
ts = float(ts)
except:
except Exception:
ts = False
if ts:
dt = datetime.utcfromtimestamp(ts)
@ -135,7 +134,7 @@ class MailTrackingEmail(models.Model):
'ua_type': 'client-type',
'url': 'url',
}
for k, v in mapping.iteritems():
for k, v in mapping.items():
if event.get(v, False):
metadata[k] = event[v]
# Special field mapping
@ -238,7 +237,7 @@ class MailTrackingEmail(models.Model):
if not res or res.status_code != 200:
raise ValidationError(_(
"Couldn't retrieve Mailgun information"))
content = json.loads(res.content, res.apparent_encoding)
content = res.json()
if "items" not in content:
raise ValidationError(_("Event information not longer stored"))
for item in content["items"]:

View File

@ -6,7 +6,6 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import requests
import json
from odoo import _, api, models
from odoo.exceptions import UserError
@ -57,7 +56,7 @@ class ResPartner(models.Model):
raise UserError(_(
'Error %s trying to '
'check mail' % res.status_code or 'of connection'))
content = json.loads(res.content, res.apparent_encoding)
content = res.json()
if 'mailbox_verification' not in content:
if not self.env.context.get('mailgun_auto_check'):
raise UserError(

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import test_mailgun

View File

@ -7,7 +7,6 @@ from odoo.tools import mute_logger
from odoo.tests.common import TransactionCase
from odoo.exceptions import UserError, ValidationError
import mock
import json
_packagepath = 'odoo.addons.mail_tracking_mailgun'
@ -29,12 +28,12 @@ class TestMailgun(TransactionCase):
def setUp(self):
super(TestMailgun, self).setUp()
self.recipient = u'to@example.com'
self.recipient = 'to@example.com'
self.mail, self.tracking_email = self.mail_send()
self.api_key = u'key-12345678901234567890123456789012'
self.domain = u'example.com'
self.token = u'f1349299097a51b9a7d886fcb5c2735b426ba200ada6e9e149'
self.timestamp = u'1471021089'
self.api_key = 'key-12345678901234567890123456789012'
self.domain = 'example.com'
self.token = 'f1349299097a51b9a7d886fcb5c2735b426ba200ada6e9e149'
self.timestamp = '1471021089'
self.signature = ('4fb6d4dbbe10ce5d620265dcd7a3c0b8'
'ca0dede1433103891bc1ae4086e9d5b2')
self.env['ir.config_parameter'].set_param(
@ -46,17 +45,17 @@ class TestMailgun(TransactionCase):
self.env['ir.config_parameter'].set_param(
'mailgun.auto_check_partner_email', '')
self.event = {
'Message-Id': u'<xxx.xxx.xxx-openerp-xxx-res.partner@test_db>',
'X-Mailgun-Sid': u'WyIwNjgxZSIsICJ0b0BleGFtcGxlLmNvbSIsICI3MG'
'Message-Id': '<xxx.xxx.xxx-openerp-xxx-res.partner@test_db>',
'X-Mailgun-Sid': 'WyIwNjgxZSIsICJ0b0BleGFtcGxlLmNvbSIsICI3MG'
'I0MWYiXQ==',
'token': self.token,
'timestamp': self.timestamp,
'signature': self.signature,
'domain': u'example.com',
'message-headers': u'[]',
'domain': 'example.com',
'message-headers': '[]',
'recipient': self.recipient,
'odoo_db': self.env.cr.dbname,
'tracking_email_id': u'%s' % self.tracking_email.id
'tracking_email_id': '%s' % self.tracking_email.id
}
self.metadata = {
'ip': '127.0.0.1',
@ -110,8 +109,8 @@ class TestMailgun(TransactionCase):
'.mail_tracking_email')
def test_bad_signature(self):
self.event.update({
'event': u'delivered',
'signature': u'bad_signature',
'event': 'delivered',
'signature': 'bad_signature',
})
response = self.env['mail.tracking.email'].event_process(
None, self.event, self.metadata)
@ -121,7 +120,7 @@ class TestMailgun(TransactionCase):
'.mail_tracking_email')
def test_bad_event_type(self):
self.event.update({
'event': u'bad_event',
'event': 'bad_event',
})
response = self.env['mail.tracking.email'].event_process(
None, self.event, self.metadata)
@ -131,19 +130,19 @@ class TestMailgun(TransactionCase):
'.mail_tracking_email')
def test_bad_db(self):
self.event.update({
'event': u'delivered',
'odoo_db': u'bad_db',
'event': 'delivered',
'odoo_db': 'bad_db',
})
response = self.env['mail.tracking.email'].event_process(
None, self.event, self.metadata)
self.assertEqual('ERROR: Invalid DB', response)
def test_bad_ts(self):
timestamp = u'7a' # Now time will be used instead
timestamp = '7a' # Now time will be used instead
signature = ('06cc05680f6e8110e59b41152b2d1c0f'
'1045d755ef2880ff922344325c89a6d4')
self.event.update({
'event': u'delivered',
'event': 'delivered',
'timestamp': timestamp,
'signature': signature,
})
@ -155,8 +154,8 @@ class TestMailgun(TransactionCase):
'.mail_tracking_email')
def test_tracking_not_found(self):
self.event.update({
'event': u'delivered',
'tracking_email_id': u'bad_id',
'event': 'delivered',
'tracking_email_id': 'bad_id',
})
response = self.env['mail.tracking.email'].event_process(
None, self.event, self.metadata)
@ -165,7 +164,7 @@ class TestMailgun(TransactionCase):
# https://documentation.mailgun.com/user_manual.html#tracking-deliveries
def test_event_delivered(self):
self.event.update({
'event': u'delivered',
'event': 'delivered',
})
response = self.env['mail.tracking.email'].event_process(
None, self.event, self.metadata)
@ -176,20 +175,20 @@ class TestMailgun(TransactionCase):
# https://documentation.mailgun.com/user_manual.html#tracking-opens
def test_event_opened(self):
ip = u'127.0.0.1'
user_agent = u'Odoo Test/8.0 Gecko Firefox/11.0'
os_family = u'Linux'
ua_family = u'Firefox'
ua_type = u'browser'
ip = '127.0.0.1'
user_agent = 'Odoo Test/8.0 Gecko Firefox/11.0'
os_family = 'Linux'
ua_family = 'Firefox'
ua_type = 'browser'
self.event.update({
'event': u'opened',
'city': u'Mountain View',
'country': u'US',
'region': u'CA',
'event': 'opened',
'city': 'Mountain View',
'country': 'US',
'region': 'CA',
'client-name': ua_family,
'client-os': os_family,
'client-type': ua_type,
'device-type': u'desktop',
'device-type': 'desktop',
'ip': ip,
'user-agent': user_agent,
})
@ -209,21 +208,21 @@ class TestMailgun(TransactionCase):
# https://documentation.mailgun.com/user_manual.html#tracking-clicks
def test_event_clicked(self):
ip = u'127.0.0.1'
user_agent = u'Odoo Test/8.0 Gecko Firefox/11.0'
os_family = u'Linux'
ua_family = u'Firefox'
ua_type = u'browser'
url = u'https://odoo-community.org'
ip = '127.0.0.1'
user_agent = 'Odoo Test/8.0 Gecko Firefox/11.0'
os_family = 'Linux'
ua_family = 'Firefox'
ua_type = 'browser'
url = 'https://odoo-community.org'
self.event.update({
'event': u'clicked',
'city': u'Mountain View',
'country': u'US',
'region': u'CA',
'event': 'clicked',
'city': 'Mountain View',
'country': 'US',
'region': 'CA',
'client-name': ua_family,
'client-os': os_family,
'client-type': ua_type,
'device-type': u'tablet',
'device-type': 'tablet',
'ip': ip,
'user-agent': user_agent,
'url': url,
@ -244,20 +243,20 @@ class TestMailgun(TransactionCase):
# https://documentation.mailgun.com/user_manual.html#tracking-unsubscribes
def test_event_unsubscribed(self):
ip = u'127.0.0.1'
user_agent = u'Odoo Test/8.0 Gecko Firefox/11.0'
os_family = u'Linux'
ua_family = u'Firefox'
ua_type = u'browser'
ip = '127.0.0.1'
user_agent = 'Odoo Test/8.0 Gecko Firefox/11.0'
os_family = 'Linux'
ua_family = 'Firefox'
ua_type = 'browser'
self.event.update({
'event': u'unsubscribed',
'city': u'Mountain View',
'country': u'US',
'region': u'CA',
'event': 'unsubscribed',
'city': 'Mountain View',
'country': 'US',
'region': 'CA',
'client-name': ua_family,
'client-os': os_family,
'client-type': ua_type,
'device-type': u'mobile',
'device-type': 'mobile',
'ip': ip,
'user-agent': user_agent,
})
@ -278,7 +277,7 @@ class TestMailgun(TransactionCase):
# user_manual.html#tracking-spam-complaints
def test_event_complained(self):
self.event.update({
'event': u'complained',
'event': 'complained',
})
response = self.env['mail.tracking.email'].event_process(
None, self.event, self.metadata)
@ -290,12 +289,12 @@ class TestMailgun(TransactionCase):
# https://documentation.mailgun.com/user_manual.html#tracking-bounces
def test_event_bounced(self):
code = u'550'
error = (u"5.1.1 The email account does not exist.\n"
code = '550'
error = ("5.1.1 The email account does not exist.\n"
"5.1.1 double-checking the recipient's email address")
notification = u"Please, check recipient's email address"
notification = "Please, check recipient's email address"
self.event.update({
'event': u'bounced',
'event': 'bounced',
'code': code,
'error': error,
'notification': notification,
@ -312,11 +311,11 @@ class TestMailgun(TransactionCase):
# https://documentation.mailgun.com/user_manual.html#tracking-failures
def test_event_dropped(self):
reason = u'hardfail'
code = u'605'
description = u'Not delivering to previously bounced address'
reason = 'hardfail'
code = '605'
description = 'Not delivering to previously bounced address'
self.event.update({
'event': u'dropped',
'event': 'dropped',
'reason': reason,
'code': code,
'description': description,
@ -336,10 +335,10 @@ class TestMailgun(TransactionCase):
self.partner.email_bounced = False
mock_request.get.return_value.apparent_encoding = 'ascii'
mock_request.get.return_value.status_code = 200
mock_request.get.return_value.content = json.dumps({
mock_request.get.return_value.json.return_value = {
'is_valid': True,
'mailbox_verification': 'true',
}, ensure_ascii=True)
}
# Trigger email auto validation in partner
self.env['ir.config_parameter'].set_param(
'mailgun.auto_check_partner_email', 'True')
@ -347,24 +346,24 @@ class TestMailgun(TransactionCase):
self.assertFalse(self.partner.email_bounced)
self.partner.email = 'xoxoxoxo@tecnativa.com'
# Not a valid mailbox
mock_request.get.return_value.content = json.dumps({
mock_request.get.return_value.json.return_value = {
'is_valid': True,
'mailbox_verification': 'false',
}, ensure_ascii=True)
}
with self.assertRaises(UserError):
self.partner.check_email_validity()
# Not a valid mail address
mock_request.get.return_value.content = json.dumps({
mock_request.get.return_value.json.return_value = {
'is_valid': False,
'mailbox_verification': 'false',
}, ensure_ascii=True)
}
with self.assertRaises(UserError):
self.partner.check_email_validity()
# Unable to fully validate
mock_request.get.return_value.content = json.dumps({
mock_request.get.return_value.json.return_value = {
'is_valid': True,
'mailbox_verification': 'unknown',
}, ensure_ascii=True)
}
with self.assertRaises(UserError):
self.partner.check_email_validity()
self.assertTrue(self.partner.email_bounced)
@ -402,9 +401,7 @@ class TestMailgun(TransactionCase):
@mock.patch(_packagepath + '.models.mail_tracking_email.requests')
def test_manual_check(self, mock_request):
mock_request.get.return_value.content = json.dumps(self.response,
ensure_ascii=True)
mock_request.get.return_value.apparent_encoding = 'ascii'
mock_request.get.return_value.json.return_value = self.response
mock_request.get.return_value.status_code = 200
self.tracking_email.action_manual_check_mailgun()
event = self.env['mail.tracking.event'].search(
@ -417,8 +414,6 @@ class TestMailgun(TransactionCase):
with self.assertRaises(ValidationError):
self.tracking_email.action_manual_check_mailgun()
mock_request.get.return_value.status_code = 200
mock_request.get.return_value.content = json.dumps('{}',
ensure_ascii=True)
mock_request.get.return_value.apparent_encoding = 'ascii'
mock_request.get.return_value.json.return_value = {}
with self.assertRaises(ValidationError):
self.tracking_email.action_manual_check_mailgun()

View File

@ -13,14 +13,14 @@
<field name="email_bounced" position="after">
<label for="check_email_bounced" string="Mailgun"
attrs="{'invisible': [('email', '=', False)]}"/>
<group col="3" name="mailgun_buttons" attrs="{'invisible': [('email', '=', False)]}">
<div name="mailgun_buttons" attrs="{'invisible': [('email', '=', False)]}">
<button name="check_email_bounced" type="object" string="Check Mailgun" class="oe_link"/>
<button name="check_email_validity" type="object" string="Check email validity" class="oe_link"/>
<button name="force_set_bounced" type="object" string="Set Bounced" class="oe_link"
attrs="{'invisible': [('email_bounced', '=', True)]}"/>
<button name="force_unset_bounced" type="object" string="Unset Bounced" class="oe_link"
attrs="{'invisible': [('email_bounced', '=', False)]}"/>
</group>
</div>
</field>
</field>
</record>