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'
Example #5
0
    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()
Example #6
0
    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()
Example #7
0
    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()