summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDuda Dornelles <ddornell@thoughtworks.com>2014-08-29 16:29:22 -0300
committerDuda Dornelles <ddornell@thoughtworks.com>2014-08-29 18:10:48 -0300
commitb727ab8090790db2854dac191faee702f085ac20 (patch)
tree6fc0fc8be8410def37d1931ea17a490f5bca96f1
parentc11bef55ee74edddaa054456338ebd3bbf72ee2f (diff)
Refactoring into mailboxes and mailsender - less colaborators for mail service
-rw-r--r--service/pixelated/adapter/mail_service.py73
-rw-r--r--service/pixelated/adapter/pixelated_mail_sender.py28
-rw-r--r--service/pixelated/adapter/pixelated_mailbox.py7
-rw-r--r--service/pixelated/adapter/pixelated_mailboxes.py28
-rw-r--r--service/pixelated/bitmask_libraries/session.py20
-rw-r--r--service/pixelated/search_query.py1
-rw-r--r--service/pixelated/user_agent.py29
-rw-r--r--service/setup.py1
-rw-r--r--service/test/adapter/mail_service_test.py77
-rw-r--r--service/test/adapter/pixelated_mailboxes_test.py42
10 files changed, 166 insertions, 140 deletions
diff --git a/service/pixelated/adapter/mail_service.py b/service/pixelated/adapter/mail_service.py
index 48495d51..6949d6cb 100644
--- a/service/pixelated/adapter/mail_service.py
+++ b/service/pixelated/adapter/mail_service.py
@@ -13,79 +13,33 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
-import traceback
-import sys
-import os
-import smtplib
-from pixelated.bitmask_libraries.smtp import LeapSmtp
+
from twisted.internet import defer
-from pixelated.bitmask_libraries.config import LeapConfig
-from pixelated.bitmask_libraries.provider import LeapProvider
-from pixelated.bitmask_libraries.session import LeapSessionFactory
-from pixelated.bitmask_libraries.auth import LeapCredentials
from pixelated.adapter.pixelated_mailbox import PixelatedMailbox
from pixelated.adapter.tag import Tag
-def open_leap_session(username, password, server_name):
- try:
- certs_home = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "certificates"))
-
- config = LeapConfig(certs_home=certs_home)
- provider = LeapProvider(server_name, config)
- session = LeapSessionFactory(provider).create(LeapCredentials(username, password))
- return session
- except:
- traceback.print_exc(file=sys.stdout)
- raise
-
-
class MailService:
__slots__ = ['leap_session', 'account', 'mailbox_name']
- def __init__(self, leap_session):
- self.leap_session = leap_session
- self.account = leap_session.account
- self.user_email = leap_session.account_email()
- self.mailbox_name = 'INBOX'
-
- def start(self):
- try:
- self.smtp_server = self._create_smtp_server()
- self.smtp_client = self._create_smtp_client(self.smtp_server.smtp_info())
- except:
- traceback.print_exc(file=sys.stdout)
- raise
-
- def _create_smtp_server(self):
- server = LeapSmtp(self.leap_session.provider, self.leap_session.nicknym.keymanager, self.leap_session.srp_session)
- server.start()
- return server
-
- def _create_smtp_client(self, smtp_info):
- smtp_servername, smtp_port = smtp_info
- client = smtplib.SMTP(smtp_servername, smtp_port)
- return client
+ def __init__(self, mailboxes, mail_sender):
+ self.mailboxes = mailboxes
+ self.mail_sender = mail_sender
@property
def mailbox(self):
- return PixelatedMailbox(self.account.getMailbox(self.mailbox_name))
+ return self.mailboxes.inbox()
def mails(self, query):
+ _mails = None
+
if not query:
return self.mailbox.mails()
- mails = []
if query['tags']:
- tags = [Tag(tag) for tag in query['tags']]
- for leap_mailbox_name in self.account.mailboxes:
- mailbox = PixelatedMailbox(self.account.getMailbox(leap_mailbox_name))
- if len(mailbox.all_tags().intersection(tags)):
- # mailbox has at least one mail with tag
- for mail in mailbox.mails():
- if len(mail.tags.intersection(tags)) > 0:
- mails.append(mail)
- return mails
+ _mails = self.mailboxes.mails_by_tag(query['tags'])
+
+ return sorted(_mails or [], key=lambda mail: mail.date, reverse=True)
def update_tags(self, mail_id, new_tags):
mail = self.mail(mail_id)
@@ -113,17 +67,14 @@ class MailService:
def _set_mail_flags(self, mail_id, flags, operation):
observer = defer.Deferred()
- leap_mailbox = self.account.getMailbox(self.mailbox_name)
+ leap_mailbox = self.mailboxes.leap_inbox_mailbox()
self.mailbox.messages.set_flags(leap_mailbox, [mail_id], tuple(flags), operation, observer)
def mail(self, mail_id):
return self.mailbox.mail(mail_id)
def send(self, mail):
- _from = self.user_email
- _to = mail.get_to()
-
- self.smtp_client.sendmail(_from, _to, mail.to_smtp_format(_from=_from))
+ self.mail_sender.sendmail(mail)
def all_tags(self):
return self.mailbox.all_tags()
diff --git a/service/pixelated/adapter/pixelated_mail_sender.py b/service/pixelated/adapter/pixelated_mail_sender.py
new file mode 100644
index 00000000..3ace6814
--- /dev/null
+++ b/service/pixelated/adapter/pixelated_mail_sender.py
@@ -0,0 +1,28 @@
+#
+# Copyright (c) 2014 ThoughtWorks, Inc.
+#
+# Pixelated is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pixelated is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
+import smtplib
+
+
+class PixelatedMailSender():
+ def __init__(self, account_email_address):
+ self.account_email_address = account_email_address
+ self.smtp_client = smtplib.SMTP('localhost', 4650)
+
+ def sendmail(self, mail):
+ _from = self.account_email_address
+ _to = mail.get_to()
+
+ self.smtp_client.sendmail(_from, _to, mail.to_smtp_format(_from=_from))
diff --git a/service/pixelated/adapter/pixelated_mailbox.py b/service/pixelated/adapter/pixelated_mailbox.py
index 280e67c7..2f50c2b4 100644
--- a/service/pixelated/adapter/pixelated_mailbox.py
+++ b/service/pixelated/adapter/pixelated_mailbox.py
@@ -35,6 +35,9 @@ class PixelatedMailbox:
mails = [PixelatedMail.from_leap_mail(mail) for mail in mails]
return mails
+ def mails_by_tags(self, tags):
+ return [mail for mail in self.mails() if len(mail.tags.intersection(tags)) > 0]
+
def mail(self, mail_id):
for message in self.leap_mailbox.messages:
if message.getUID() == int(mail_id):
@@ -56,3 +59,7 @@ class PixelatedMailbox:
flags = tuple(current_flags.union(new_flags))
self.leap_mailbox.setFlags(flags)
+
+ @classmethod
+ def create(cls, account, mailbox_name='INBOX'):
+ return PixelatedMailbox(account.getMailbox(mailbox_name))
diff --git a/service/pixelated/adapter/pixelated_mailboxes.py b/service/pixelated/adapter/pixelated_mailboxes.py
new file mode 100644
index 00000000..1125c137
--- /dev/null
+++ b/service/pixelated/adapter/pixelated_mailboxes.py
@@ -0,0 +1,28 @@
+from pixelated.adapter.tag import Tag
+from pixelated.adapter.pixelated_mailbox import PixelatedMailbox
+
+
+class PixelatedMailBoxes():
+ def __init__(self, account):
+ self.account = account
+ self.mailbox_name = 'INBOX'
+
+ def inbox(self):
+ return PixelatedMailbox.create(self.account)
+
+ @property
+ def mailboxes(self):
+ return [PixelatedMailbox.create(self.account, leap_mailbox_name) for leap_mailbox_name in
+ self.account.mailboxes]
+
+ def mails_by_tag(self, query_tags):
+ tags = map(Tag, query_tags)
+
+ mails = []
+ for mailbox in self.mailboxes:
+ mails.extend(mailbox.mails_by_tags(tags))
+
+ return mails
+
+ def leap_inbox_mailbox(self):
+ return self.account.getMailbox(self.mailbox_name)
diff --git a/service/pixelated/bitmask_libraries/session.py b/service/pixelated/bitmask_libraries/session.py
index bb9bd3fe..09229e17 100644
--- a/service/pixelated/bitmask_libraries/session.py
+++ b/service/pixelated/bitmask_libraries/session.py
@@ -21,16 +21,31 @@ from leap.mail.imap.account import SoledadBackedAccount
import sys
from leap.mail.imap.memorystore import MemoryStore
from leap.mail.imap.soledadstore import SoledadStore
+from pixelated.bitmask_libraries.config import LeapConfig
+from pixelated.bitmask_libraries.provider import LeapProvider
from twisted.internet import reactor
from .nicknym import NickNym
-from .auth import LeapAuthenticator
+from .auth import LeapAuthenticator, LeapCredentials
from .soledad import SoledadSessionFactory, SoledadSession
from .smtp import LeapSmtp
SESSIONS = {}
+def open(username, password, server_name):
+ try:
+ certs_home = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "certificates"))
+
+ config = LeapConfig(certs_home=certs_home)
+ provider = LeapProvider(server_name, config)
+ session = LeapSessionFactory(provider).create(LeapCredentials(username, password))
+ return session
+ except:
+ traceback.print_exc(file=sys.stdout)
+ raise
+
+
class LeapSession(object):
"""
A LEAP session.
@@ -118,6 +133,9 @@ class LeapSessionFactory(object):
incoming_mail_fetcher = self._create_incoming_mail_fetcher(nicknym, soledad,
account, auth)
+ smtp = LeapSmtp(self._provider, nicknym.keymanager, auth)
+ smtp.start()
+
return LeapSession(self._provider, auth, soledad, nicknym, account, incoming_mail_fetcher)
def _lookup_session(self, key):
diff --git a/service/pixelated/search_query.py b/service/pixelated/search_query.py
index e2860cac..278e1f2f 100644
--- a/service/pixelated/search_query.py
+++ b/service/pixelated/search_query.py
@@ -22,6 +22,7 @@ def compile(query):
sanitized_query = re.sub(r"['\"]", "", query.encode('utf8'))
scanner = StringScanner(sanitized_query)
first_token = True
+
while not scanner.is_eos:
token = scanner.scan(_next_token())
diff --git a/service/pixelated/user_agent.py b/service/pixelated/user_agent.py
index cee360cb..13dd767c 100644
--- a/service/pixelated/user_agent.py
+++ b/service/pixelated/user_agent.py
@@ -14,20 +14,22 @@
# You should have received a copy of the GNU Affero General Public License
# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
import json
-import os
-import datetime
-import dateutil.parser as dateparser
+import os
from flask import Flask
from flask import request
from flask import Response
-
+from pixelated.adapter.pixelated_mail_sender import PixelatedMailSender
+from pixelated.adapter.pixelated_mailboxes import PixelatedMailBoxes
import pixelated.reactor_manager as reactor_manager
import pixelated.search_query as search_query
-from pixelated.adapter.mail_service import MailService, open_leap_session
+import pixelated.bitmask_libraries.session as LeapSession
+from pixelated.adapter.mail_service import MailService
from pixelated.adapter.pixelated_mail import PixelatedMail
+
static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "web-ui", "app"))
+
# this is a workaround for packaging
if not os.path.exists(static_folder):
static_folder = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "..", "web-ui", "app"))
@@ -73,10 +75,6 @@ def mails():
if "inbox" in query['tags']:
mails = [mail for mail in mails if not mail.has_tag('trash')]
- mails = sorted(mails, key=lambda mail: mail.date, reverse=True)
-
- mails = [mail.as_dict() for mail in mails]
-
response = {
"stats": {
"total": len(mails),
@@ -84,7 +82,7 @@ def mails():
"starred": 0,
"replied": 0
},
- "mails": mails
+ "mails": [mail.as_dict() for mail in mails]
}
return respond_json(response)
@@ -141,10 +139,15 @@ def setup():
debug_enabled = os.environ.get('DEBUG', False)
reactor_manager.start_reactor(logging=debug_enabled)
app.config.from_pyfile(os.path.join(os.environ['HOME'], '.pixelated'))
- leap_session = open_leap_session(app.config['LEAP_USERNAME'], app.config['LEAP_PASSWORD'], app.config['LEAP_SERVER_NAME'])
- mail_service = MailService(leap_session)
+
+ leap_session = LeapSession.open(app.config['LEAP_USERNAME'], app.config['LEAP_PASSWORD'],
+ app.config['LEAP_SERVER_NAME'])
+ pixelated_mailboxes = PixelatedMailBoxes(leap_session.account)
+ pixelated_mail_sender = PixelatedMailSender(leap_session.account_email())
+
global mail_service
- mail_service.start()
+ mail_service = MailService(pixelated_mailboxes, pixelated_mail_sender)
+
app.run(host=app.config['HOST'], debug=debug_enabled,
port=app.config['PORT'], use_reloader=False)
diff --git a/service/setup.py b/service/setup.py
index 2c7180be..c8b0b6e8 100644
--- a/service/setup.py
+++ b/service/setup.py
@@ -79,6 +79,7 @@ setup(name='Pixelated User Agent Service',
'nose',
'mock',
'httmock',
+ 'mockito',
'gunicorn'
],
entry_points={
diff --git a/service/test/adapter/mail_service_test.py b/service/test/adapter/mail_service_test.py
index 04fbeb82..f66a3fae 100644
--- a/service/test/adapter/mail_service_test.py
+++ b/service/test/adapter/mail_service_test.py
@@ -16,80 +16,27 @@
import unittest
from pixelated.adapter.mail_service import MailService
-from mock import Mock, MagicMock, patch, PropertyMock
-import test_helper
-from pixelated.adapter.tag import Tag
-from pixelated.adapter.pixelated_mailbox import PixelatedMailbox
+from mockito import *
class TestMailService(unittest.TestCase):
-
- @patch.object(MailService, '_append_mail_flags', return_value=None)
- @patch.object(MailService, '_remove_mail_flags', return_value=None)
- def test_custom_tags_get_created_if_not_exists(self, mockRemoveFlags, mockAppendFlags):
- mailbox = test_helper.leap_mailbox(leap_flags=['\\Recent'])
- account = MagicMock()
- leap_session = MagicMock()
- leap_session.account = account
- leap_session.account.getMailbox.return_value = mailbox
-
- mailservice = MailService(leap_session)
-
- new_tags = ['test', 'inbox']
- updated_tags = mailservice.update_tags(6, new_tags)
-
- self.assertEquals(set([Tag('test'), Tag('inbox')]), set(updated_tags))
- # make sure that special tags are skipped when setting leap flags (eg.: tag_inbox)
- mockAppendFlags.assert_called_with(6, ['tag_test'])
- mockRemoveFlags.assert_called_with(6, [])
+ def setUp(self):
+ self.mailboxes = mock()
+ self.mail_sender = mock()
+ self.mail_service = MailService(self.mailboxes, self.mail_sender)
def test_search_without_query_returns_unfiltered_mailbox(self):
- # given
- mailbox = test_helper.leap_mailbox(leap_flags=['\\Recent'], extra_flags=['tag_custom_tag'])
- account = MagicMock()
- account.getMailbox.return_value = mailbox
- leap_session = MagicMock(account=account)
-
- # when
- mailservice = MailService(leap_session)
- mails = mailservice.mails({})
+ mailbox_inbox = mock()
+ when(mailbox_inbox).mails().thenReturn(["mail"])
+ when(self.mailboxes).inbox().thenReturn(mailbox_inbox)
- # then
- self.assertEqual(1, len(mails))
-
- def test_search_for_tags(self):
- # given
- mailbox = test_helper.leap_mailbox(leap_flags=['\\Recent'], extra_flags=['tag_custom_tag'])
- matching_mail = test_helper.leap_mail(uid=6, leap_flags=[], extra_flags=['tag_custom_tag'])
- not_matching_mail = test_helper.leap_mail(uid=6, leap_flags=[], extra_flags=['tag_other'])
- mailbox.messages = [matching_mail, not_matching_mail]
- mailbox.all_tags.return_value = set()
- account = MagicMock()
- account.mailboxes = ['inbox']
- account.getMailbox.return_value = mailbox
-
- leap_session = MagicMock(account=account)
+ mails = self.mail_service.mails({})
- # when
- mailservice = MailService(leap_session)
- mails = mailservice.mails({'tags': ['inbox', 'custom_tag']})
-
- # then
self.assertEqual(1, len(mails))
- self.assertEqual(set([Tag('custom_tag')]), mails[0].tags)
def test_send_mail(self):
- leap_session = Mock(account_email=lambda: "username@leap_server")
- mail_service = MailService(leap_session)
- mail_service._create_smtp_server = lambda: None
- mail_service._create_smtp_server = lambda: None
-
- mail_service.smtp_client = Mock()
- mail = Mock(
- to_smtp_format=lambda _from=None: "mail as string",
- get_to=lambda: 'to@pixelated.org'
- )
+ mail = "mail"
- mail_service.send(mail)
+ self.mail_service.send(mail)
- mail_service.smtp_client.sendmail.assert_called_with('username@leap_server', 'to@pixelated.org', "mail as string")
+ verify(self.mail_sender).sendmail(mail)
diff --git a/service/test/adapter/pixelated_mailboxes_test.py b/service/test/adapter/pixelated_mailboxes_test.py
new file mode 100644
index 00000000..b90e17a0
--- /dev/null
+++ b/service/test/adapter/pixelated_mailboxes_test.py
@@ -0,0 +1,42 @@
+#
+# Copyright (c) 2014 ThoughtWorks, Inc.
+#
+# Pixelated is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Pixelated is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
+import unittest
+
+from pixelated.adapter.pixelated_mailbox import PixelatedMailbox
+from pixelated.adapter.pixelated_mailboxes import PixelatedMailBoxes
+from mockito import *
+
+
+class PixelatedMailboxesTest(unittest.TestCase):
+ def setUp(self):
+ self.account = mock()
+ self.mailboxes = PixelatedMailBoxes(self.account)
+
+ def test_search_for_tags(self):
+ # given
+ mailbox = mock()
+ self.account.mailboxes = ['INBOX']
+ tags_to_search_for = {'tags': ['inbox', 'custom_tag']}
+
+ when(PixelatedMailbox).create(self.account, 'INBOX').thenReturn(mailbox)
+ when(mailbox).mails_by_tags(any(list)).thenReturn(["mail"])
+
+ # when
+ mails = self.mailboxes.mails_by_tag(tags_to_search_for)
+
+ # then
+ self.assertEqual(1, len(mails))
+ self.assertEqual("mail", mails[0])