[geary/mjog/invert-folder-class-hierarchy: 51/72] Geary.Folder: Add flags for options when retrieving specific email




commit 3513ab9442cd297119bba93e9cc5748304b7cea9
Author: Michael Gratton <mike vee net>
Date:   Sat Feb 20 10:40:48 2021 +1100

    Geary.Folder: Add flags for options when retrieving specific email
    
    Although it's possible to avoid doing a get for one or more email that
    doesn't meet the field requirements by doing a fields-only get, working
    out which email satisfies the field requirements, then doing a full
    get, this is a non-trivial amount of work and double the number of
    round-trips needed to local storage.
    
    To accommodate this use case, add `GetFlags` to folder and add these
    as a parameter to `get_email_by_id` and `get_email_by_multiple_id`
    on `Geary.Folder` `Geary.Account` and `Geary.App.EmailStore`, and
    implement as needed.

 .../application-email-store-factory.vala           |   4 +-
 .../application-notification-plugin-context.vala   |   1 +
 .../application/application-plugin-manager.vala    |   1 +
 src/client/composer/composer-widget.vala           |   1 +
 .../conversation-list/conversation-list-store.vala |   1 +
 .../conversation-viewer/conversation-email.vala    |   1 +
 .../conversation-viewer/conversation-list-box.vala |   1 +
 .../plugin/mail-merge/mail-merge-folder.vala       |  16 +-
 src/client/plugin/mail-merge/mail-merge.vala       |   1 +
 src/engine/api/geary-account.vala                  |  54 ++++--
 src/engine/api/geary-folder.vala                   |  44 +++--
 src/engine/app/app-conversation-monitor.vala       |   5 +-
 src/engine/app/app-email-store.vala                |  15 +-
 src/engine/app/app-search-folder.vala              |  14 +-
 .../app/email-store/app-fetch-operation.vala       |   8 +-
 src/engine/app/email-store/app-list-operation.vala |   6 +-
 src/engine/imap-db/imap-db-account.vala            |  24 ++-
 src/engine/imap-db/imap-db-folder.vala             |  23 ++-
 .../imap-engine/imap-engine-generic-account.vala   |  20 ++-
 .../imap-engine/imap-engine-minimal-folder.vala    |  14 +-
 src/engine/outbox/outbox-folder.vala               |   6 +-
 src/engine/smtp/smtp-client-service.vala           |   4 +-
 test/engine/imap-db/imap-db-account-test.vala      |  15 +-
 .../imap-engine-generic-account-test.vala          | 181 +++++++++++++++++++++
 .../imap-engine-minimal-folder-test.vala           | 107 +++++++++++-
 test/mock/mock-account.vala                        |  30 ++--
 test/mock/mock-folder.vala                         |  26 +--
 test/mock/mock-remote-folder.vala                  |  28 ++--
 28 files changed, 520 insertions(+), 131 deletions(-)
