Repository: allura Updated Branches: refs/heads/db/8132 [created] c620dab54
[#8132] thread messages by References: if missing In-Reply-To: Project: http://git-wip-us.apache.org/repos/asf/allura/repo Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/c620dab5 Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/c620dab5 Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/c620dab5 Branch: refs/heads/db/8132 Commit: c620dab5416011096baea72b6a74fddb461ec29b Parents: 67b553c Author: Dave Brondsema <d...@brondsema.net> Authored: Wed Oct 5 13:59:16 2016 -0500 Committer: Dave Brondsema <d...@brondsema.net> Committed: Wed Oct 5 14:10:00 2016 -0500 ---------------------------------------------------------------------- Allura/allura/app.py | 2 +- ForgeDiscussion/forgediscussion/model/forum.py | 10 +++-- .../tests/functional/test_forum.py | 41 +++++++++++++++++--- 3 files changed, 43 insertions(+), 10 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/allura/blob/c620dab5/Allura/allura/app.py ---------------------------------------------------------------------- diff --git a/Allura/allura/app.py b/Allura/allura/app.py index f155514..22190e6 100644 --- a/Allura/allura/app.py +++ b/Allura/allura/app.py @@ -703,7 +703,7 @@ class Application(object): post_id=message_id, artifact_id=message_id) return - # Handle duplicates + # Handle duplicates (from multipart mail messages) post = self.PostClass.query.get(_id=message_id) if post: log.info( http://git-wip-us.apache.org/repos/asf/allura/blob/c620dab5/ForgeDiscussion/forgediscussion/model/forum.py ---------------------------------------------------------------------- diff --git a/ForgeDiscussion/forgediscussion/model/forum.py b/ForgeDiscussion/forgediscussion/model/forum.py index 675efa2..15125dd 100644 --- a/ForgeDiscussion/forgediscussion/model/forum.py +++ b/ForgeDiscussion/forgediscussion/model/forum.py @@ -113,11 +113,15 @@ class Forum(M.Discussion): subject = '[no subject]' parent_id = None if data is not None: + prev_msg = None in_reply_to = data.get('in_reply_to') + references = data.get('references') if in_reply_to: - parent_id = in_reply_to[0].split('/')[-1] - else: - parent_id = None + prev_msg = in_reply_to[0] + elif references: + prev_msg = references[-1] + if prev_msg: + parent_id = prev_msg.split('/')[-1] message_id = data.get('message_id') or '' subject = data['headers'].get('Subject', subject) if parent_id is not None: http://git-wip-us.apache.org/repos/asf/allura/blob/c620dab5/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py ---------------------------------------------------------------------- diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py index 99df83b..99648ca 100644 --- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py +++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py @@ -25,6 +25,8 @@ from email.mime.image import MIMEImage from email.mime.multipart import MIMEMultipart import pkg_resources +import pymongo + from ming.odm import ThreadLocalORMSession from pylons import tmpl_context as c @@ -129,7 +131,11 @@ class TestForumEmail(TestController): M.artifact_orm_session.flush() -class TestForumAsync(TestController): +class TestForumMessageHandling(TestController): + ''' + Tests all the "handle_message" related logic, which is what inbound emails run through + ''' + def setUp(self): TestController.setUp(self) self.app.get('/discussion/') @@ -166,17 +172,37 @@ class TestForumAsync(TestController): posts = FM.ForumPost.query.find() assert_equal(posts.count(), 1) assert_equal(FM.ForumThread.query.get().num_replies, 1) - assert_equal(FM.ForumThread.query.get() - .first_post_id, 'test_re...@domain.net') + assert_equal(FM.ForumThread.query.get().first_post_id, 'test_re...@domain.net') post = posts.first() self._post('testforum', 'Test Reply', 'Nothing here, either', - message_id=post.thread.url() + post._id, + message_id='test_reply-m...@domain.net', in_reply_to=['test_re...@domain.net']) assert_equal(FM.ForumThread.query.find().count(), 1) assert_equal(FM.ForumPost.query.find().count(), 2) - assert_equal(FM.ForumThread.query.get() - .first_post_id, 'test_re...@domain.net') + assert_equal(FM.ForumThread.query.get().first_post_id, 'test_re...@domain.net') + + def test_reply_only_references_headers(self): + # send message missing in-reply-to header, only References header + self._post('testforum', 'Test Thread', 'Nothing here', + message_id='first-message-id') + prev_post = FM.ForumPost.query.find().first() + thread = FM.ForumThread.query.find().first() + + refs = M.Notification._references(thread, prev_post) + ['first-message-id'] + self._post('testforum', 'Test Thread', 'Nothing here, yet', + message_id='second-message-id', + references=refs) + assert_equal(FM.ForumThread.query.find().count(), 1) + assert_equal(FM.ForumPost.query.find().count(), 2) + + prev_post = FM.ForumPost.query.find().sort('timestamp', pymongo.DESCENDING).first() + refs = M.Notification._references(thread, prev_post) + ['second-message-id'] + self._post('testforum', 'Test Reply', 'Nothing here, either', + message_id='third-message-id', + references=refs) + assert_equal(FM.ForumThread.query.find().count(), 1) + assert_equal(FM.ForumPost.query.find().count(), 3) def test_attach(self): self._post('testforum', 'Attachment Thread', 'This is a text file', @@ -255,6 +281,9 @@ class TestForumAsync(TestController): params=dict(subject='', delete='on')) def _post(self, topic, subject, body, **kw): + ''' + Submit a message very similar to how incoming email works + ''' message_id = kw.pop('message_id', '%s...@test.com' % random.random()) with h.push_config(c, user=self.user): c.app.handle_message(