def test_renamed_label_refresh(db, default_account, thread, message, imapuid, folder, mock_imapclient, monkeypatch): # Check that imapuids see their labels refreshed after running # the LabelRenameHandler. msg_uid = imapuid.msg_uid uid_dict = {msg_uid: GmailFlags((), ('stale label',))} update_metadata(default_account.id, folder.id, uid_dict, db.session) new_flags = {msg_uid: {'FLAGS': ('\\Seen',), 'X-GM-LABELS': ('new label',)}} mock_imapclient._data['[Gmail]/All mail'] = new_flags mock_imapclient.add_folder_data(folder.name, new_flags) monkeypatch.setattr(MockIMAPClient, 'search', lambda x, y: [msg_uid]) rename_handler = LabelRenameHandler(default_account.id, default_account.namespace.id, 'new label') rename_handler.start() rename_handler.join() labels = list(imapuid.labels) assert len(labels) == 1 assert labels[0].name == 'new label'
def test_renamed_label_refresh(db, default_account, thread, message, imapuid, folder, mock_imapclient, monkeypatch): # Check that imapuids see their labels refreshed after running # the LabelRenameHandler. msg_uid = imapuid.msg_uid uid_dict = {msg_uid: GmailFlags((), ('stale label', ), ('23', ))} update_metadata(default_account.id, folder.id, folder.canonical_name, uid_dict, db.session) new_flags = { msg_uid: { 'FLAGS': ('\\Seen', ), 'X-GM-LABELS': ('new label', ), 'MODSEQ': ('23', ) } } mock_imapclient._data['[Gmail]/All mail'] = new_flags mock_imapclient.add_folder_data(folder.name, new_flags) monkeypatch.setattr(MockIMAPClient, 'search', lambda x, y: [msg_uid]) semaphore = Semaphore(value=1) rename_handler = LabelRenameHandler(default_account.id, default_account.namespace.id, 'new label', semaphore) # Acquire the semaphore to check that LabelRenameHandlers block if # the semaphore is in-use. semaphore.acquire() rename_handler.start() # Wait 10 secs and check that the data hasn't changed. gevent.sleep(10) labels = list(imapuid.labels) assert len(labels) == 1 assert labels[0].name == 'stale label' semaphore.release() rename_handler.join() db.session.refresh(imapuid) # Now check that the label got updated. labels = list(imapuid.labels) assert len(labels) == 1 assert labels[0].name == 'new label'
def test_renamed_label_refresh(db, default_account, thread, message, imapuid, folder, mock_imapclient, monkeypatch): # Check that imapuids see their labels refreshed after running # the LabelRenameHandler. msg_uid = imapuid.msg_uid uid_dict = {msg_uid: GmailFlags((), ("stale label", ), ("23", ))} update_metadata(default_account.id, folder.id, folder.canonical_name, uid_dict, db.session) new_flags = { msg_uid: { b"FLAGS": (b"\\Seen", ), b"X-GM-LABELS": (b"new label", ), b"MODSEQ": (23, ), } } mock_imapclient._data["[Gmail]/All mail"] = new_flags mock_imapclient.add_folder_data(folder.name, new_flags) monkeypatch.setattr(MockIMAPClient, "search", lambda x, y: [msg_uid]) semaphore = Semaphore(value=1) rename_handler = LabelRenameHandler(default_account.id, default_account.namespace.id, "new label", semaphore) # Acquire the semaphore to check that LabelRenameHandlers block if # the semaphore is in-use. semaphore.acquire() rename_handler.start() gevent.sleep(0) # yield to the handler labels = list(imapuid.labels) assert len(labels) == 1 assert labels[0].name == "stale label" semaphore.release() rename_handler.join() db.session.refresh(imapuid) # Now check that the label got updated. labels = list(imapuid.labels) assert len(labels) == 1 assert labels[0].name == "new label"
def test_renamed_label_refresh(db, default_account, thread, message, imapuid, folder, mock_imapclient, monkeypatch): # Check that imapuids see their labels refreshed after running # the LabelRenameHandler. msg_uid = imapuid.msg_uid uid_dict = {msg_uid: GmailFlags((), ('stale label',), ('23',))} update_metadata(default_account.id, folder.id, folder.canonical_name, uid_dict, db.session) new_flags = {msg_uid: {'FLAGS': ('\\Seen',), 'X-GM-LABELS': ('new label',), 'MODSEQ': ('23',)}} mock_imapclient._data['[Gmail]/All mail'] = new_flags mock_imapclient.add_folder_data(folder.name, new_flags) monkeypatch.setattr(MockIMAPClient, 'search', lambda x, y: [msg_uid]) semaphore = Semaphore(value=1) rename_handler = LabelRenameHandler(default_account.id, default_account.namespace.id, 'new label', semaphore) # Acquire the semaphore to check that LabelRenameHandlers block if # the semaphore is in-use. semaphore.acquire() rename_handler.start() # Wait 10 secs and check that the data hasn't changed. gevent.sleep(10) labels = list(imapuid.labels) assert len(labels) == 1 assert labels[0].name == 'stale label' semaphore.release() rename_handler.join() db.session.refresh(imapuid) # Now check that the label got updated. labels = list(imapuid.labels) assert len(labels) == 1 assert labels[0].name == 'new label'
def save_folder_names(self, db_session, raw_folders): """ Save the folders, labels present on the remote backend for an account. * Create Folder/ Label objects. * Delete Folders/ Labels that no longer exist on the remote. Notes ----- Gmail uses IMAP folders and labels. Canonical folders ('all', 'trash', 'spam') are therefore mapped to both Folder and Label objects, everything else is created as a Label only. We don't canonicalize names to lowercase when saving because different backends may be case-sensitive or otherwise - code that references saved names should canonicalize if needed when doing comparisons. """ account = db_session.query(Account).get(self.account_id) current_labels = set() old_labels = {label for label in db_session.query(Label).filter( Label.account_id == self.account_id, Label.deleted_at == None)} # noqa # Is it the first time we've been syncing folders? # It's important to know this because we don't want to # be refreshing the labels for every message at the very # beginning of the initial sync. first_time_syncing_folders = len(old_labels) == 0 # Create new labels, folders for raw_folder in raw_folders: if raw_folder.role == 'starred': # The starred state of messages is tracked separately # (we set Message.is_starred from the '\\Flagged' flag) continue if raw_folder.role in ('all', 'spam', 'trash'): self.handle_raw_folder_change(db_session, account, raw_folder) label = Label.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) if label.deleted_at is not None: # This is a label which was previously marked as deleted # but which mysteriously reappeared. Unmark it. log.info('Deleted label recreated on remote', name=raw_folder.display_name) label.deleted_at = None label.category.deleted_at = None current_labels.add(label) new_labels = current_labels - old_labels db_session.commit() if not first_time_syncing_folders: # Try to see if a label has been renamed. for label in new_labels: db_session.refresh(label) db_session.expunge(label) rename_handler = LabelRenameHandler( account_id=self.account_id, namespace_id=self.namespace_id, label_name=label.name, semaphore=self.label_rename_semaphore) rename_handler.start() self.set_sync_should_run_bit(account) deleted_labels = old_labels - current_labels self.mark_deleted_labels(db_session, deleted_labels) db_session.commit()
def save_folder_names(self, db_session, raw_folders): """ Save the folders, labels present on the remote backend for an account. * Create Folder/ Label objects. * Delete Folders/ Labels that no longer exist on the remote. Notes ----- Gmail uses IMAP folders and labels. Canonical folders ('all', 'trash', 'spam') are therefore mapped to both Folder and Label objects, everything else is created as a Label only. We don't canonicalize names to lowercase when saving because different backends may be case-sensitive or otherwise - code that references saved names should canonicalize if needed when doing comparisons. """ account = db_session.query(Account).get(self.account_id) current_labels = set() old_labels = {label for label in db_session.query(Label).filter( Label.account_id == self.account_id, Label.deleted_at == None)} # noqa # Is it the first time we've been syncing folders? # It's important to know this because we don't want to # be refreshing the labels for every message at the very # beginning of the initial sync. first_time_syncing_folders = len(old_labels) == 0 # Create new labels, folders for raw_folder in raw_folders: if raw_folder.role == 'starred': # The starred state of messages is tracked separately # (we set Message.is_starred from the '\\Flagged' flag) continue if raw_folder.role in ('all', 'spam', 'trash'): self.handle_raw_folder_change(db_session, account, raw_folder) label = Label.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) if label.deleted_at is not None: # This is a label which was previously marked as deleted # but which mysteriously reappeared. Unmark it. log.info('Deleted label recreated on remote', name=raw_folder.display_name) label.deleted_at = None label.category.deleted_at = EPOCH current_labels.add(label) new_labels = current_labels - old_labels db_session.commit() if not first_time_syncing_folders: # Try to see if a label has been renamed. for label in new_labels: db_session.refresh(label) db_session.expunge(label) rename_handler = LabelRenameHandler( account_id=self.account_id, namespace_id=self.namespace_id, label_name=label.name, semaphore=self.label_rename_semaphore) rename_handler.start() self.set_sync_should_run_bit(account) deleted_labels = old_labels - current_labels self.mark_deleted_labels(db_session, deleted_labels) db_session.commit()
def save_folder_names(self, db_session, raw_folders): """ Save the folders, labels present on the remote backend for an account. * Create Folder/ Label objects. * Delete Folders/ Labels that no longer exist on the remote. Notes ----- Gmail uses IMAP folders and labels. Canonical folders ('all', 'trash', 'spam') are therefore mapped to both Folder and Label objects, everything else is created as a Label only. We don't canonicalize names to lowercase when saving because different backends may be case-sensitive or otherwise - code that references saved names should canonicalize if needed when doing comparisons. """ account = db_session.query(Account).get(self.account_id) old_labels = {label for label in db_session.query(Label).filter( Label.account_id == self.account_id, Label.deleted_at == None)} current_labels = set() # Create new labels, folders for raw_folder in raw_folders: if raw_folder.role == 'starred': # The starred state of messages is tracked separately # (we set Message.is_starred from the '\\Flagged' flag) continue label = Label.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) current_labels.add(label) if label.deleted_at is not None: # This is a label which was previously marked as deleted # but which mysteriously reappeared. Unmark it. log.info('Deleted label recreated on remote', name=raw_folder.display_name) label.deleted_at = None label.category.deleted_at = None if raw_folder.role in ('all', 'spam', 'trash'): folder = db_session.query(Folder).filter( Folder.account_id == account.id, Folder.canonical_name == raw_folder.role).first() if folder: if folder.name != raw_folder.display_name: log.info('Folder name changed on remote', account_id=self.account_id, role=raw_folder.role, new_name=raw_folder.display_name, name=folder.name) folder.name = raw_folder.display_name if folder.category: if folder.category.display_name != \ raw_folder.display_name: folder.category.display_name = raw_folder.display_name # noqa else: log.info('Creating category for folder', account_id=self.account_id, folder_name=folder.name) folder.category = Category.find_or_create( db_session, namespace_id=account.namespace.id, name=raw_folder.role, display_name=raw_folder.display_name, type_='folder') else: Folder.find_or_create(db_session, account, raw_folder.display_name, raw_folder.role) # Ensure sync_should_run is True for the folders we want to sync (for # Gmail, that's just all folders, since we created them above if # they didn't exist.) for folder in account.folders: if folder.imapsyncstatus: folder.imapsyncstatus.sync_should_run = True # Go through the labels which have been "deleted" (i.e: they don't # show up when running LIST) and mark them as such. # We can't delete labels directly because Gmail allows users to hide # folders --- we need to check that there's no messages still # associated with the label. deleted_labels = old_labels - current_labels for deleted_label in deleted_labels: deleted_label.deleted_at = datetime.now() cat = deleted_label.category cat.deleted_at = datetime.now() new_labels = current_labels - old_labels for label in new_labels: rename_handler = LabelRenameHandler( account_id=self.account_id, namespace_id=self.namespace_id, label_name=label.name) rename_handler.start() db_session.commit()