[geary/mjog/search-refinement: 4/14] Add Geary.Account::list_local_email_async



commit b3488f6cf93b22e65e46303e1a2a921cd213b33c
Author: Michael Gratton <mike vee net>
Date:   Tue Dec 10 13:36:36 2019 +1100

    Add Geary.Account::list_local_email_async
    
    Add new method and implementation to support breaking search folder
    impl out of the ImapDb package.

 src/engine/api/geary-account.vala                  | 16 ++++++
 src/engine/imap-db/imap-db-account.vala            | 42 ++++++++++++++++
 .../imap-engine/imap-engine-generic-account.vala   | 23 ++++++---
 test/engine/api/geary-account-mock.vala            | 12 +++++
 test/engine/imap-db/imap-db-account-test.vala      | 57 ++++++++++++++++++++++
 5 files changed, 144 insertions(+), 6 deletions(-)
---
diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala
index 0bb90aba..54b1d9db 100644
--- a/src/engine/api/geary-account.vala
+++ b/src/engine/api/geary-account.vala
@@ -425,6 +425,22 @@ public abstract class Geary.Account : BaseObject, Logging.Source {
     public abstract async Geary.Email local_fetch_email_async(Geary.EmailIdentifier email_id,
         Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error;
 
+    /**
+     * Return a collection of email with the given identifiers.
+     *
+     * The returned collection will be in the same order as the
+     * natural ordering of the given identifiers.
+     *
+     * Throws {@link EngineError.NOT_FOUND} if any email is not found
+     * and {@link EngineError.INCOMPLETE_MESSAGE} if the fields aren't
+     * available.
+     */
+    public abstract async Gee.List<Email> list_local_email_async(
+        Gee.Collection<EmailIdentifier> ids,
+        Email.Field required_fields,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error;
+
     /**
      * Create a new {@link SearchQuery} for this {@link Account}.
      *
diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala
index c50a7b5a..59c60125 100644
--- a/src/engine/imap-db/imap-db-account.vala
+++ b/src/engine/imap-db/imap-db-account.vala
@@ -804,6 +804,48 @@ private class Geary.ImapDB.Account : BaseObject {
         return search_matches;
     }
 
+    public async Gee.List<Email>? list_email(Gee.Collection<EmailIdentifier> ids,
+                                             Email.Field required_fields,
+                                             GLib.Cancellable? cancellable = null)
+    throws GLib.Error {
+        check_open();
+
+        var results = new Gee.ArrayList<Email>();
+        yield db.exec_transaction_async(Db.TransactionType.RO, (cx) => {
+                foreach (var id in ids) {
+                    // TODO: once we have a way of deleting messages, we won't be able
+                    // to assume that a row id will point to the same email outside of
+                    // transactions, because SQLite will reuse row ids.
+                    Geary.Email.Field db_fields;
+                    MessageRow row = Geary.ImapDB.Folder.do_fetch_message_row(
+                        cx, id.message_id, required_fields, out db_fields, cancellable
+                    );
+                    if (!row.fields.fulfills(required_fields)) {
+                        throw new EngineError.INCOMPLETE_MESSAGE(
+                            "Message %s only fulfills %Xh fields (required: %Xh)",
+                            id.to_string(), row.fields, required_fields
+                        );
+                    }
+
+                    Email email = row.to_email(id);
+                    Attachment.add_attachments(
+                        cx,
+                        this.db.attachments_path,
+                        email,
+                        id.message_id,
+                        cancellable
+                    );
+
+                    results.add(email);
+                }
+                return Db.TransactionOutcome.DONE;
+            },
+            cancellable
+        );
+
+        return results;
+    }
+
     public async Geary.Email fetch_email_async(ImapDB.EmailIdentifier email_id,
         Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error {
         check_open();
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index 4edcc80e..7599ee06 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -512,6 +512,16 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         return (Gee.Collection<ImapDB.EmailIdentifier>) ids;
     }
 
+    /** {@inheritDoc} */
+    public override async SearchQuery new_search_query(string query,
+                                                       SearchQuery.Strategy strategy,
+                                                       GLib.Cancellable? cancellable)
+        throws GLib.Error {
+        return yield new ImapDB.SearchQuery(
+            this, local, query, strategy, cancellable
+        );
+    }
+
     public override async Gee.MultiMap<Geary.Email, Geary.FolderPath?>? local_search_message_id_async(
         Geary.RFC822.MessageID message_id, Geary.Email.Field requested_fields, bool partial_ok,
         Gee.Collection<Geary.FolderPath?>? folder_blacklist, Geary.EmailFlags? flag_blacklist,
@@ -526,12 +536,13 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
     }
 
     /** {@inheritDoc} */
-    public override async SearchQuery new_search_query(string query,
-                                                       SearchQuery.Strategy strategy,
-                                                       GLib.Cancellable? cancellable)
-        throws GLib.Error {
-        return yield new ImapDB.SearchQuery(
-            this, local, query, strategy, cancellable
+    public override async Gee.List<Email> list_local_email_async(
+        Gee.Collection<EmailIdentifier> ids,
+        Email.Field required_fields,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error {
+        return yield local.list_email(
+            check_ids(ids), required_fields, cancellable
         );
     }
 
diff --git a/test/engine/api/geary-account-mock.vala b/test/engine/api/geary-account-mock.vala
index 07551eb1..9f074d62 100644
--- a/test/engine/api/geary-account-mock.vala
+++ b/test/engine/api/geary-account-mock.vala
@@ -204,6 +204,18 @@ public class Geary.MockAccount : Account, MockObject {
         );
     }
 
+    public override async Gee.List<Email> list_local_email_async(
+        Gee.Collection<EmailIdentifier> ids,
+        Email.Field required_fields,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error {
+        return object_or_throw_call<Gee.List<Email>>(
+            "list_local_email_async",
+            {ids, box_arg(required_fields), cancellable},
+            new EngineError.NOT_FOUND("Mock call")
+        );
+    }
+
     public override async Email local_fetch_email_async(EmailIdentifier email_id,
                                                         Email.Field required_fields,
                                                         Cancellable? cancellable = null)
diff --git a/test/engine/imap-db/imap-db-account-test.vala b/test/engine/imap-db/imap-db-account-test.vala
index a82fb1ce..3d1e90ab 100644
--- a/test/engine/imap-db/imap-db-account-test.vala
+++ b/test/engine/imap-db/imap-db-account-test.vala
@@ -26,6 +26,7 @@ class Geary.ImapDB.AccountTest : TestCase {
         add_test("fetch_base_folder", fetch_base_folder);
         add_test("fetch_child_folder", fetch_child_folder);
         add_test("fetch_nonexistent_folder", fetch_nonexistent_folder);
+        add_test("list_local_email", list_local_email);
     }
 
     public override void set_up() throws GLib.Error {
@@ -310,4 +311,60 @@ class Geary.ImapDB.AccountTest : TestCase {
         }
     }
 
+    public void list_local_email() throws GLib.Error {
+        Email.Field fixture_fields = Email.Field.RECEIVERS;
+        string fixture_to = "test1 example com";
+        this.account.db.exec(
+            "INSERT INTO MessageTable (id, fields, to_field) " +
+            "VALUES (1, %d, '%s');".printf(fixture_fields, fixture_to)
+        );
+        this.account.db.exec(
+            "INSERT INTO MessageTable (id, fields, to_field) " +
+            "VALUES (2, %d, '%s');".printf(fixture_fields, fixture_to)
+        );
+
+        this.account.list_email.begin(
+            iterate_array<Geary.ImapDB.EmailIdentifier>({
+                    new EmailIdentifier(1, null),
+                    new EmailIdentifier(2, null)
+                }).to_linked_list(),
+            Email.Field.RECEIVERS,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        Gee.List<Email> result = this.account.list_email.end(
+            async_result()
+        );
+
+        assert_int(2, result.size, "Not enough email listed");
+        assert_true(new EmailIdentifier(1, null).equal_to(result[0].id));
+        assert_true(new EmailIdentifier(2, null).equal_to(result[1].id));
+
+        this.account.list_email.begin(
+            Collection.single(new EmailIdentifier(3, null)),
+            Email.Field.RECEIVERS,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        try {
+            this.account.list_email.end(async_result());
+            assert_not_reached();
+        } catch (EngineError.NOT_FOUND error) {
+            // All good
+        }
+
+        this.account.list_email.begin(
+            Collection.single(new EmailIdentifier(1, null)),
+            Email.Field.BODY,
+            null,
+            (obj, ret) => { async_complete(ret); }
+        );
+        try {
+            this.account.list_email.end(async_result());
+            assert_not_reached();
+        } catch (EngineError.INCOMPLETE_MESSAGE error) {
+            // All good
+        }
+    }
+
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]