Beispiel #1
0
    def handle_email_deleted(self, label_id, error=''):
        if label_id != self.label_id:
            return

        # Trying to delete previously restored email won't produce an error, email will be deleted.
        # But trying to delete previously deleted email will produce an error.
        if error:
            error_code = get_error_code(error)
            if error_code == 404:
                LOG.warning(
                    "Failed to delete the email, it was already deleted.")
                self.on_error.emit(
                    self.label_id,
                    "Can't delete that email, because it was already deleted.")
            else:
                LOG.error(f"Failed to delete email. Error: {error}")
                self.on_error.emit(self.label_id,
                                   "Failed to delete the email.")

            self.sync_helper.pull_event()
            self.sync_helper.push_next_event()
            return

        self.sync_helper.pull_event()
        LOG.info("Email completely deleted.")
        self.sync_helper.push_next_event()
Beispiel #2
0
    def handle_email_restored(self, email, to_add, error=''):
        if self.label_id != GMAIL_LABEL_TRASH and self.label_id not in to_add:
            return

        # Trying to restore already restored email won't produce an error.
        # But trying to restore previously deleted email will produce an error.
        if error:
            if self.label_id != GMAIL_LABEL_TRASH:
                return
            error_code = get_error_code(error)
            if error_code == 404:
                LOG.warning(
                    "Failed to restore the email, it was already deleted.")
                self.on_error.emit(
                    self.label_id,
                    "Can't restore that email, because it was already deleted."
                )
            else:
                LOG.error(f"Failed to restore email. Error: {error}")
                self.on_error.emit(self.label_id,
                                   "Failed to restore the email.")

            self.sync_helper.pull_event()
            self.sync_helper.push_next_event()
            return

        if self.label_id == GMAIL_LABEL_TRASH:
            self.sync_helper.pull_event()
            LOG.info(f"Email completely restored(label_id: {self.label_id})")
            self.sync_helper.push_next_event()
        elif self.label_id in to_add:
            self.insert_email(email)
Beispiel #3
0
 def dispatch_updates(self, history_records, error=''):
     if error:
         parsed_error = get_error_code(error)
         if parsed_error == 404:
             # TODO: Partial sync failed, do full sync. Don't forget to wrap short_sync
             #  api call in try-except block.
             LOG.error(f"Partial sync failed. Error: {error}.")
             return
     try:
         self._dispatch_updates(history_records)
     except Exception as err:
         # TODO: Reset all models. And run full sync.
         LOG.error(f"Failed to dispatch history_records. Error: {err}")
         if TESTING is True:
             raise err
Beispiel #4
0
    def handle_email_trashed(self, email, from_lbl_id, to_remove, error=''):
        if from_lbl_id != self.label_id and self.label_id not in to_remove and \
                self.label_id != GMAIL_LABEL_TRASH:
            return

        # Trying to move email to trash that was previously moved to trash won't produce an error.
        # But trying to move email to trash that was previously deleted will produce an error.
        if error:
            # In case we are in the EmailModel for trashed emails, then we just return
            # cause there was an error, and we don't have to fix nor store anything.
            if self.label_id != from_lbl_id:
                return
            error_code = get_error_code(error)
            if error_code == 404:
                LOG.warning(
                    "Failed to move email to trash, it was already deleted.")
                self.on_error.emit(
                    self.label_id,
                    "Can't move that email to trash because it was already deleted."
                )
            else:
                LOG.error(f"Failed to move the email to trash. Error: {error}")
                self.on_error.emit(self.label_id,
                                   "Failed to move the email to trash.")

            self.sync_helper.pull_event()
            self.sync_helper.push_next_event()
            return

        # Check if self.label_id == from_lbl_id first, because that label also exists in to_remove
        if from_lbl_id == self.label_id:
            # This is the inbox model, so now we can remove the event
            self.sync_helper.pull_event()
            LOG.info(
                f"Email sent to trash successfully(label_id): {self.label_id}")
            # Now send next event if there's any left in the queue
            self.sync_helper.push_next_event()
        elif self.label_id == GMAIL_LABEL_TRASH:
            # This is the trash model, so now we add it to model data
            self.insert_email(email)
        elif self.label_id in to_remove:
            self.remove_email(email.get('message_id'))
            self._maybe_load_more_data()
