Changeset - fdde16d7cea0
[Not reviewed]
default
0 6 0
Mads Kiilerich - 6 years ago 2020-02-29 16:06:30
mads@kiilerich.com
Grafted from: bc10216c34c7
celery: fix send_email to work with JSON encoding (Issue #363)

Long time ago, c935bcaf7086 introduced an optional User object parameter to the
send_email task and used the computed full_name_or_username property. Due to the
magic of pickle, that also worked when using Celery to run the task async.

Now, Celery 4 changed the default encoding from Pickle to JSON, which we
anticipated in e539db6cc0da. That broke send_email in some cases, for example
when a user comments on another user's changeset.

Fixed by passing the "From" name as string instead of passing the whole User
object.

Thanks to vyom for reporting.
6 files changed with 34 insertions and 32 deletions:
0 comments (0 inline, 0 general)
kallithea/lib/celerylib/tasks.py
Show inline comments
 
@@ -233,7 +233,7 @@ def get_commits_stats(repo_name, ts_min_
 

	
 
@celerylib.task
 
@celerylib.dbsession
 
def send_email(recipients, subject, body='', html_body='', headers=None, author=None):
 
def send_email(recipients, subject, body='', html_body='', headers=None, from_name=None):
 
    """
 
    Sends an email with defined parameters from the .ini files.
 

	
 
@@ -243,7 +243,8 @@ def send_email(recipients, subject, body
 
    :param body: body of the mail
 
    :param html_body: html version of body
 
    :param headers: dictionary of prepopulated e-mail headers
 
    :param author: User object of the author of this mail, if known and relevant
 
    :param from_name: full name to be used as sender of this mail - often a
 
    .full_name_or_username value
 
    """
 
    assert isinstance(recipients, list), recipients
 
    if headers is None:
 
@@ -275,13 +276,13 @@ def send_email(recipients, subject, body
 
    # SMTP sender
 
    envelope_from = email_config.get('app_email_from', 'Kallithea')
 
    # 'From' header
 
    if author is not None:
 
        # set From header based on author but with a generic e-mail address
 
    if from_name is not None:
 
        # set From header based on from_name but with a generic e-mail address
 
        # In case app_email_from is in "Some Name <e-mail>" format, we first
 
        # extract the e-mail address.
 
        envelope_addr = author_email(envelope_from)
 
        headers['From'] = '"%s" <%s>' % (
 
            email.utils.quote('%s (no-reply)' % author.full_name_or_username),
 
            email.utils.quote('%s (no-reply)' % from_name),
 
            envelope_addr)
 

	
 
    user = email_config.get('smtp_username')
kallithea/model/notification.py
Show inline comments
 
@@ -132,7 +132,8 @@ class NotificationModel(object):
 
        # send email with notification to all other participants
 
        for rec in rec_objs:
 
            tasks.send_email([rec.email], email_subject, email_txt_body,
 
                     email_html_body, headers, author=created_by_obj)
 
                     email_html_body, headers,
 
                     from_name=created_by_obj.full_name_or_username)
 

	
 

	
 
class EmailNotificationModel(object):
kallithea/tests/functional/test_login.py
Show inline comments
 
@@ -410,7 +410,7 @@ class TestLoginController(base.TestContr
 
            User.get_by_username(username), timestamp, self.session_csrf_secret_token())
 

	
 
        collected = []
 
        def mock_send_email(recipients, subject, body='', html_body='', headers=None, author=None):
 
        def mock_send_email(recipients, subject, body='', html_body='', headers=None, from_name=None):
 
            collected.append((recipients, subject, body, html_body))
 

	
 
        with mock.patch.object(kallithea.lib.celerylib.tasks, 'send_email', mock_send_email), \
kallithea/tests/models/test_dump_html_mails.ref.html
Show inline comments
 
@@ -5,7 +5,7 @@
 
<hr/>
 
<h1>cs_comment, is_mention=False, status_change=None</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Comment] repo/name changeset cafe1234 "This changeset did something cl..." on brunch
 
</pre>
 
@@ -164,7 +164,7 @@ View Comment: http://comment.org
 
<hr/>
 
<h1>cs_comment, is_mention=True, status_change=None</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Comment] repo/name changeset cafe1234 "This changeset did something cl..." on brunch
 
