Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[14.0] google_gmail: avoid SMTPServerDisconnected errors #1257

Open
wants to merge 2 commits into
base: 14.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion addons/google_gmail/models/google_gmail_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,14 @@ def _generate_oauth2_string(self, user, refresh_token):
:return: The SASL argument for the OAuth2 mechanism.
"""
self.ensure_one()
# token life is around 1 hour, add 5s threshold to gives
# time to contact google servers and avoid clock synchronization
# problems
threshold_seconds = 5
now_timestamp = int(time.time())
if not self.google_gmail_access_token \
or not self.google_gmail_access_token_expiration \
or self.google_gmail_access_token_expiration < now_timestamp:
or self.google_gmail_access_token_expiration - threshold_seconds < now_timestamp:

access_token, expiration = self._fetch_gmail_access_token(self.google_gmail_refresh_token)

Expand Down
1 change: 1 addition & 0 deletions addons/google_gmail/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_google_gmail
50 changes: 50 additions & 0 deletions addons/google_gmail/tests/test_google_gmail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from odoo.tests.common import SavepointCase
from unittest import mock
import time

class TestIrMailServer(SavepointCase):

def test_generate_oauth2_string_token_valid(self):
now_timestamp = int(time.time())
mail_server = self.env["ir.mail_server"].new(
{
"google_gmail_access_token": "fake_access_token",
"google_gmail_access_token_expiration": now_timestamp + 10,
}
)
oauth2_string = mail_server._generate_oauth2_string("user-account", "refresh-token")

self.assertIn("fake_access_token", oauth2_string)


def test_generate_oauth2_string_token_expire_in_less_than_5s(self):
now_timestamp = int(time.time())
mail_server = self.env["ir.mail_server"].new(
{
"google_gmail_access_token": "fake_access_token",
"google_gmail_access_token_expiration": now_timestamp + 2,
}
)
with mock.patch(
"odoo.addons.google_gmail.models.google_gmail_mixin.GoogleGmailMixin._fetch_gmail_access_token",
return_value=("new-access-token", now_timestamp + 60*60)
):
oauth2_string = mail_server._generate_oauth2_string("user-account", "refresh-token")

self.assertIn("new-access-token", oauth2_string)

def test_generate_oauth2_string_token_expired(self):
now_timestamp = int(time.time())
mail_server = self.env["ir.mail_server"].new(
{
"google_gmail_access_token": "fake_access_token",
"google_gmail_access_token_expiration": now_timestamp - 2,
}
)
with mock.patch(
"odoo.addons.google_gmail.models.google_gmail_mixin.GoogleGmailMixin._fetch_gmail_access_token",
return_value=("new-access-token", now_timestamp + 60*60)
):
oauth2_string = mail_server._generate_oauth2_string("user-account", "refresh-token")

self.assertIn("new-access-token", oauth2_string)
7 changes: 6 additions & 1 deletion addons/mail/models/mail_mail.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,12 @@ def send(self, auto_commit=False, raise_exception=False):
len(batch_ids), server_id)
finally:
if smtp_session:
smtp_session.quit()
try:
smtp_session.quit()
except smtplib.SMTPServerDisconnected:
_logger.info(
"Ignoring SMTPServerDisconnected while trying to quit non open session"
)

def _send(self, auto_commit=False, raise_exception=False, smtp_session=None):
IrMailServer = self.env['ir.mail_server']
Expand Down
1 change: 1 addition & 0 deletions addons/mail/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
from . import test_mail_render
from . import test_res_partner
from . import test_update_notification
from . import test_mail_mail
17 changes: 17 additions & 0 deletions addons/mail/tests/test_mail_mail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from odoo.tests import SavepointCase
from unittest import mock
import smtplib


class MailCase(SavepointCase):

def test_mail_send_missing_not_connected(self):
"""This assume while calling self.env['ir.mail_server'].connect() it return
an disconnected smtp_session which is a non falsy object value"""

not_connected = smtplib.SMTP(local_hostname="fake-hostname.com", port=9999, timeout=1)
mail = self.env["mail.mail"].new({})
with mock.patch("odoo.addons.base.models.ir_mail_server.IrMailServer.connect", return_value=not_connected):
mail.send()
# if we get here SMTPServerDisconnected was not raised
self.assertEqual(mail.state, "outgoing")