---
diff --git a/src/client/application/application-email-store-factory.vala 
b/src/client/application/application-email-store-factory.vala
index ab5c1daf6..10d4cdf09 100644
--- a/src/client/application/application-email-store-factory.vala
+++ b/src/client/application/application-email-store-factory.vala
@@ -72,6 +72,7 @@ internal class Application.EmailStoreFactory : Geary.BaseObject {
                     yield context.emails.get_multiple_email_by_id(
                         found_accounts.get(context),
                         REQUIRED_FIELDS,
+                        NONE,
                         context.cancellable
                     );
                 if (!batch.is_empty) {
@@ -194,9 +195,10 @@ internal class Application.EmailStoreFactory : Geary.BaseObject {
             throws GLib.Error {
             if (!(Geary.Email.REQUIRED_FOR_MESSAGE in this.backing.fields)) {
                 Geary.Account account = this.account.backing.account;
-                this.backing = yield account.local_fetch_email_async(
+                this.backing = yield account.get_email_by_id(
                     this.backing.id,
                     Geary.Email.REQUIRED_FOR_MESSAGE | this.backing.fields,
+                    NONE,
                     cancellable
                 );
             }
diff --git a/src/client/application/application-notification-plugin-context.vala 
b/src/client/application/application-notification-plugin-context.vala
index 4201ecc9f..759ddd434 100644
--- a/src/client/application/application-notification-plugin-context.vala
+++ b/src/client/application/application-notification-plugin-context.vala
@@ -292,6 +292,7 @@ internal class Application.NotificationPluginContext :
                 email = yield folder.get_multiple_email_by_id(
                     email_ids,
                     REQUIRED_FIELDS,
+                    NONE,
                     info.cancellable
                 );
             } catch (GLib.Error err) {
diff --git a/src/client/application/application-plugin-manager.vala 
b/src/client/application/application-plugin-manager.vala
index 39cc915c4..0320ace1d 100644
--- a/src/client/application/application-plugin-manager.vala
+++ b/src/client/application/application-plugin-manager.vala
@@ -168,6 +168,7 @@ public class Application.PluginManager : GLib.Object {
                 email = yield source_impl.backing.emails.get_multiple_email_by_id(
                     Geary.Collection.single(id),
                     Composer.Widget.REQUIRED_FIELDS,
+                    NONE,
                     source_impl.backing.cancellable
                 );
             } catch (GLib.Error err) {
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index 912f63712..23dccb29c 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -696,6 +696,7 @@ public class Composer.Widget : Gtk.EventBox, Geary.BaseInterface {
                 yield this.sender_context.emails.get_multiple_email_by_id(
                     Geary.Collection.single(context.id),
                     REQUIRED_FIELDS,
+                    NONE,
                     this.sender_context.cancellable
                 );
             if (email.is_empty) {
diff --git a/src/client/conversation-list/conversation-list-store.vala 
b/src/client/conversation-list/conversation-list-store.vala
index ad5c07e9d..fc8d0cdef 100644
--- a/src/client/conversation-list/conversation-list-store.vala
+++ b/src/client/conversation-list/conversation-list-store.vala
@@ -208,6 +208,7 @@ public class ConversationListStore : Gtk.ListStore {
             emails = yield email_store.get_multiple_email_by_id(
                 emails_needing_previews,
                 WITH_PREVIEW_FIELDS,
+                NONE,
                 cancellable
             );
         } catch (GLib.IOError.CANCELLED err) {
diff --git a/src/client/conversation-viewer/conversation-email.vala 
b/src/client/conversation-viewer/conversation-email.vala
index 65460d8a5..89d4e61b5 100644
--- a/src/client/conversation-viewer/conversation-email.vala
+++ b/src/client/conversation-viewer/conversation-email.vala
@@ -360,6 +360,7 @@ public class ConversationEmail : Gtk.Box, Geary.BaseInterface {
                 this.email = yield this.email_store.get_email_by_id(
                     this.email.id,
                     REQUIRED_FOR_LOAD,
+                    NONE,
                     this.load_cancellable
                 );
                 loaded = true;
diff --git a/src/client/conversation-viewer/conversation-list-box.vala 
b/src/client/conversation-viewer/conversation-list-box.vala
index 46b71b783..802e209f6 100644
--- a/src/client/conversation-viewer/conversation-list-box.vala
+++ b/src/client/conversation-viewer/conversation-list-box.vala
@@ -1156,6 +1156,7 @@ public class ConversationListBox : Gtk.ListBox, Geary.BaseInterface {
         Geary.Email full_email = yield this.email_store.get_email_by_id(
             id,
             REQUIRED_FIELDS | ConversationEmail.REQUIRED_FOR_CONSTRUCT,
+            NONE,
             this.cancellable
         );
 
diff --git a/src/client/plugin/mail-merge/mail-merge-folder.vala 
b/src/client/plugin/mail-merge/mail-merge-folder.vala
index aa7e0cc92..d8f809eb8 100644
--- a/src/client/plugin/mail-merge/mail-merge-folder.vala
+++ b/src/client/plugin/mail-merge/mail-merge-folder.vala
@@ -192,10 +192,12 @@ public class MailMerge.Folder : Geary.BaseObject,
     }
 
     /** {@inheritDoc} */
-    public async Geary.Email get_email_by_id(Geary.EmailIdentifier id,
-                                             Geary.Email.Field required_fields,
-                                             GLib.Cancellable? cancellable = null)
-        throws GLib.Error {
+    public async Geary.Email get_email_by_id(
+        Geary.EmailIdentifier id,
+        Geary.Email.Field required_fields = ALL,
+        Geary.Folder.GetFlags flags = NONE,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error {
         var email = this.email.get(id);
         if (email == null) {
             throw new Geary.EngineError.NOT_FOUND(
@@ -207,7 +209,8 @@ public class MailMerge.Folder : Geary.BaseObject,
 
     public async Gee.Set<Geary.Email> get_multiple_email_by_id(
         Gee.Collection<Geary.EmailIdentifier> ids,
-        Geary.Email.Field required_fields,
+        Geary.Email.Field required_fields = ALL,
+        Geary.Folder.GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error {
         var results = Geary.Email.new_identifier_based_set();
@@ -339,9 +342,10 @@ public class MailMerge.Folder : Geary.BaseObject,
         throws GLib.Error {
         var template = this.template;
         if (!template.fields.fulfills(Geary.Email.REQUIRED_FOR_MESSAGE)) {
-            template = yield this.account.local_fetch_email_async(
+            template = yield this.account.get_email_by_id(
                 template.id,
                 Geary.Email.REQUIRED_FOR_MESSAGE,
+                NONE,
                 cancellable
             );
         }
diff --git a/src/client/plugin/mail-merge/mail-merge.vala b/src/client/plugin/mail-merge/mail-merge.vala
index bf66da9a1..ebc9c5b13 100644
--- a/src/client/plugin/mail-merge/mail-merge.vala
+++ b/src/client/plugin/mail-merge/mail-merge.vala
@@ -418,6 +418,7 @@ public class Plugin.MailMerge :
             engine = yield account_context.emails.get_email_by_id(
                 engine.id,
                 global::MailMerge.Processor.REQUIRED_FIELDS,
+                NONE,
                 this.cancellable
             );
         }
diff --git a/src/engine/api/geary-account.vala b/src/engine/api/geary-account.vala
index 3cf727a7f..a514e6027 100644
--- a/src/engine/api/geary-account.vala
+++ b/src/engine/api/geary-account.vala
@@ -534,17 +534,38 @@ public abstract class Geary.Account : BaseObject, Logging.Source {
         Cancellable? cancellable = null) throws Error;
 
     /**
-     * Return a single email fulfilling the required fields.  The email to pull
-     * is identified by an EmailIdentifier from a previous call to
-     * local_search_message_id_async() or local_search_async().  Throw
-     * EngineError.NOT_FOUND if the email isn't found and
-     * EngineError.INCOMPLETE_MESSAGE if the fields aren't available.
-     */
-    public abstract async Geary.Email local_fetch_email_async(Geary.EmailIdentifier email_id,
-        Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error;
+     * Returns an email from local storage.
+     *
+     * The returned email object will have its property values set for
+     * at least all requested fields, others may or may not be. If is
+     * good practice for callers request only the fields be loaded
+     * that they actually require, since the time taken to load the
+     * message will be reduced as there will be less data to load from
+     * local storage.
+     *
+     * Note that for remote-backed folders, an email may not have yet
+     * been fully downloaded and hence might exist incomplete in local
+     * storage. If the requested fields are not available, {@link
+     * EngineError.INCOMPLETE_MESSAGE} is thrown, unless the {@link
+     * Folder.GetFlags.INCLUDING_PARTIAL} in specified. Connect to the {@link
+     * Account.email_complete} signal to be notified of when email is
+     * fully downloaded in this case.
+     *
+     * If any of the given email identifiers are not present in local
+     * storage, an {@link EngineError.NOT_FOUND} error is
+     * thrown.
+     *
+     * @see Folder.get_email_by_id
+     */
+    public abstract async Geary.Email get_email_by_id(
+        EmailIdentifier email_id,
+        Email.Field required_fields = ALL,
+        Folder.GetFlags flags = NONE,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error;
 
     /**
-     * Returns a set of non-contiguous email objects from local storage.
+     * Returns a set of email from local storage.
      *
      * Any {@link Gee.Collection} of email identifiers is accepted,
      * but the returned set will only contain one email for each
@@ -554,15 +575,20 @@ public abstract class Geary.Account : BaseObject, Logging.Source {
      * been fully downloaded and hence might exist incomplete in local
      * storage. If the requested fields are not available for all
      * given identifiers, {@link EngineError.INCOMPLETE_MESSAGE} is
-     * thrown. Connect to the {@link Account.email_complete} signal to
-     * be notified of when email is fully downloaded in this case.
+     * thrown, unless the {@link Folder.GetFlags.INCLUDING_PARTIAL} in
+     * specified. Connect to the {@link Account.email_complete} signal
+     * to be notified of when email is fully downloaded in this case.
+     *
+     * If any of the given email identifiers are not present in local
+     * storage, an {@link EngineError.NOT_FOUND} error is
+     * thrown.
      *
-     * If any of the given email identifiers are not present in the
-     * vector, an {@link EngineError.NOT_FOUND} error is thrown.
+     * @see Folder.get_multiple_email_by_id
      */
     public abstract async Gee.Set<Email> get_multiple_email_by_id(
         Gee.Collection<EmailIdentifier> ids,
-        Email.Field required_fields,
+        Email.Field required_fields = ALL,
+        Folder.GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error;
 
diff --git a/src/engine/api/geary-folder.vala b/src/engine/api/geary-folder.vala
index 0aaa82207..8e1aee803 100644
--- a/src/engine/api/geary-folder.vala
+++ b/src/engine/api/geary-folder.vala
@@ -494,7 +494,23 @@ public interface Geary.Folder : GLib.Object, Logging.Source {
     }
 
     /**
-     * Flags for modifying the ranges of email retrieved from the vector.
+     * Flags modifying retrieval of specific email from the vector.
+     *
+     * @see get_email_by_id
+     * @see get_multiple_email_by_id
+     */
+    [Flags]
+    public enum GetFlags {
+
+        NONE = 0,
+
+        /** Include email that only partially matches the requested fields. */
+        INCLUDING_PARTIAL;
+
+    }
+
+    /**
+     * Flags for modifying retrieval of ranges of email from the vector.
      *
      * @see list_email_range_by_id
      */
@@ -658,7 +674,7 @@ public interface Geary.Folder : GLib.Object, Logging.Source {
     throws GLib.Error;
 
     /**
-     * Returns email from the folder's vector.
+     * Returns an email from the folder's vector.
      *
      * The returned email object will have its property values set for
      * at least all requested fields, others may or may not be. If is
@@ -670,21 +686,25 @@ public interface Geary.Folder : GLib.Object, Logging.Source {
      * Note that for remote-backed folders, an email may not have yet
      * been fully downloaded and hence might exist incomplete in local
      * storage. If the requested fields are not available, {@link
-     * EngineError.INCOMPLETE_MESSAGE} is thrown. Connect to the
-     * {@link Account.email_complete} signal to be notified of when
-     * email is fully downloaded in this case.
+     * EngineError.INCOMPLETE_MESSAGE} is thrown, unless the {@link
+     * GetFlags.INCLUDING_PARTIAL} in specified. Connect to the {@link
+     * Account.email_complete} signal to be notified of when email is
+     * fully downloaded in this case.
      *
      * If the given email identifier is not present in the vector, an
      * {@link EngineError.NOT_FOUND} error is thrown.
+     *
+     * @see Account.get_email_by_id
      */
     public abstract async Geary.Email get_email_by_id(
         EmailIdentifier email_id,
-        Email.Field required_fields,
+        Email.Field required_fields = ALL,
+        GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error;
 
     /**
-     * Returns a set of non-contiguous emails from the folder's vector.
+     * Returns a set of emails from the folder's vector.
      *
      * Similar in contract to {@link get_email_by_id}, but for a
      * collection of {@link Geary.EmailIdentifier}s rather than a
@@ -698,15 +718,19 @@ public interface Geary.Folder : GLib.Object, Logging.Source {
      * been fully downloaded and hence might exist incomplete in local
      * storage. If the requested fields are not available for all
      * given identifiers, {@link EngineError.INCOMPLETE_MESSAGE} is
-     * thrown. Connect to the {@link Account.email_complete} signal to
-     * be notified of when email is fully downloaded in this case.
+     * thrown, unless the {@link GetFlags.INCLUDING_PARTIAL} in
+     * specified. Connect to the {@link Account.email_complete} signal
+     * to be notified of when email is fully downloaded in this case.
      *
      * If any of the given email identifiers are not present in the
      * vector, an {@link EngineError.NOT_FOUND} error is thrown.
+     *
+     * @see Account.get_multiple_email_by_id
      */
     public abstract async Gee.Set<Email> get_multiple_email_by_id(
         Gee.Collection<EmailIdentifier> ids,
-        Email.Field required_fields,
+        Email.Field required_fields = ALL,
+        GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error;
 
diff --git a/src/engine/app/app-conversation-monitor.vala b/src/engine/app/app-conversation-monitor.vala
index 0fcaa92f8..5fdd0bf5d 100644
--- a/src/engine/app/app-conversation-monitor.vala
+++ b/src/engine/app/app-conversation-monitor.vala
@@ -500,7 +500,7 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
         try {
             Gee.Collection<Geary.Email> messages =
                 yield this.base_folder.get_multiple_email_by_id(
-                    ids, required_fields, this.operation_cancellable
+                    ids, required_fields, NONE, this.operation_cancellable
                 );
 
             if (!messages.is_empty) {
@@ -532,7 +532,7 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
         // First just get the bare minimum we need to determine if we even
         // care about the messages.
         Gee.Set<Geary.Email> emails = yield folder.get_multiple_email_by_id(
-            ids, REFERENCES, this.operation_cancellable
+            ids, REFERENCES, NONE, this.operation_cancellable
         );
         if (!emails.is_empty) {
             var relevant_ids = new Gee.HashSet<Geary.EmailIdentifier>();
@@ -551,6 +551,7 @@ public class Geary.App.ConversationMonitor : BaseObject, Logging.Source {
                 emails = yield folder.get_multiple_email_by_id(
                     relevant_ids,
                     required_fields,
+                    NONE,
                     this.operation_cancellable
                 );
             } else {
diff --git a/src/engine/app/app-email-store.vala b/src/engine/app/app-email-store.vala
index a6cf28d8e..ffa448f14 100644
--- a/src/engine/app/app-email-store.vala
+++ b/src/engine/app/app-email-store.vala
@@ -65,12 +65,14 @@ public class Geary.App.EmailStore : BaseObject {
      * Fetches any EmailIdentifier regardless of what folder it's in.
      */
     public async Email get_email_by_id(EmailIdentifier email_id,
-                                       Email.Field required_fields,
+                                       Email.Field required_fields = ALL,
+                                       Folder.GetFlags flags = NONE,
                                        GLib.Cancellable? cancellable = null)
         throws GLib.Error {
-        FetchOperation op = new Geary.App.FetchOperation(required_fields);
-        yield do_folder_operation_async(op,
-            Geary.iterate<Geary.EmailIdentifier>(email_id).to_array_list(), cancellable);
+            FetchOperation op = new Geary.App.FetchOperation(required_fields, flags);
+        yield do_folder_operation_async(
+            op, Collection.single(email_id), cancellable
+        );
 
         if (op.result == null)
             throw new EngineError.NOT_FOUND("Couldn't fetch email ID %s", email_id.to_string());
@@ -82,10 +84,11 @@ public class Geary.App.EmailStore : BaseObject {
      */
     public async Gee.Collection<Geary.Email> get_multiple_email_by_id(
         Gee.Collection<EmailIdentifier> emails,
-        Email.Field required_fields,
+        Email.Field required_fields = ALL,
+        Folder.GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error {
-        var op = new Geary.App.ListOperation(required_fields);
+        var op = new Geary.App.ListOperation(required_fields, flags);
         yield do_folder_operation_async(op, emails, cancellable);
         return op.results;
     }
diff --git a/src/engine/app/app-search-folder.vala b/src/engine/app/app-search-folder.vala
index 3ddd01028..ea3771e66 100644
--- a/src/engine/app/app-search-folder.vala
+++ b/src/engine/app/app-search-folder.vala
@@ -219,23 +219,25 @@ public class Geary.App.SearchFolder : BaseObject,
 
     /** {@inheritDoc} */
     public async Email get_email_by_id(EmailIdentifier fetch,
-                                       Email.Field required_fields,
+                                       Email.Field required_fields = ALL,
+                                       GetFlags flags = NONE,
                                        GLib.Cancellable? cancellable = null)
         throws GLib.Error {
         require_id(fetch);
-        return yield this.account.local_fetch_email_async(
-            fetch, required_fields, cancellable
+        return yield this.account.get_email_by_id(
+            fetch, required_fields, flags, cancellable
         );
     }
 
     /** {@inheritDoc} */
     public async Gee.Set<Email> get_multiple_email_by_id(
         Gee.Collection<Geary.EmailIdentifier> ids,
-        Email.Field required_fields,
+        Email.Field required_fields = ALL,
+        GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error {
         return yield this.account.get_multiple_email_by_id(
-            check_ids(ids), required_fields, cancellable
+            check_ids(ids), required_fields, flags, cancellable
         );
     }
 
@@ -324,6 +326,7 @@ public class Geary.App.SearchFolder : BaseObject,
                 results = yield this.account.get_multiple_email_by_id(
                     engine_ids,
                     required_fields,
+                    NONE,
                     cancellable
                 );
             } catch (GLib.Error error) {
@@ -550,6 +553,7 @@ public class Geary.App.SearchFolder : BaseObject,
                     yield this.account.get_multiple_email_by_id(
                         id_results,
                         PROPERTIES,
+                        NONE,
                         cancellable
                     );
 
diff --git a/src/engine/app/email-store/app-fetch-operation.vala 
b/src/engine/app/email-store/app-fetch-operation.vala
index cf1fc98b3..a14c7d8bb 100644
--- a/src/engine/app/email-store/app-fetch-operation.vala
+++ b/src/engine/app/email-store/app-fetch-operation.vala
@@ -9,11 +9,13 @@ private class Geary.App.FetchOperation : Geary.App.AsyncFolderOperation {
     public override Type folder_type { get { return typeof(Geary.Folder); } }
 
     public Email? result { get; private set; default = null; }
-    public Email.Field required_fields;
+    public Email.Field required_fields { get; private set; }
+    public Folder.GetFlags flags { get; private set; }
 
 
-    public FetchOperation(Email.Field required_fields) {
+    public FetchOperation(Email.Field required_fields, Folder.GetFlags flags) {
         this.required_fields = required_fields;
+        this.flags = flags;
     }
 
     public override async Gee.Collection<EmailIdentifier> execute_async(
@@ -23,7 +25,7 @@ private class Geary.App.FetchOperation : Geary.App.AsyncFolderOperation {
     ) throws GLib.Error {
         var id = Collection.first(ids);
         this.result = yield folder.get_email_by_id(
-            id, required_fields, cancellable
+            id, required_fields, this.flags, cancellable
         );
         return iterate<EmailIdentifier>(id).to_array_list();
     }
diff --git a/src/engine/app/email-store/app-list-operation.vala 
b/src/engine/app/email-store/app-list-operation.vala
index 7d288ccb6..75711501f 100644
--- a/src/engine/app/email-store/app-list-operation.vala
+++ b/src/engine/app/email-store/app-list-operation.vala
@@ -10,11 +10,13 @@ private class Geary.App.ListOperation : Geary.App.AsyncFolderOperation {
 
     public Gee.Collection<Geary.Email> results { get; private set; }
     public Email.Field required_fields { get; private set; }
+    public Folder.GetFlags flags { get; private set; }
 
 
-    public ListOperation(Email.Field required_fields) {
+    public ListOperation(Email.Field required_fields, Folder.GetFlags flags) {
         this.results = new Gee.HashSet<Geary.Email>();
         this.required_fields = required_fields;
+        this.flags = flags;
     }
 
     public override async Gee.Collection<Geary.EmailIdentifier> execute_async(
@@ -23,7 +25,7 @@ private class Geary.App.ListOperation : Geary.App.AsyncFolderOperation {
         GLib.Cancellable? cancellable
     ) throws GLib.Error {
         this.results = yield folder.get_multiple_email_by_id(
-            ids, required_fields, cancellable
+            ids, required_fields, this.flags, cancellable
         );
         return ids;
     }
diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala
index 6f575ca93..978195ad9 100644
--- a/src/engine/imap-db/imap-db-account.vala
+++ b/src/engine/imap-db/imap-db-account.vala
@@ -666,10 +666,12 @@ private class Geary.ImapDB.Account : BaseObject {
         return search_matches;
     }
 
-    public async Gee.Set<Email> list_email(Gee.Collection<EmailIdentifier> ids,
-                                           Email.Field required_fields,
-                                           GLib.Cancellable? cancellable = null)
-        throws GLib.Error {
+    public async Gee.Set<Email> fetch_muliple_email(
+        Gee.Collection<EmailIdentifier> ids,
+        Email.Field required_fields,
+        Geary.Folder.GetFlags flags,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error {
         check_open();
 
         var results = Email.new_identifier_based_set();
@@ -682,7 +684,8 @@ private class Geary.ImapDB.Account : BaseObject {
                     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)) {
+                    if (!row.fields.fulfills(required_fields) &&
+                        !(Geary.Folder.GetFlags.INCLUDING_PARTIAL in flags)) {
                         throw new EngineError.INCOMPLETE_MESSAGE(
                             "Message %s only fulfills %Xh fields (required: %Xh)",
                             id.to_string(), row.fields, required_fields
@@ -708,8 +711,12 @@ private class Geary.ImapDB.Account : BaseObject {
         return results;
     }
 
-    public async Geary.Email fetch_email_async(ImapDB.EmailIdentifier email_id,
-        Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error {
+    public async Geary.Email fetch_email(
+        ImapDB.EmailIdentifier email_id,
+        Geary.Email.Field required_fields,
+        Geary.Folder.GetFlags flags,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error {
         check_open();
 
         Geary.Email? email = null;
@@ -721,7 +728,8 @@ private class Geary.ImapDB.Account : BaseObject {
             MessageRow row = Geary.ImapDB.Folder.do_fetch_message_row(
                 cx, email_id.message_id, required_fields, out db_fields, cancellable);
 
-            if (!row.fields.fulfills(required_fields))
+            if (!row.fields.fulfills(required_fields) &&
+                !(Geary.Folder.GetFlags.INCLUDING_PARTIAL in flags))
                 throw new EngineError.INCOMPLETE_MESSAGE(
                     "Message %s only fulfills %Xh fields (required: %Xh)",
                     email_id.to_string(), row.fields, required_fields);
diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala
index dd84b610d..43646b5a0 100644
--- a/src/engine/imap-db/imap-db-folder.vala
+++ b/src/engine/imap-db/imap-db-folder.vala
@@ -56,15 +56,15 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
         OLDEST_TO_NEWEST,
         ONLY_INCOMPLETE;
 
-        public bool is_all_set(LoadFlags flags) {
-            return (this & flags) == flags;
-        }
-
-        public bool include_marked_for_remove() {
-            return is_all_set(INCLUDE_MARKED_FOR_REMOVE);
+        public static LoadFlags from_folder_get(Geary.Folder.GetFlags flags) {
+            LoadFlags result = NONE;
+            if (Geary.Folder.GetFlags.INCLUDING_PARTIAL in flags) {
+                result |= PARTIAL_OK;
+            }
+            return result;
         }
 
-        public static LoadFlags from_folder_flags(Geary.Folder.ListFlags flags) {
+        public static LoadFlags from_folder_list(Geary.Folder.ListFlags flags) {
             LoadFlags result = NONE;
 
             if (flags.is_all_set(Geary.Folder.ListFlags.INCLUDING_ID))
@@ -78,6 +78,15 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
 
             return result;
         }
+
+        public bool is_all_set(LoadFlags flags) {
+            return (this & flags) == flags;
+        }
+
+        public bool include_marked_for_remove() {
+            return is_all_set(INCLUDE_MARKED_FOR_REMOVE);
+        }
+
     }
 
     private class LocationIdentifier {
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index b7097d0d5..298612317 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -598,19 +598,27 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
             message_id, requested_fields, partial_ok, folder_blacklist, flag_blacklist, cancellable);
     }
 
-    public override async Geary.Email local_fetch_email_async(Geary.EmailIdentifier email_id,
-        Geary.Email.Field required_fields, Cancellable? cancellable = null) throws Error {
-        return yield local.fetch_email_async(check_id(email_id), required_fields, cancellable);
+    /** {@inheritDoc} */
+    public override async Email get_email_by_id(
+        EmailIdentifier id,
+        Email.Field required_fields = ALL,
+        Folder.GetFlags flags = NONE,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error {
+        return yield this.local.fetch_email(
+            check_id(id), required_fields, flags, cancellable
+        );
     }
 
     /** {@inheritDoc} */
     public override async Gee.Set<Email> get_multiple_email_by_id(
         Gee.Collection<EmailIdentifier> ids,
-        Email.Field required_fields,
+        Email.Field required_fields = ALL,
+        Folder.GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error {
-        return yield local.list_email(
-            check_ids(ids), required_fields, cancellable
+        return yield this.local.fetch_muliple_email(
+            check_ids(ids), required_fields, flags, cancellable
         );
     }
 
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala 
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index c5331b6cb..0831ced82 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -884,14 +884,15 @@ private class Geary.ImapEngine.MinimalFolder : BaseObject,
 
     /** {@inheritDoc} */
     public async Email get_email_by_id(EmailIdentifier id,
-                                       Email.Field required_fields,
+                                       Email.Field required_fields = ALL,
+                                       GetFlags flags = NONE,
                                        GLib.Cancellable? cancellable = null)
         throws GLib.Error {
         check_id("get_email_by_id", id);
         return yield this.local_folder.fetch_email_async(
             (ImapDB.EmailIdentifier) id,
             required_fields,
-            NONE,
+            ImapDB.Folder.LoadFlags.from_folder_get(flags),
             cancellable
         );
     }
@@ -899,13 +900,14 @@ private class Geary.ImapEngine.MinimalFolder : BaseObject,
     /** {@inheritDoc} */
     public async Gee.Set<Email> get_multiple_email_by_id(
         Gee.Collection<Geary.EmailIdentifier> ids,
-        Email.Field required_fields,
+        Email.Field required_fields = ALL,
+        GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error {
         return yield this.local_folder.list_email_by_sparse_id_async(
             check_ids("get_multiple_email_by_id", ids),
             required_fields,
-            NONE,
+            ImapDB.Folder.LoadFlags.from_folder_get(flags),
             true,
             cancellable
         );
@@ -929,7 +931,7 @@ private class Geary.ImapEngine.MinimalFolder : BaseObject,
             imap_id,
             count,
             required_fields,
-            ImapDB.Folder.LoadFlags.from_folder_flags(flags),
+            ImapDB.Folder.LoadFlags.from_folder_list(flags),
             cancellable
         );
     }
@@ -1299,7 +1301,7 @@ private class Geary.ImapEngine.MinimalFolder : BaseObject,
             // locally possibly before the server notified that the
             // message exists. As such, fetch any missing parts from
             // the remote to ensure it is properly filled in.
-            yield get_email_by_id(op.created_id, ALL, cancellable);
+            yield get_email_by_id(op.created_id, ALL, NONE, cancellable);
         } else {
             // The server didn't return a UID for the new email, so do
             // a sync now to ensure it shows up immediately.
diff --git a/src/engine/outbox/outbox-folder.vala b/src/engine/outbox/outbox-folder.vala
index 9857a3964..d63bd3ba8 100644
--- a/src/engine/outbox/outbox-folder.vala
+++ b/src/engine/outbox/outbox-folder.vala
@@ -218,7 +218,8 @@ public class Geary.Outbox.Folder : BaseObject,
 
     /** {@inheritDoc} */
     public async Email get_email_by_id(Geary.EmailIdentifier id,
-                                       Email.Field required_fields,
+                                       Email.Field required_fields = ALL,
+                                       GetFlags flags = NONE,
                                        GLib.Cancellable? cancellable = null)
         throws GLib.Error {
         EmailIdentifier? outbox_id = id as EmailIdentifier;
@@ -241,7 +242,8 @@ public class Geary.Outbox.Folder : BaseObject,
     /** {@inheritDoc} */
     public async Gee.Set<Email> get_multiple_email_by_id(
         Gee.Collection<Geary.EmailIdentifier> ids,
-        Email.Field required_fields,
+        Email.Field required_fields = ALL,
+        GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error {
         var email = Email.new_identifier_based_set();
diff --git a/src/engine/smtp/smtp-client-service.vala b/src/engine/smtp/smtp-client-service.vala
index 39c38b854..2c4cd326c 100644
--- a/src/engine/smtp/smtp-client-service.vala
+++ b/src/engine/smtp/smtp-client-service.vala
@@ -252,7 +252,9 @@ public class Geary.Smtp.ClientService : Geary.ClientService {
             throw new SmtpError.AUTHENTICATION_FAILED("Credentials not loaded");
         }
 
-        Email email = yield this.outbox.get_email_by_id(id, ALL, cancellable);
+        Email email = yield this.outbox.get_email_by_id(
+            id, ALL, NONE, cancellable
+        );
 
         if (!email.email_flags.contains(EmailFlags.OUTBOX_SENT)) {
             RFC822.Message message = email.get_message();
diff --git a/test/engine/imap-db/imap-db-account-test.vala b/test/engine/imap-db/imap-db-account-test.vala
index db51d6b64..0eafb7b2e 100644
--- a/test/engine/imap-db/imap-db-account-test.vala
+++ b/test/engine/imap-db/imap-db-account-test.vala
@@ -323,16 +323,17 @@ class Geary.ImapDB.AccountTest : TestCase {
             "VALUES (2, %d, '%s');".printf(fixture_fields, fixture_to)
         );
 
-        this.account.list_email.begin(
+        this.account.fetch_muliple_email.begin(
             iterate_array<Geary.ImapDB.EmailIdentifier>({
                     new EmailIdentifier(1, null),
                     new EmailIdentifier(2, null)
                 }).to_linked_list(),
             Email.Field.RECEIVERS,
+            NONE,
             null,
             this.async_completion
         );
-        Gee.Set<Email> result = this.account.list_email.end(
+        Gee.Set<Email> result = this.account.fetch_muliple_email.end(
             async_result()
         );
 
@@ -342,27 +343,29 @@ class Geary.ImapDB.AccountTest : TestCase {
         assert_true(new EmailIdentifier(1, null).equal_to(list[0].id));
         assert_true(new EmailIdentifier(2, null).equal_to(list[1].id));
 
-        this.account.list_email.begin(
+        this.account.fetch_muliple_email.begin(
             Collection.single(new EmailIdentifier(3, null)),
             Email.Field.RECEIVERS,
+            NONE,
             null,
             this.async_completion
         );
         try {
-            this.account.list_email.end(async_result());
+            this.account.fetch_muliple_email.end(async_result());
             assert_not_reached();
         } catch (EngineError.NOT_FOUND error) {
             // All good
         }
 
-        this.account.list_email.begin(
+        this.account.fetch_muliple_email.begin(
             Collection.single(new EmailIdentifier(1, null)),
             Email.Field.BODY,
+            NONE,
             null,
             this.async_completion
         );
         try {
-            this.account.list_email.end(async_result());
+            this.account.fetch_muliple_email.end(async_result());
             assert_not_reached();
         } catch (EngineError.INCOMPLETE_MESSAGE error) {
             // All good
diff --git a/test/engine/imap-engine/imap-engine-generic-account-test.vala 
b/test/engine/imap-engine/imap-engine-generic-account-test.vala
index a65ca2221..b93dd64df 100644
--- a/test/engine/imap-engine/imap-engine-generic-account-test.vala
+++ b/test/engine/imap-engine/imap-engine-generic-account-test.vala
@@ -11,6 +11,36 @@ internal class Geary.ImapEngine.GenericAccountTest : AccountBasedTest {
     public GenericAccountTest() {
         base("Geary.ImapEngine.GenericAccountTest");
         add_test("to_email_identifier", to_email_identifier);
+        add_test("get_email_by_id", get_email_by_id);
+        add_test("get_email_by_id_partial", get_email_by_id_partial);
+        add_test("get_multiple_email_by_id", get_multiple_email_by_id);
+        add_test(
+            "get_multiple_email_by_id_partial", get_multiple_email_by_id_partial
+        );
+    }
+
+    public override void set_up() throws GLib.Error {
+        base.set_up();
+
+        this.local_account.db.exec(
+            "INSERT INTO FolderTable (id, name) VALUES (1, 'test');"
+        );
+        this.local_account.db.exec(
+            "INSERT INTO MessageTable (id, fields, to_field, from_field) VALUES " +
+            "(1, %d, '%s', null),".printf(
+                Email.Field.RECEIVERS,
+                "test1 example com"
+            ) +
+            "(2, %d, '%s', '%s');".printf(
+                (Email.Field.RECEIVERS | Email.Field.ORIGINATORS),
+                "test2 example com",
+                "sender example com"
+            )
+        );
+        this.local_account.db.exec("""
+            INSERT INTO MessageLocationTable (id, message_id, folder_id, ordering)
+                VALUES (1, 1, 1, 1), (2, 2, 1, 2);
+        """);
     }
 
     public void to_email_identifier() throws GLib.Error {
@@ -32,4 +62,155 @@ internal class Geary.ImapEngine.GenericAccountTest : AccountBasedTest {
         );
     }
 
+    public void get_email_by_id() throws GLib.Error {
+        var account = new_test_account();
+        var invalid_id = new ImapDB.EmailIdentifier(0, new Imap.UID(0));
+        var valid_id = new ImapDB.EmailIdentifier(1, new Imap.UID(1));
+
+        account.get_email_by_id.begin(
+            invalid_id, ALL, NONE, null, this.async_completion
+        );
+        try {
+            account.get_email_by_id.end(async_result());
+            assert_not_reached();
+        } catch (EngineError.NOT_FOUND err) {
+            // all good
+        }
+
+        account.get_email_by_id.begin(
+            valid_id, RECEIVERS, NONE, null, this.async_completion
+        );
+        var email = account.get_email_by_id.end(async_result());
+        assert_true(email.id.equal_to(valid_id));
+        assert_non_null(email.to);
+        assert_equal<int?>(email.to.size, 1);
+        assert_equal(email.to[0].address, "test1 example com");
+
+        account.get_email_by_id.begin(
+            valid_id, ALL, NONE, null, this.async_completion
+        );
+        try {
+            account.get_email_by_id.end(async_result());
+            assert_not_reached();
+        } catch (EngineError.INCOMPLETE_MESSAGE err) {
+            // all good
+        }
+    }
+
+    public void get_email_by_id_partial() throws GLib.Error {
+        var account = new_test_account();
+        var valid_id1 = new ImapDB.EmailIdentifier(1, new Imap.UID(1));
+        var valid_id2 = new ImapDB.EmailIdentifier(1, new Imap.UID(2));
+
+        // Get an email that actually has all requested flags
+
+        account.get_email_by_id.begin(
+            valid_id2,
+            ORIGINATORS|RECEIVERS,
+            INCLUDING_PARTIAL,
+            null,
+            this.async_completion
+        );
+        var complete = account.get_email_by_id.end(async_result());
+        assert_true(complete.id.equal_to(valid_id2));
+
+        // Get an  email missing requested flags
+
+        account.get_email_by_id.begin(
+            valid_id1,
+            ORIGINATORS|RECEIVERS,
+            INCLUDING_PARTIAL,
+            null,
+            this.async_completion
+        );
+        var incomplete = account.get_email_by_id.end(async_result());
+        assert_true(incomplete.id.equal_to(valid_id1));
+
+    }
+
+    public void get_multiple_email_by_id() throws GLib.Error {
+        var account = new_test_account();
+        var invalid_id = new ImapDB.EmailIdentifier(0, new Imap.UID(0));
+        var valid_id = new ImapDB.EmailIdentifier(1, new Imap.UID(1));
+
+        account.get_multiple_email_by_id.begin(
+            Collection.single(invalid_id), ALL, NONE, null, this.async_completion
+        );
+        try {
+            account.get_multiple_email_by_id.end(async_result());
+            assert_not_reached();
+        } catch (EngineError.NOT_FOUND err) {
+            // all good
+        }
+
+        account.get_multiple_email_by_id.begin(
+            Collection.single(valid_id), RECEIVERS, NONE, null, this.async_completion
+        );
+        var email = assert_collection(
+            account.get_multiple_email_by_id.end(async_result())
+        ).size(1)[0];
+        assert_true(email.id.equal_to(valid_id));
+        assert_non_null(email.to);
+        assert_equal<int?>(email.to.size, 1);
+        assert_equal(email.to[0].address, "test1 example com");
+
+        account.get_multiple_email_by_id.begin(
+            Collection.single(valid_id), ALL, NONE, null, this.async_completion
+        );
+        try {
+            account.get_multiple_email_by_id.end(async_result());
+            assert_not_reached();
+        } catch (EngineError.INCOMPLETE_MESSAGE err) {
+            // all good
+        }
+    }
+
+    public void get_multiple_email_by_id_partial() throws GLib.Error {
+        var account = new_test_account();
+        var valid_id1 = new ImapDB.EmailIdentifier(1, new Imap.UID(1));
+        var valid_id2 = new ImapDB.EmailIdentifier(2, new Imap.UID(2));
+
+        // get an email that does fulfil all fields
+
+        account.get_multiple_email_by_id.begin(
+            Collection.single(valid_id2),
+            ORIGINATORS|RECEIVERS,
+            INCLUDING_PARTIAL,
+            null,
+            this.async_completion
+        );
+        var complete = assert_collection(
+            account.get_multiple_email_by_id.end(async_result())
+        ).size(1)[0];
+        assert_true(complete.id.equal_to(valid_id2));
+
+        // get an email that does not fulfil all fields
+
+        account.get_multiple_email_by_id.begin(
+            Collection.single(valid_id1),
+            ORIGINATORS|RECEIVERS,
+            INCLUDING_PARTIAL,
+            null,
+            this.async_completion
+        );
+        var incomplete = assert_collection(
+            account.get_multiple_email_by_id.end(async_result())
+        ).size(1)[0];
+        assert_true(incomplete.id.equal_to(valid_id1));
+
+
+        // get a mix of both
+
+        account.get_multiple_email_by_id.begin(
+            new Gee.ArrayList<EmailIdentifier>.wrap({valid_id1,valid_id2}),
+            ORIGINATORS|RECEIVERS,
+            INCLUDING_PARTIAL,
+            null,
+            this.async_completion
+        );
+        assert_collection(
+            account.get_multiple_email_by_id.end(async_result())
+        ).size(2);
+    }
+
 }
diff --git a/test/engine/imap-engine/imap-engine-minimal-folder-test.vala 
b/test/engine/imap-engine/imap-engine-minimal-folder-test.vala
index f27da215a..e71d2b3fe 100644
--- a/test/engine/imap-engine/imap-engine-minimal-folder-test.vala
+++ b/test/engine/imap-engine/imap-engine-minimal-folder-test.vala
@@ -15,8 +15,14 @@ internal class Geary.ImapEngine.MinimalFolderTest : AccountBasedTest {
     public MinimalFolderTest() {
         base("Geary.ImapEngine.MinimalFolderTest");
         add_test("get_email_by_id", get_email_by_id);
+        add_test("get_email_by_id_partial", get_email_by_id_partial);
         add_test("get_multiple_email_by_id", get_multiple_email_by_id);
-        add_test("list_email_range_by_null_id", list_email_range_by_null_id);
+        add_test(
+            "get_multiple_email_by_id_partial", get_multiple_email_by_id_partial
+        );
+        add_test(
+            "list_email_range_by_null_id", list_email_range_by_null_id
+        );
         add_test(
             "list_email_range_by_non_null_id_descending",
             list_email_range_by_non_null_id_descending
@@ -79,7 +85,7 @@ internal class Geary.ImapEngine.MinimalFolderTest : AccountBasedTest {
         var valid_id = new ImapDB.EmailIdentifier(1, new Imap.UID(1));
 
         folder.get_email_by_id.begin(
-            invalid_id, ALL, null, this.async_completion
+            invalid_id, ALL, NONE, null, this.async_completion
         );
         try {
             folder.get_email_by_id.end(async_result());
@@ -89,7 +95,7 @@ internal class Geary.ImapEngine.MinimalFolderTest : AccountBasedTest {
         }
 
         folder.get_email_by_id.begin(
-            valid_id, RECEIVERS, null, this.async_completion
+            valid_id, RECEIVERS, NONE, null, this.async_completion
         );
         var email = folder.get_email_by_id.end(async_result());
         assert_true(email.id.equal_to(valid_id));
@@ -98,7 +104,7 @@ internal class Geary.ImapEngine.MinimalFolderTest : AccountBasedTest {
         assert_equal(email.to[0].address, "test1 example com");
 
         folder.get_email_by_id.begin(
-            valid_id, ALL, null, this.async_completion
+            valid_id, ALL, NONE, null, this.async_completion
         );
         try {
             folder.get_email_by_id.end(async_result());
@@ -108,6 +114,41 @@ internal class Geary.ImapEngine.MinimalFolderTest : AccountBasedTest {
         }
     }
 
+    public void get_email_by_id_partial() throws GLib.Error {
+        var folder = new MinimalFolder(
+            this.account,
+            this.local_folder,
+            NONE
+        );
+        var valid_id1 = new ImapDB.EmailIdentifier(1, new Imap.UID(1));
+        var valid_id2 = new ImapDB.EmailIdentifier(1, new Imap.UID(2));
+
+        // Get an email that actually has all requested flags
+
+        folder.get_email_by_id.begin(
+            valid_id2,
+            ORIGINATORS|RECEIVERS,
+            INCLUDING_PARTIAL,
+            null,
+            this.async_completion
+        );
+        var complete = folder.get_email_by_id.end(async_result());
+        assert_true(complete.id.equal_to(valid_id2));
+
+        // Get an  email missing requested flags
+
+        folder.get_email_by_id.begin(
+            valid_id1,
+            ORIGINATORS|RECEIVERS,
+            INCLUDING_PARTIAL,
+            null,
+            this.async_completion
+        );
+        var incomplete = folder.get_email_by_id.end(async_result());
+        assert_true(incomplete.id.equal_to(valid_id1));
+
+    }
+
     public void get_multiple_email_by_id() throws GLib.Error {
         var folder = new MinimalFolder(
             this.account,
@@ -118,7 +159,7 @@ internal class Geary.ImapEngine.MinimalFolderTest : AccountBasedTest {
         var valid_id = new ImapDB.EmailIdentifier(1, new Imap.UID(1));
 
         folder.get_multiple_email_by_id.begin(
-            Collection.single(invalid_id), ALL, null, this.async_completion
+            Collection.single(invalid_id), ALL, NONE, null, this.async_completion
         );
         try {
             folder.get_multiple_email_by_id.end(async_result());
@@ -128,7 +169,7 @@ internal class Geary.ImapEngine.MinimalFolderTest : AccountBasedTest {
         }
 
         folder.get_multiple_email_by_id.begin(
-            Collection.single(valid_id), RECEIVERS, null, this.async_completion
+            Collection.single(valid_id), RECEIVERS, NONE, null, this.async_completion
         );
         var email = assert_collection(
             folder.get_multiple_email_by_id.end(async_result())
@@ -139,7 +180,7 @@ internal class Geary.ImapEngine.MinimalFolderTest : AccountBasedTest {
         assert_equal(email.to[0].address, "test1 example com");
 
         folder.get_multiple_email_by_id.begin(
-            Collection.single(valid_id), ALL, null, this.async_completion
+            Collection.single(valid_id), ALL, NONE, null, this.async_completion
         );
         try {
             folder.get_multiple_email_by_id.end(async_result());
@@ -149,6 +190,58 @@ internal class Geary.ImapEngine.MinimalFolderTest : AccountBasedTest {
         }
     }
 
+    public void get_multiple_email_by_id_partial() throws GLib.Error {
+        var folder = new MinimalFolder(
+            this.account,
+            this.local_folder,
+            NONE
+        );
+        var valid_id1 = new ImapDB.EmailIdentifier(1, new Imap.UID(1));
+        var valid_id2 = new ImapDB.EmailIdentifier(2, new Imap.UID(2));
+
+        // get an email that does fulfil all fields
+
+        folder.get_multiple_email_by_id.begin(
+            Collection.single(valid_id2),
+            ORIGINATORS|RECEIVERS,
+            INCLUDING_PARTIAL,
+            null,
+            this.async_completion
+        );
+        var complete = assert_collection(
+            folder.get_multiple_email_by_id.end(async_result())
+        ).size(1)[0];
+        assert_true(complete.id.equal_to(valid_id2));
+
+        // get an email that does not fulfil all fields
+
+        folder.get_multiple_email_by_id.begin(
+            Collection.single(valid_id1),
+            ORIGINATORS|RECEIVERS,
+            INCLUDING_PARTIAL,
+            null,
+            this.async_completion
+        );
+        var incomplete = assert_collection(
+            folder.get_multiple_email_by_id.end(async_result())
+        ).size(1)[0];
+        assert_true(incomplete.id.equal_to(valid_id1));
+
+
+        // get a mix of both
+
+        folder.get_multiple_email_by_id.begin(
+            new Gee.ArrayList<EmailIdentifier>.wrap({valid_id1,valid_id2}),
+            ORIGINATORS|RECEIVERS,
+            INCLUDING_PARTIAL,
+            null,
+            this.async_completion
+        );
+        assert_collection(
+            folder.get_multiple_email_by_id.end(async_result())
+        ).size(2);
+    }
+
     public void list_email_range_by_null_id() throws GLib.Error {
         var folder = new MinimalFolder(
             this.account,
diff --git a/test/mock/mock-account.vala b/test/mock/mock-account.vala
index 1622f1894..711224cb4 100644
--- a/test/mock/mock-account.vala
+++ b/test/mock/mock-account.vala
@@ -198,26 +198,28 @@ public class Mock.Account : Geary.Account,
         );
     }
 
-    public override async Gee.Set<Geary.Email> get_multiple_email_by_id(
-        Gee.Collection<Geary.EmailIdentifier> ids,
-        Geary.Email.Field required_fields,
+    public override async Geary.Email get_email_by_id(
+        Geary.EmailIdentifier id,
+        Geary.Email.Field required_fields = ALL,
+        Geary.Folder.GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error {
-        return object_or_throw_call<Gee.Set<Geary.Email>>(
-            "get_multiple_email_by_id",
-            {ids, box_arg(required_fields), cancellable},
+        return object_or_throw_call<Geary.Email>(
+            "get_email_by_id",
+            {id, box_arg(required_fields), box_arg(flags), cancellable},
             new Geary.EngineError.NOT_FOUND("Mock call")
         );
     }
 
-    public override async Geary.Email
-        local_fetch_email_async(Geary.EmailIdentifier email_id,
-                                Geary.Email.Field required_fields,
-                                GLib.Cancellable? cancellable = null)
-        throws GLib.Error {
-        return object_or_throw_call<Geary.Email>(
-            "local_fetch_email_async",
-            {email_id, box_arg(required_fields), cancellable},
+    public override async Gee.Set<Geary.Email> get_multiple_email_by_id(
+        Gee.Collection<Geary.EmailIdentifier> ids,
+        Geary.Email.Field required_fields = ALL,
+        Geary.Folder.GetFlags flags = NONE,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error {
+        return object_or_throw_call<Gee.Set<Geary.Email>>(
+            "get_multiple_email_by_id",
+            {ids, box_arg(required_fields), box_arg(flags), cancellable},
             new Geary.EngineError.NOT_FOUND("Mock call")
         );
     }
diff --git a/test/mock/mock-folder.vala b/test/mock/mock-folder.vala
index 2aa371925..e113e01e9 100644
--- a/test/mock/mock-folder.vala
+++ b/test/mock/mock-folder.vala
@@ -72,25 +72,27 @@ public class Mock.Folder : GLib.Object,
 
     public async Geary.Email get_email_by_id(
         Geary.EmailIdentifier email_id,
-        Geary.Email.Field required_fields,
+        Geary.Email.Field required_fields = ALL,
+        Geary.Folder.GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error {
-        return yield object_call_async<Geary.Email>(
+        return object_or_throw_call<Geary.Email>(
             "get_email_by_id",
-            {email_id, box_arg(required_fields), cancellable},
-            new Geary.Email(new Mock.EmailIdentifer(0))
+            {email_id, box_arg(required_fields), box_arg(flags), cancellable},
+            new Geary.EngineError.UNSUPPORTED("Mock method")
         );
     }
 
-    public async Gee.Set<Geary.Email>
-        get_multiple_email_by_id(Gee.Collection<Geary.EmailIdentifier> ids,
-                                 Geary.Email.Field required_fields,
-                                 GLib.Cancellable? cancellable = null
-        ) throws GLib.Error {
-        return yield object_call_async<Gee.Set<Geary.Email>>(
+    public async Gee.Set<Geary.Email> get_multiple_email_by_id(
+        Gee.Collection<Geary.EmailIdentifier> ids,
+        Geary.Email.Field required_fields = ALL,
+        Geary.Folder.GetFlags flags = NONE,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error {
+        return object_or_throw_call<Gee.Set<Geary.Email>>(
             "get_multiple_email_by_id",
-            {ids, box_arg(required_fields), cancellable},
-            new Gee.HashSet<Geary.Email>()
+            {ids, box_arg(required_fields), box_arg(flags), cancellable},
+            new Geary.EngineError.UNSUPPORTED("Mock method")
         );
     }
 
diff --git a/test/mock/mock-remote-folder.vala b/test/mock/mock-remote-folder.vala
index 0f88098cb..74a700084 100644
--- a/test/mock/mock-remote-folder.vala
+++ b/test/mock/mock-remote-folder.vala
@@ -120,25 +120,27 @@ public class Mock.RemoteFolder : GLib.Object,
 
     public async Geary.Email get_email_by_id(
         Geary.EmailIdentifier email_id,
-        Geary.Email.Field required_fields,
+        Geary.Email.Field required_fields = ALL,
+        Geary.Folder.GetFlags flags = NONE,
         GLib.Cancellable? cancellable = null
     ) throws GLib.Error {
-        return yield object_call_async<Geary.Email>(
-            "get_email",
-            {email_id, box_arg(required_fields), cancellable},
-            new Geary.Email(new Mock.EmailIdentifer(0))
+        return object_or_throw_call<Geary.Email>(
+            "get_email_by_id",
+            {email_id, box_arg(required_fields), box_arg(flags), cancellable},
+            new Geary.EngineError.UNSUPPORTED("Mock method")
         );
     }
 
-    public async Gee.Set<Geary.Email>
-        get_multiple_email_by_id(Gee.Collection<Geary.EmailIdentifier> ids,
-                                 Geary.Email.Field required_fields,
-                                 GLib.Cancellable? cancellable = null
-        ) throws GLib.Error {
-        return yield object_call_async<Gee.Set<Geary.Email>>(
+    public async Gee.Set<Geary.Email> get_multiple_email_by_id(
+        Gee.Collection<Geary.EmailIdentifier> ids,
+        Geary.Email.Field required_fields = ALL,
+        Geary.Folder.GetFlags flags = NONE,
+        GLib.Cancellable? cancellable = null
+    ) throws GLib.Error {
+        return object_or_throw_call<Gee.Set<Geary.Email>>(
             "get_multiple_email_by_id",
-            {ids, box_arg(required_fields), cancellable},
-            new Gee.HashSet<Geary.Email>()
+            {ids, box_arg(required_fields), box_arg(flags), cancellable},
+            new Geary.EngineError.UNSUPPORTED("Mock method")
         );
     }
 


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