summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorDuda Dornelles <ddornell@thoughtworks.com>2014-12-23 11:28:48 -0200
committerDuda Dornelles <ddornell@thoughtworks.com>2014-12-23 11:28:48 -0200
commit0047054a53cb88bbe1b4cf2de0ebdc26daa96ade (patch)
treed2f8616eab4fe22c91f5d01e57d4203c6a3bb3f0 /service
parent51ec3f9edc355f4b824f5eefae19f9824dde0bf8 (diff)
Refactoring soledad querier to use mixins
Diffstat (limited to 'service')
-rw-r--r--service/pixelated/adapter/services/draft_service.py8
-rw-r--r--service/pixelated/adapter/services/mailbox.py3
-rw-r--r--service/pixelated/adapter/soledad/soledad_duplicate_removal_mixin.py41
-rw-r--r--service/pixelated/adapter/soledad/soledad_facade_mixin.py61
-rw-r--r--service/pixelated/adapter/soledad/soledad_querier.py183
-rw-r--r--service/pixelated/adapter/soledad/soledad_reader_mixin.py111
-rw-r--r--service/pixelated/adapter/soledad/soledad_search_key_masterkey_retrieval_mixin.py28
-rw-r--r--service/pixelated/adapter/soledad/soledad_writer_mixin.py69
-rw-r--r--service/pixelated/controllers/mails_controller.py19
-rw-r--r--service/test/integration/drafts_test.py1
-rw-r--r--service/test/integration/soledad_querier_test.py1
11 files changed, 339 insertions, 186 deletions
diff --git a/service/pixelated/adapter/services/draft_service.py b/service/pixelated/adapter/services/draft_service.py
index ddb86c5c..df295eec 100644
--- a/service/pixelated/adapter/services/draft_service.py
+++ b/service/pixelated/adapter/services/draft_service.py
@@ -22,13 +22,13 @@ class DraftService(object):
self._mailboxes = mailboxes
def create_draft(self, input_mail):
- self._drafts().add(input_mail)
- return input_mail
+ pixelated_mail = self._drafts().add(input_mail)
+ return pixelated_mail
def update_draft(self, ident, input_mail):
- new_mail = self.create_draft(input_mail)
+ pixelated_mail = self.create_draft(input_mail)
self._drafts().remove(ident)
- return new_mail
+ return pixelated_mail
def _drafts(self):
return self._mailboxes.drafts()
diff --git a/service/pixelated/adapter/services/mailbox.py b/service/pixelated/adapter/services/mailbox.py
index fbdbfc30..508dec31 100644
--- a/service/pixelated/adapter/services/mailbox.py
+++ b/service/pixelated/adapter/services/mailbox.py
@@ -43,6 +43,9 @@ class Mailbox:
def remove(self, ident):
mail = self.querier.mail(ident)
+ self.remove_mail(mail)
+
+ def remove_mail(self, mail):
mail.remove_all_tags()
self.querier.remove_mail(mail)
diff --git a/service/pixelated/adapter/soledad/soledad_duplicate_removal_mixin.py b/service/pixelated/adapter/soledad/soledad_duplicate_removal_mixin.py
new file mode 100644
index 00000000..e695a55f
--- /dev/null
+++ b/service/pixelated/adapter/soledad/soledad_duplicate_removal_mixin.py
@@ -0,0 +1,41 @@
+#
+# 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/>.
+from pixelated.adapter.soledad.soledad_facade_mixin import SoledadDbFacadeMixin
+
+
+class SoledadDuplicateRemovalMixin(SoledadDbFacadeMixin, object):
+
+ def remove_duplicates(self):
+ for mailbox in ['INBOX', 'DRAFTS', 'SENT', 'TRASH']:
+ self._remove_dup_inboxes(mailbox)
+ self._remove_dup_recent(mailbox)
+
+ def _remove_many(self, docs):
+ [self.delete_doc(doc) for doc in docs]
+
+ def _remove_dup_inboxes(self, mailbox_name):
+ mailboxes = self.get_mbox(mailbox_name)
+ if len(mailboxes) == 0:
+ return
+ mailboxes_to_remove = sorted(mailboxes, key=lambda x: x.content['created'])[1:len(mailboxes)]
+ self._remove_many(mailboxes_to_remove)
+
+ def _remove_dup_recent(self, mailbox_name):
+ rct = self.get_recent_by_mbox(mailbox_name)
+ if len(rct) == 0:
+ return
+ rct_to_remove = sorted(rct, key=lambda x: len(x.content['rct']), reverse=True)[1:len(rct)]
+ self._remove_many(rct_to_remove) \ No newline at end of file
diff --git a/service/pixelated/adapter/soledad/soledad_facade_mixin.py b/service/pixelated/adapter/soledad/soledad_facade_mixin.py
new file mode 100644
index 00000000..4d6cf675
--- /dev/null
+++ b/service/pixelated/adapter/soledad/soledad_facade_mixin.py
@@ -0,0 +1,61 @@
+#
+# 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/>.
+
+
+class SoledadDbFacadeMixin(object):
+
+ def get_all_flags(self):
+ return self.soledad.get_from_index('by-type', 'flags')
+
+ def get_all_flags_by_mbox(self, mbox):
+ return self.soledad.get_from_index('by-type-and-mbox', 'flags', mbox)
+
+ def get_all_headers_by_chash(self, chash):
+ return self.soledad.get_from_index('by-type-and-contenthash', 'head', chash)
+
+ def get_content_by_phash(self, phash):
+ return self.soledad.get_from_index('by-type-and-payloadhash', 'cnt', phash)
+
+ def get_flags_by_chash(self, chash):
+ return self.soledad.get_from_index('by-type-and-contenthash', 'flags', chash)[0]
+
+ def get_header_by_chash(self, chash):
+ return self.soledad.get_from_index('by-type-and-contenthash', 'head', chash)[0]
+
+ def get_recent_by_mbox(self, mbox):
+ return self.soledad.get_from_index('by-type-and-mbox', 'rct', mbox)
+
+ def put_doc(self, doc):
+ return self.soledad.put_doc(doc)
+
+ def create_doc(self, doc):
+ return self.soledad.create_doc(doc)
+
+ def delete_doc(self, doc):
+ return self.soledad.delete_doc(doc)
+
+ def idents_by_mailbox(self, mbox):
+ return set(doc.content['chash'] for doc in self.soledad.get_from_index('by-type-and-mbox-and-deleted', 'flags', mbox, '0'))
+
+ def get_all_mbox(self):
+ return self.soledad.get_from_index('by-type', 'mbox')
+
+ def get_mbox(self, mbox):
+ return self.soledad.get_from_index('by-type-and-mbox', 'mbox', mbox)
+
+ def get_search_index_masterkey(self):
+ return self.soledad.get_from_index('by-type', 'index_key')
+
diff --git a/service/pixelated/adapter/soledad/soledad_querier.py b/service/pixelated/adapter/soledad/soledad_querier.py
index b319eac0..e0b215d3 100644
--- a/service/pixelated/adapter/soledad/soledad_querier.py
+++ b/service/pixelated/adapter/soledad/soledad_querier.py
@@ -13,182 +13,17 @@
#
# 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 base64
-import quopri
+from pixelated.adapter.soledad.soledad_duplicate_removal_mixin import SoledadDuplicateRemovalMixin
+from pixelated.adapter.soledad.soledad_reader_mixin import SoledadReaderMixin
+from pixelated.adapter.soledad.soledad_search_key_masterkey_retrieval_mixin import SoledadSearchIndexMasterkeyRetrievalMixin
+from pixelated.adapter.soledad.soledad_writer_mixin import SoledadWriterMixin
-from cryptography.fernet import Fernet
-from pixelated.adapter.model.mail import PixelatedMail
-import re
-
-class SoledadQuerier:
+class SoledadQuerier(SoledadWriterMixin,
+ SoledadReaderMixin,
+ SoledadDuplicateRemovalMixin,
+ SoledadSearchIndexMasterkeyRetrievalMixin,
+ object):
def __init__(self, soledad):
self.soledad = soledad
-
- def mark_all_as_not_recent(self):
- for mailbox in ['INBOX', 'DRAFTS', 'SENT', 'TRASH']:
- rct = self.soledad.get_from_index('by-type-and-mbox', 'rct', mailbox)
- if len(rct) == 0:
- return
- rct = rct[0]
- rct.content['rct'] = []
- self.soledad.put_doc(rct)
-
- def all_mails(self):
- fdocs_chash = [(fdoc, fdoc.content['chash']) for fdoc in self.soledad.get_from_index('by-type', 'flags')]
- if len(fdocs_chash) == 0:
- return []
- return self._build_mails_from_fdocs(fdocs_chash)
-
- def all_mails_by_mailbox(self, mailbox_name):
- fdocs_chash = [(fdoc, fdoc.content['chash']) for fdoc in self.soledad.get_from_index('by-type-and-mbox', 'flags', mailbox_name)]
- return self._build_mails_from_fdocs(fdocs_chash)
-
- def _build_mails_from_fdocs(self, fdocs_chash):
- if len(fdocs_chash) == 0:
- return []
-
- fdocs_hdocs = []
- for fdoc, chash in fdocs_chash:
- hdoc = self.soledad.get_from_index('by-type-and-contenthash', 'head', chash)
- if len(hdoc) == 0:
- continue
- fdocs_hdocs.append((fdoc, hdoc[0]))
-
- fdocs_hdocs_bodyphash = [(f[0], f[1], f[1].content.get('body')) for f in fdocs_hdocs]
- fdocs_hdocs_bdocs_parts = []
- for fdoc, hdoc, body_phash in fdocs_hdocs_bodyphash:
- bdoc = self.soledad.get_from_index('by-type-and-payloadhash', 'cnt', body_phash)
- if len(bdoc) == 0:
- continue
- parts = self._extract_parts(hdoc.content)
- fdocs_hdocs_bdocs_parts.append((fdoc, hdoc, bdoc[0], parts))
-
- return [PixelatedMail.from_soledad(*raw_mail, soledad_querier=self) for raw_mail in fdocs_hdocs_bdocs_parts]
-
- def save_mail(self, mail):
- self.soledad.put_doc(mail.fdoc)
- self._update_index([mail.fdoc])
-
- def create_mail(self, mail, mailbox_name):
- mbox = [m for m in self.soledad.get_from_index('by-type', 'mbox') if m.content['mbox'] == 'INBOX'][0]
-
- uid = mbox.content['lastuid'] + 1
- new_docs = [self.soledad.create_doc(doc) for doc in mail.get_for_save(next_uid=uid, mailbox=mailbox_name)]
- mbox.content['lastuid'] = uid
-
- self.soledad.put_doc(mbox)
- self._update_index(new_docs)
-
- return self.mail(mail.ident)
-
- def mail(self, ident):
- fdoc = self.soledad.get_from_index('by-type-and-contenthash', 'flags', ident)[0]
- hdoc = self.soledad.get_from_index('by-type-and-contenthash', 'head', ident)[0]
- parts = self._extract_parts(hdoc.content)
- bdoc = self.soledad.get_from_index('by-type-and-payloadhash', 'cnt', hdoc.content['body'])[0]
-
- return PixelatedMail.from_soledad(fdoc, hdoc, bdoc, parts=parts, soledad_querier=self)
-
- def mails(self, idents):
- fdocs_chash = [(self.soledad.get_from_index('by-type-and-contenthash', 'flags', ident), ident) for ident in idents]
- fdocs_chash = [(result[0], ident) for result, ident in fdocs_chash if result]
- return self._build_mails_from_fdocs(fdocs_chash)
-
- def remove_mail(self, mail):
- _mail = self.mail(mail.ident)
- # FIX-ME: Must go through all the part_map phash to delete all the cdocs
- self.soledad.delete_doc(_mail.fdoc)
- self.soledad.delete_doc(_mail.hdoc)
- self.soledad.delete_doc(_mail.bdoc)
-
- def idents_by_mailbox(self, mailbox_name):
- return set(doc.content['chash'] for doc in self.soledad.get_from_index('by-type-and-mbox-and-deleted', 'flags', mailbox_name, '0'))
-
- def _update_index(self, docs):
- db = self.soledad._db
-
- indexed_fields = db._get_indexed_fields()
- if indexed_fields:
- # It is expected that len(indexed_fields) is shorter than
- # len(raw_doc)
- getters = [(field, db._parse_index_definition(field))
- for field in indexed_fields]
- for doc in docs:
- db._update_indexes(doc.doc_id, doc.content, getters, db._db_handle)
-
- # Attachments
- def attachment(self, ident, encoding):
- bdoc = self.soledad.get_from_index('by-type-and-payloadhash', 'cnt', ident)[0]
- return {'content': self._try_decode(bdoc.content['raw'], encoding),
- 'content-type': bdoc.content['content-type']}
-
- def _try_decode(self, raw, encoding):
- encoding = encoding.lower()
- if encoding == 'base64':
- return base64.decodestring(raw)
- elif encoding == 'quoted-printable':
- return quopri.decodestring(raw)
- else:
- return str(raw)
-
- def _extract_parts(self, hdoc, parts=None):
- if not parts:
- parts = {'alternatives': [], 'attachments': []}
-
- if hdoc['multi']:
- for part_key in hdoc.get('part_map', {}).keys():
- self._extract_parts(hdoc['part_map'][part_key], parts)
- else:
- headers_dict = {elem[0]: elem[1] for elem in hdoc.get('headers', [])}
- if 'attachment' in headers_dict.get('Content-Disposition', ''):
- parts['attachments'].append(self._extract_attachment(hdoc, headers_dict))
- else:
- parts['alternatives'].append(self._extract_alternative(hdoc, headers_dict))
- return parts
-
- def _extract_alternative(self, hdoc, headers_dict):
- bdoc = self.soledad.get_from_index('by-type-and-payloadhash', 'cnt', hdoc['phash'])[0]
- raw_content = bdoc.content['raw']
- return {'headers': headers_dict, 'content': raw_content}
-
- def _extract_attachment(self, hdoc, headers_dict):
- content_disposition = headers_dict['Content-Disposition']
- match = re.compile('.*name=\"(.*)\".*').search(content_disposition)
- filename = ''
- if match:
- filename = match.group(1)
- return {'headers': headers_dict, 'ident': hdoc['phash'], 'name': filename}
-
- # Removing duplicates
- def remove_duplicates(self):
- for mailbox in ['INBOX', 'DRAFTS', 'SENT', 'TRASH']:
- self._remove_dup_inboxes(mailbox)
- self._remove_dup_recent(mailbox)
-
- def _remove_many(self, docs):
- [self.soledad.delete_doc(doc) for doc in docs]
-
- def _remove_dup_inboxes(self, mailbox_name):
- mailboxes = self.soledad.get_from_index('by-type-and-mbox', 'mbox', mailbox_name)
- if len(mailboxes) == 0:
- return
- mailboxes_to_remove = sorted(mailboxes, key=lambda x: x.content['created'])[1:len(mailboxes)]
- self._remove_many(mailboxes_to_remove)
-
- def _remove_dup_recent(self, mailbox_name):
- rct = self.soledad.get_from_index('by-type-and-mbox', 'rct', mailbox_name)
- if len(rct) == 0:
- return
- rct_to_remove = sorted(rct, key=lambda x: len(x.content['rct']), reverse=True)[1:len(rct)]
- self._remove_many(rct_to_remove)
-
- # Search Index encryption key get/create
- def get_index_masterkey(self):
- index_key = self.soledad.get_from_index('by-type', 'index_key')
- if len(index_key) == 0:
- index_key = Fernet.generate_key()
- self.soledad.create_doc(dict(type='index_key', value=index_key))
- return index_key
- return str(index_key[0].content['value'])
diff --git a/service/pixelated/adapter/soledad/soledad_reader_mixin.py b/service/pixelated/adapter/soledad/soledad_reader_mixin.py
new file mode 100644
index 00000000..d0bd2053
--- /dev/null
+++ b/service/pixelated/adapter/soledad/soledad_reader_mixin.py
@@ -0,0 +1,111 @@
+#
+# 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 base64
+import quopri
+from pixelated.adapter.soledad.soledad_facade_mixin import SoledadDbFacadeMixin
+import re
+from pixelated.adapter.model.mail import PixelatedMail
+
+
+class SoledadReaderMixin(SoledadDbFacadeMixin, object):
+
+ def all_mails(self):
+ fdocs_chash = [(fdoc, fdoc.content['chash']) for fdoc in self.get_all_flags()]
+ if len(fdocs_chash) == 0:
+ return []
+ return self._build_mails_from_fdocs(fdocs_chash)
+
+ def all_mails_by_mailbox(self, mailbox_name):
+ fdocs_chash = [(fdoc, fdoc.content['chash']) for fdoc in self.get_all_flags_by_mbox(mailbox_name)]
+ return self._build_mails_from_fdocs(fdocs_chash)
+
+ def _build_mails_from_fdocs(self, fdocs_chash):
+ if len(fdocs_chash) == 0:
+ return []
+
+ fdocs_hdocs = []
+ for fdoc, chash in fdocs_chash:
+ hdoc = self.get_all_headers_by_chash(chash)
+ if len(hdoc) == 0:
+ continue
+ fdocs_hdocs.append((fdoc, hdoc[0]))
+
+ fdocs_hdocs_bodyphash = [(f[0], f[1], f[1].content.get('body')) for f in fdocs_hdocs]
+ fdocs_hdocs_bdocs_parts = []
+ for fdoc, hdoc, body_phash in fdocs_hdocs_bodyphash:
+ bdoc = self.get_content_by_phash(body_phash)
+ if len(bdoc) == 0:
+ continue
+ parts = self._extract_parts(hdoc.content)
+ fdocs_hdocs_bdocs_parts.append((fdoc, hdoc, bdoc[0], parts))
+
+ return [PixelatedMail.from_soledad(*raw_mail, soledad_querier=self) for raw_mail in fdocs_hdocs_bdocs_parts]
+
+ def mail(self, ident):
+ fdoc = self.get_flags_by_chash(ident)
+ hdoc = self.get_header_by_chash(ident)
+ bdoc = self.get_content_by_phash(hdoc.content['body'])[0]
+ parts = self._extract_parts(hdoc.content)
+
+ return PixelatedMail.from_soledad(fdoc, hdoc, bdoc, parts=parts, soledad_querier=self)
+
+ def mails(self, idents):
+ fdocs_chash = [(self.get_flags_by_chash(ident), ident) for ident in
+ idents]
+ fdocs_chash = [(result, ident) for result, ident in fdocs_chash if result]
+ return self._build_mails_from_fdocs(fdocs_chash)
+
+ def attachment(self, attachment_ident, encoding):
+ bdoc = self.get_content_by_phash(attachment_ident)[0]
+ return {'content': self._try_decode(bdoc.content['raw'], encoding),
+ 'content-type': bdoc.content['content-type']}
+
+ def _try_decode(self, raw, encoding):
+ encoding = encoding.lower()
+ if encoding == 'base64':
+ return base64.decodestring(raw)
+ elif encoding == 'quoted-printable':
+ return quopri.decodestring(raw)
+ else:
+ return str(raw)
+
+ def _extract_parts(self, hdoc, parts=None):
+ if not parts:
+ parts = {'alternatives': [], 'attachments': []}
+
+ if hdoc['multi']:
+ for part_key in hdoc.get('part_map', {}).keys():
+ self._extract_parts(hdoc['part_map'][part_key], parts)
+ else:
+ headers_dict = {elem[0]: elem[1] for elem in hdoc.get('headers', [])}
+ if 'attachment' in headers_dict.get('Content-Disposition', ''):
+ parts['attachments'].append(self._extract_attachment(hdoc, headers_dict))
+ else:
+ parts['alternatives'].append(self._extract_alternative(hdoc, headers_dict))
+ return parts
+
+ def _extract_alternative(self, hdoc, headers_dict):
+ bdoc = self.get_content_by_phash(hdoc['phash'])[0]
+ raw_content = bdoc.content['raw']
+ return {'headers': headers_dict, 'content': raw_content}
+
+ def _extract_attachment(self, hdoc, headers_dict):
+ content_disposition = headers_dict['Content-Disposition']
+ match = re.compile('.*name=\"(.*)\".*').search(content_disposition)
+ filename = ''
+ if match:
+ filename = match.group(1)
+ return {'headers': headers_dict, 'ident': hdoc['phash'], 'name': filename}
diff --git a/service/pixelated/adapter/soledad/soledad_search_key_masterkey_retrieval_mixin.py b/service/pixelated/adapter/soledad/soledad_search_key_masterkey_retrieval_mixin.py
new file mode 100644
index 00000000..4946e704
--- /dev/null
+++ b/service/pixelated/adapter/soledad/soledad_search_key_masterkey_retrieval_mixin.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/>.
+from pixelated.adapter.soledad.soledad_facade_mixin import SoledadDbFacadeMixin
+from cryptography.fernet import Fernet
+
+
+class SoledadSearchIndexMasterkeyRetrievalMixin(SoledadDbFacadeMixin, object):
+
+ def get_index_masterkey(self):
+ index_key = self.get_search_index_masterkey()
+ if len(index_key) == 0:
+ index_key = Fernet.generate_key()
+ self.create_doc(dict(type='index_key', value=index_key))
+ return index_key
+ return str(index_key[0].content['value']) \ No newline at end of file
diff --git a/service/pixelated/adapter/soledad/soledad_writer_mixin.py b/service/pixelated/adapter/soledad/soledad_writer_mixin.py
new file mode 100644
index 00000000..124a50b5
--- /dev/null
+++ b/service/pixelated/adapter/soledad/soledad_writer_mixin.py
@@ -0,0 +1,69 @@
+#
+# 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/>.
+from pixelated.adapter.model.mail import PixelatedMail
+from pixelated.adapter.soledad.soledad_facade_mixin import SoledadDbFacadeMixin
+
+
+class SoledadWriterMixin(SoledadDbFacadeMixin, object):
+
+ def mark_all_as_not_recent(self):
+ for mailbox in ['INBOX', 'DRAFTS', 'SENT', 'TRASH']:
+ rct = self.get_recent_by_mbox(mailbox)
+ if len(rct) == 0:
+ return
+ rct = rct[0]
+ rct.content['rct'] = []
+ self.put_doc(rct)
+
+ def save_mail(self, mail):
+ self.put_doc(mail.fdoc)
+ self._update_index([mail.fdoc])
+
+ def create_mail(self, mail, mailbox_name):
+ mbox = [m for m in self.get_all_mbox() if m.content['mbox'] == 'INBOX'][0]
+ uid = mbox.content['lastuid'] + 1
+
+ new_docs = [self.create_doc(doc) for doc in mail.get_for_save(next_uid=uid, mailbox=mailbox_name)]
+ fdoc, hdoc, cdocs = new_docs[0], new_docs[1], new_docs[2:len(new_docs)]
+ bdoc_index = None
+ for i, val in enumerate(cdocs):
+ if val.content['phash'] == hdoc.content['body']:
+ bdoc_index = i
+ bdoc = cdocs.pop(bdoc_index)
+
+ mbox.content['lastuid'] = uid
+ self.put_doc(mbox)
+
+ self._update_index(new_docs)
+ return self.mail(mail.ident) # PixelatedMail.from_soledad(fdoc, hdoc, bdoc, parts=None, soledad_querier=self)
+
+ def remove_mail(self, mail):
+ # FIX-ME: Must go through all the part_map phash to delete all the cdocs
+ self.delete_doc(mail.fdoc)
+ self.delete_doc(mail.hdoc)
+ self.delete_doc(mail.bdoc)
+
+ def _update_index(self, docs):
+ db = self.soledad._db
+
+ indexed_fields = db._get_indexed_fields()
+ if indexed_fields:
+ # It is expected that len(indexed_fields) is shorter than
+ # len(raw_doc)
+ getters = [(field, db._parse_index_definition(field))
+ for field in indexed_fields]
+ for doc in docs:
+ db._update_indexes(doc.doc_id, doc.content, getters, db._db_handle)
diff --git a/service/pixelated/controllers/mails_controller.py b/service/pixelated/controllers/mails_controller.py
index 50256fa5..9d649a1f 100644
--- a/service/pixelated/controllers/mails_controller.py
+++ b/service/pixelated/controllers/mails_controller.py
@@ -60,19 +60,23 @@ class MailsController:
self._search_engine.index_mail(mail)
return ""
- def delete_mail(self, request, mail_id):
+ def _delete_mail(self, mail_id):
mail = self._mail_service.mail(mail_id)
if mail.mailbox_name == 'TRASH':
self._mail_service.delete_permanent(mail_id)
+ self._search_engine.remove_from_index(mail_id)
else:
trashed_mail = self._mail_service.delete_mail(mail_id)
self._search_engine.index_mail(trashed_mail)
+
+ def delete_mail(self, request, mail_id):
+ self._delete_mail(mail_id)
return respond_json(None, request)
def delete_mails(self, request):
idents = json.loads(request.content.read())['idents']
for ident in idents:
- self.delete_mail(request, ident)
+ self._delete_mail(ident)
return respond_json(None, request)
def send_mail(self, request):
@@ -102,17 +106,16 @@ class MailsController:
def update_draft(self, request):
content_dict = json.loads(request.content.read())
-
_mail = InputMail.from_dict(content_dict)
draft_id = content_dict.get('ident')
+
if draft_id:
- ident = self._draft_service.update_draft(draft_id, _mail).ident
+ pixelated_mail = self._draft_service.update_draft(draft_id, _mail)
self._search_engine.remove_from_index(draft_id)
else:
- ident = self._draft_service.create_draft(_mail).ident
-
- self._search_engine.index_mail(self._mail_service.mail(ident))
- return respond_json({'ident': ident}, request)
+ pixelated_mail = self._draft_service.create_draft(_mail)
+ self._search_engine.index_mail(pixelated_mail)
+ return respond_json({'ident': pixelated_mail.ident}, request)
def reply_all_template(self, request, mail_id):
mail = self._mail_service.reply_all_template(mail_id)
diff --git a/service/test/integration/drafts_test.py b/service/test/integration/drafts_test.py
index 2ba14dfd..c24c5ecc 100644
--- a/service/test/integration/drafts_test.py
+++ b/service/test/integration/drafts_test.py
@@ -73,3 +73,4 @@ class DraftsTest(SoledadTestBase):
self.assertEquals(1, len(drafts))
self.assertEquals('First draft edited', drafts[0].subject)
+
diff --git a/service/test/integration/soledad_querier_test.py b/service/test/integration/soledad_querier_test.py
index 4a99a620..f8767630 100644
--- a/service/test/integration/soledad_querier_test.py
+++ b/service/test/integration/soledad_querier_test.py
@@ -26,6 +26,7 @@ class SoledadQuerierTest(SoledadTestBase, WithMsgFields):
def setUp(self):
SoledadTestBase.setUp(self)
self.soledad = self.client.soledad
+ self.maxDiff = None
self.soledad_querier = self.client.soledad_querier
def tearDown(self):