Beispiel #5
0
    def handle_contact_removed(self, error=''):
        # Trying to remove a contact that was edited in the meantime won't produce an error.
        # Trying to remove a contact that was removed in the meantime will produce a 404 error.
        if error:
            error_code = get_error_code(error)
            if error_code == 404:
                LOG.warning(
                    "Failed to remove the contact, it was already removed.")
                self.on_error.emit(
                    "Can't remove that contact because it was already removed."
                )
            else:
                LOG.error(f"Failed to remove the contact. Error: {error}")
                self.on_error.emit("Failed to remove the contact.")

            self.sync_helper.pull_event()
            self.sync_helper.push_next_event()
            return

        # we got a successful response back, now remove the event
        self.sync_helper.pull_event()
        LOG.info("Contact completely removed.")
        # Now send next event if there's any left in the queue
        self.sync_helper.push_next_event()
Beispiel #6
0
    def handle_contact_edited(self, name, email, resourceName, etag, error=''):
        # Trying to edit a contact that was edited in the meantime will produce a 400 error.
        # In which case you have to update the contact before you can edit it.
        # Trying to edit a contact that was deleted in the meantime will produce a 404 error.
        if error:
            error_code = get_error_code(error)
            _, _, payload, contact = self.sync_helper.pull_event()
            ulid = contact.get('ulid')
            if error_code == 400:
                # Report error -> revert contact data -> remove all events associated with that contact.
                LOG.error(f"Failed to edit a contact. Error: {error}")
                self.on_error.emit(
                    "Can't edit that contact, because it was edited by someone else."
                )

                old_name = payload['contact']['name']
                old_email = payload['contact']['email']
                contact['name'] = old_name
                contact['email'] = old_email

                events = self.sync_helper.events()
                idx = 0
                while idx < len(events):
                    _, topic, payload, con = events[idx]
                    if con.get('ulid'):
                        self.sync_helper.remove_event(idx)
                    else:
                        idx += 1

            elif error_code == 404:
                # Report error -> remove contact -> remove all associated events -> reset UI.
                LOG.error(f"Failed to edit a contact. Error: {error}")
                self.on_error.emit(
                    "Can't edit that contact, because it was already deleted.")

                for idx, con in enumerate(self._data):
                    if con.get('ulid') == ulid:
                        self._data.pop(idx)
                        self.end = min(self.begin + self.page_length,
                                       len(self._data))
                        break

                events = self.sync_helper.events()
                idx = 0
                while idx < len(events):
                    _, topic, payload, con = events[idx]
                    if con.get('ulid'):
                        self.sync_helper.remove_event(idx)
                    else:
                        idx += 1

                self.beginResetModel()
                self._displayed_data = self._data[self.begin:self.end]
                self.endResetModel()
            else:
                LOG.error(f"Failed to edit the contact. Error: {error}")
                self.on_error.emit("Failed to edit the contact.")

            self.sync_helper.push_next_event()
            return

        _, _, _, contact = self.sync_helper.pull_event()
        ulid = contact.get('ulid')
        found = False
        for idx, con in enumerate(self._data):
            if con.get('ulid') == ulid:
                # Update etag which will certainly change
                # I am not sure about name, email or resourceName, but I think they won't change
                self._data[idx]['etag'] = etag
                found = True
        # NOTE: What is someone edited the contact twice, that's why we have to go through
        #       all queued up events as well, and make sure they are updated with new information.
        #       Or if event was deleted of course, but that's more obvious.
        for event in self.sync_helper.events():
            _, topic, payload, contact = event
            if contact.get('ulid') == ulid:
                found = True
                if topic == 'edit_contact':
                    # We only need to update etag of the contact in the payload, because etag of the
                    # underlying contact has already been updated in the data list iteration. And we
                    # don't really use it in the intermediate steps, which means even if the contact
                    # was deleted and thus it's etag not updated, it's okay.
                    payload['contact']['etag'] = etag
                    # We break here, because there is no need to update other edit_contact events,
                    # because response to this event will update them anyways.
                    break
        # If found is False something really went wrong.
        assert found is True

        LOG.info("Contact successfully edited(name, email):", name, email)
        self.sync_helper.push_next_event()