From 0047054a53cb88bbe1b4cf2de0ebdc26daa96ade Mon Sep 17 00:00:00 2001 From: Duda Dornelles Date: Tue, 23 Dec 2014 11:28:48 -0200 Subject: Refactoring soledad querier to use mixins --- .../pixelated/adapter/services/draft_service.py | 8 +- service/pixelated/adapter/services/mailbox.py | 3 + .../soledad/soledad_duplicate_removal_mixin.py | 41 +++++ .../adapter/soledad/soledad_facade_mixin.py | 61 +++++++ .../pixelated/adapter/soledad/soledad_querier.py | 183 +-------------------- .../adapter/soledad/soledad_reader_mixin.py | 111 +++++++++++++ ...soledad_search_key_masterkey_retrieval_mixin.py | 28 ++++ .../adapter/soledad/soledad_writer_mixin.py | 69 ++++++++ service/pixelated/controllers/mails_controller.py | 19 ++- service/test/integration/drafts_test.py | 1 + service/test/integration/soledad_querier_test.py | 1 + 11 files changed, 339 insertions(+), 186 deletions(-) create mode 100644 service/pixelated/adapter/soledad/soledad_duplicate_removal_mixin.py create mode 100644 service/pixelated/adapter/soledad/soledad_facade_mixin.py create mode 100644 service/pixelated/adapter/soledad/soledad_reader_mixin.py create mode 100644 service/pixelated/adapter/soledad/soledad_search_key_masterkey_retrieval_mixin.py create mode 100644 service/pixelated/adapter/soledad/soledad_writer_mixin.py (limited to 'service') 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 . +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 . + + +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 . -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 . +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 . +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 . +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): -- cgit v1.2.3