</pre>
 
@@ -323,7 +323,7 @@ View Comment: http://comment.org
 
<hr/>
 
<h1>cs_comment, is_mention=False, status_change='Approved'</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Approved: Comment] repo/name changeset cafe1234 "This changeset did something cl..." on brunch
 
</pre>
 
@@ -500,7 +500,7 @@ View Comment: http://comment.org
 
<hr/>
 
<h1>cs_comment, is_mention=True, status_change='Approved'</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Approved: Comment] repo/name changeset cafe1234 "This changeset did something cl..." on brunch
 
</pre>
 
@@ -677,7 +677,7 @@ View Comment: http://comment.org
 
<hr/>
 
<h1>message</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: Test Message
 
</pre>
 
@@ -748,7 +748,7 @@ Subject: Test Message
 
<hr/>
 
<h1>registration</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: New user newbie registered
 
</pre>
 
@@ -881,7 +881,7 @@ View User Profile: http://newbie.org
 
<hr/>
 
<h1>pull_request, is_mention=False</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Review] repo/name PR #7 "The Title" from devbranch by u2
 
</pre>
 
@@ -1072,7 +1072,7 @@ View Pull Request: http://pr.org/7
 
<hr/>
 
<h1>pull_request, is_mention=True</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Review] repo/name PR #7 "The Title" from devbranch by u2
 
</pre>
 
@@ -1263,7 +1263,7 @@ View Pull Request: http://pr.org/7
 
<hr/>
 
<h1>pull_request_comment, is_mention=False, status_change=None, closing_pr=False</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Comment] repo/name PR #7 "The Title" from devbranch by u2
 
</pre>
 
@@ -1430,7 +1430,7 @@ View Comment: http://pr.org/comment
 
<hr/>
 
<h1>pull_request_comment, is_mention=True, status_change=None, closing_pr=False</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Comment] repo/name PR #7 "The Title" from devbranch by u2
 
</pre>
 
@@ -1597,7 +1597,7 @@ View Comment: http://pr.org/comment
 
<hr/>
 
<h1>pull_request_comment, is_mention=False, status_change='Under Review', closing_pr=False</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Under Review: Comment] repo/name PR #7 "The Title" from devbranch by u2
 
</pre>
 
@@ -1782,7 +1782,7 @@ View Comment: http://pr.org/comment
 
<hr/>
 
<h1>pull_request_comment, is_mention=True, status_change='Under Review', closing_pr=False</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Under Review: Comment] repo/name PR #7 "The Title" from devbranch by u2
 
</pre>
 
@@ -1967,7 +1967,7 @@ View Comment: http://pr.org/comment
 
<hr/>
 
<h1>pull_request_comment, is_mention=False, status_change=None, closing_pr=True</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Closing: Comment] repo/name PR #7 "The Title" from devbranch by u2
 
</pre>
 
@@ -2151,7 +2151,7 @@ View Comment: http://pr.org/comment
 
<hr/>
 
<h1>pull_request_comment, is_mention=True, status_change=None, closing_pr=True</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Closing: Comment] repo/name PR #7 "The Title" from devbranch by u2
 
</pre>
 
@@ -2335,7 +2335,7 @@ View Comment: http://pr.org/comment
 
<hr/>
 
<h1>pull_request_comment, is_mention=False, status_change='Under Review', closing_pr=True</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Under Review, Closing: Comment] repo/name PR #7 "The Title" from devbranch by u2
 
</pre>
 
@@ -2525,7 +2525,7 @@ View Comment: http://pr.org/comment
 
<hr/>
 
<h1>pull_request_comment, is_mention=True, status_change='Under Review', closing_pr=True</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: u2@example.com
 
Subject: [Under Review, Closing: Comment] repo/name PR #7 "The Title" from devbranch by u2
 
</pre>
 
@@ -2715,7 +2715,7 @@ View Comment: http://pr.org/comment
 
<hr/>
 
<h1>TYPE_PASSWORD_RESET</h1>
 
<pre>
 
From: u1
 
From: u1 u1 <name@example.com>
 
To: john@doe.com
 
Subject: Password reset link
 
</pre>
kallithea/tests/models/test_notifications.py
Show inline comments
 
@@ -43,12 +43,12 @@ class TestNotifications(base.TestControl
 
        with test_context(self.app):
 
            usrs = [self.u1, self.u2]
 

	
 
            def send_email(recipients, subject, body='', html_body='', headers=None, author=None):
 
            def send_email(recipients, subject, body='', html_body='', headers=None, from_name=None):
 
                assert recipients == ['u2@example.com']
 
                assert subject == 'Test Message'
 
                assert body == "hi there"
 
                assert '>hi there<' in html_body
 
                assert author.username == 'u1'
 
                assert from_name == 'u1 u1'
 
            with mock.patch.object(kallithea.lib.celerylib.tasks, 'send_email', send_email):
 
                NotificationModel().create(created_by=self.u1,
 
                                                   subject='subj', body='hi there',
 
@@ -59,11 +59,11 @@ class TestNotifications(base.TestControl
 
        # Exercise all notification types and dump them to one big html file
 
        l = []
 

	
 
        def send_email(recipients, subject, body='', html_body='', headers=None, author=None):
 
        def send_email(recipients, subject, body='', html_body='', headers=None, from_name=None):
 
            l.append('<hr/>\n')
 
            l.append('<h1>%s</h1>\n' % desc) # desc is from outer scope
 
            l.append('<pre>\n')
 
            l.append('From: %s\n' % author.username)
 
            l.append('From: %s <name@example.com>\n' % from_name)
 
            l.append('To: %s\n' % ' '.join(recipients))
 
            l.append('Subject: %s\n' % subject)
 
            l.append('</pre>\n')
 
@@ -159,7 +159,7 @@ class TestNotifications(base.TestControl
 
                    "Password reset link",
 
                    EmailNotificationModel().get_email_tmpl(EmailNotificationModel.TYPE_PASSWORD_RESET, 'txt', **kwargs),
 
                    EmailNotificationModel().get_email_tmpl(EmailNotificationModel.TYPE_PASSWORD_RESET, 'html', **kwargs),
 
                    author=User.get(self.u1))
 
                    from_name=User.get(self.u1).full_name_or_username)
 

	
 
        out = '<!doctype html>\n<html lang="en">\n<head><title>Notifications</title><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head>\n<body>\n%s\n</body>\n</html>\n' % \
 
            re.sub(r'<(/?(?:!doctype|html|head|title|meta|body)\b[^>]*)>', r'<!--\1-->', ''.join(l))
kallithea/tests/other/test_mail.py
Show inline comments
 
@@ -135,7 +135,7 @@ class TestMail(base.TestController):
 
            'app_email_from': envelope_from,
 
        }
 
        with mock.patch('kallithea.lib.celerylib.tasks.config', config_mock):
 
            kallithea.lib.celerylib.tasks.send_email(recipients, subject, body, html_body, author=author)
 
            kallithea.lib.celerylib.tasks.send_email(recipients, subject, body, html_body, from_name=author.full_name_or_username)
 

	
 
        assert smtplib_mock.lastdest == set(recipients)
 
        assert smtplib_mock.lastsender == envelope_from
 
@@ -159,7 +159,7 @@ class TestMail(base.TestController):
 
            'app_email_from': envelope_from,
 
        }
 
        with mock.patch('kallithea.lib.celerylib.tasks.config', config_mock):
 
            kallithea.lib.celerylib.tasks.send_email(recipients, subject, body, html_body, author=author)
 
            kallithea.lib.celerylib.tasks.send_email(recipients, subject, body, html_body, from_name=author.full_name_or_username)
 

	
 
        assert smtplib_mock.lastdest == set(recipients)
 
        assert smtplib_mock.lastsender == envelope_from
 
@@ -184,7 +184,7 @@ class TestMail(base.TestController):
 
        }
 
        with mock.patch('kallithea.lib.celerylib.tasks.config', config_mock):
 
            kallithea.lib.celerylib.tasks.send_email(recipients, subject, body, html_body,
 
                                                     author=author, headers=headers)
 
                                                     from_name=author.full_name_or_username, headers=headers)
 

	
 
        assert smtplib_mock.lastdest == set(recipients)
 
        assert smtplib_mock.lastsender == envelope_from
0 comments (0 inline, 0 general)