This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit f5d90c6bc735c4f5657beed27b8558d19fd7c136
Author: Benoit Tellier <[email protected]>
AuthorDate: Mon Nov 16 17:28:19 2020 +0700

    JAMES-3440 JMAP Draft should use EmailQueryView when enabled
---
 .../src/test/resources/listeners.xml               |  4 ++
 .../integration/GetMessageListMethodTest.java      | 22 +++++++
 .../src/test/resources/listeners.xml               |  4 ++
 .../src/test/resources/listeners.xml               |  4 ++
 .../jmap/draft/methods/GetMessageListMethod.java   | 77 ++++++++++++++++++++--
 .../org/apache/james/jmap/draft/model/Filter.java  |  8 +++
 .../james/jmap/draft/model/FilterCondition.java    | 39 +++++++++++
 .../src/test/resources/listeners.xml               |  4 ++
 .../src/test/resources/listeners.xml               |  4 ++
 9 files changed, 159 insertions(+), 7 deletions(-)

diff --git 
a/server/protocols/jmap-draft-integration-testing/cassandra-jmap-draft-integration-testing/src/test/resources/listeners.xml
 
b/server/protocols/jmap-draft-integration-testing/cassandra-jmap-draft-integration-testing/src/test/resources/listeners.xml
index ff2e517..1ff4055 100644
--- 
a/server/protocols/jmap-draft-integration-testing/cassandra-jmap-draft-integration-testing/src/test/resources/listeners.xml
+++ 
b/server/protocols/jmap-draft-integration-testing/cassandra-jmap-draft-integration-testing/src/test/resources/listeners.xml
@@ -46,4 +46,8 @@
       <name>second</name>
     </configuration>
   </listener>
+  <listener>
+    <class>org.apache.james.jmap.event.PopulateEmailQueryViewListener</class>
+    <async>true</async>
+  </listener>
 </listeners>
\ No newline at end of file
diff --git 
a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java
 
b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java
index f130a7d..bfff531 100644
--- 
a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java
+++ 
b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java
@@ -1771,6 +1771,28 @@ public abstract class GetMessageListMethodTest {
     }
 
     @Test
+    public void 
getMessageListShouldSortMessagesWhenSortedByDateDescAndInMailbox() throws 
Exception {
+        MailboxId mailboxId = 
mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, ALICE.asString(), 
"mailbox");
+
+        LocalDate date = LocalDate.now();
+        ComposedMessageId message1 = 
mailboxProbe.appendMessage(ALICE.asString(), ALICE_MAILBOX,
+                new ByteArrayInputStream("Subject: 
test\r\n\r\ntestmail".getBytes()), convertToDate(date.plusDays(1)), false, new 
Flags());
+        ComposedMessageId message2 = 
mailboxProbe.appendMessage(ALICE.asString(), ALICE_MAILBOX,
+                new ByteArrayInputStream("Subject: 
test2\r\n\r\ntestmail".getBytes()), convertToDate(date), false, new Flags());
+        await();
+
+        given()
+            .header("Authorization", aliceAccessToken.asString())
+            .body("[[\"getMessageList\", {\"filter\":{\"inMailboxes\":[\"" + 
mailboxId.serialize() + "\"]}, \"sort\":[\"date desc\"]}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("messageList"))
+            .body(ARGUMENTS + ".messageIds", 
contains(message1.getMessageId().serialize(), 
message2.getMessageId().serialize()));
+    }
+
+    @Test
     public void getMessageListShouldWorkWhenCollapseThreadIsFalse() {
         given()
             .header("Authorization", aliceAccessToken.asString())
diff --git 
a/server/protocols/jmap-draft-integration-testing/memory-jmap-draft-integration-testing/src/test/resources/listeners.xml
 
b/server/protocols/jmap-draft-integration-testing/memory-jmap-draft-integration-testing/src/test/resources/listeners.xml
index ae2e80a..a686755 100644
--- 
a/server/protocols/jmap-draft-integration-testing/memory-jmap-draft-integration-testing/src/test/resources/listeners.xml
+++ 
b/server/protocols/jmap-draft-integration-testing/memory-jmap-draft-integration-testing/src/test/resources/listeners.xml
@@ -46,4 +46,8 @@
   <listener>
     <class>org.apache.james.mailbox.spamassassin.SpamAssassinListener</class>
   </listener>
+  <listener>
+    <class>org.apache.james.jmap.event.PopulateEmailQueryViewListener</class>
+    <async>true</async>
+  </listener>
 </listeners>
\ No newline at end of file
diff --git 
a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/resources/listeners.xml
 
b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/resources/listeners.xml
index cac2777..43c8b96 100644
--- 
a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/resources/listeners.xml
+++ 
b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/resources/listeners.xml
@@ -49,4 +49,8 @@
   <listener>
     <class>org.apache.james.mailbox.spamassassin.SpamAssassinListener</class>
   </listener>
+  <listener>
+    <class>org.apache.james.jmap.event.PopulateEmailQueryViewListener</class>
+    <async>true</async>
+  </listener>
 </listeners>
\ No newline at end of file
diff --git 
a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
 
b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
index a14069c..5935b79 100644
--- 
a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
+++ 
b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
@@ -21,6 +21,7 @@ package org.apache.james.jmap.draft.methods;
 
 import static org.apache.james.util.ReactorUtils.context;
 
+import java.time.ZonedDateTime;
 import java.util.List;
 import java.util.Optional;
 import java.util.Set;
@@ -31,6 +32,8 @@ import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.apache.commons.lang3.NotImplementedException;
+import org.apache.james.jmap.JMAPConfiguration;
+import org.apache.james.jmap.api.projections.EmailQueryView;
 import org.apache.james.jmap.draft.model.Filter;
 import org.apache.james.jmap.draft.model.FilterCondition;
 import org.apache.james.jmap.draft.model.GetMessageListRequest;
@@ -42,17 +45,19 @@ import org.apache.james.jmap.draft.utils.FilterToCriteria;
 import org.apache.james.jmap.draft.utils.SortConverter;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxId.Factory;
 import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.util.MDCBuilder;
+import org.apache.james.util.streams.Limit;
 
 import com.github.fge.lambdas.Throwing;
 import com.github.steveash.guavate.Guavate;
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
@@ -71,17 +76,25 @@ public class GetMessageListMethod implements Method {
     private final long maximumLimit;
     private final GetMessagesMethod getMessagesMethod;
     private final Factory mailboxIdFactory;
+    private final EmailQueryView emailQueryView;
+    private final JMAPConfiguration configuration;
     private final MetricFactory metricFactory;
 
     @Inject
-    @VisibleForTesting public GetMessageListMethod(MailboxManager 
mailboxManager,
-            @Named(MAXIMUM_LIMIT) long maximumLimit, GetMessagesMethod 
getMessagesMethod, MailboxId.Factory mailboxIdFactory,
-            MetricFactory metricFactory) {
+    private GetMessageListMethod(MailboxManager mailboxManager,
+                                 @Named(MAXIMUM_LIMIT) long maximumLimit,
+                                 GetMessagesMethod getMessagesMethod,
+                                 Factory mailboxIdFactory,
+                                 EmailQueryView emailQueryView,
+                                 JMAPConfiguration configuration,
+                                 MetricFactory metricFactory) {
 
         this.mailboxManager = mailboxManager;
         this.maximumLimit = maximumLimit;
         this.getMessagesMethod = getMessagesMethod;
         this.mailboxIdFactory = mailboxIdFactory;
+        this.emailQueryView = emailQueryView;
+        this.configuration = configuration;
         this.metricFactory = metricFactory;
     }
 
@@ -149,14 +162,64 @@ public class GetMessageListMethod implements Method {
     }
 
     private Mono<GetMessageListResponse> 
getMessageListResponse(GetMessageListRequest messageListRequest, MailboxSession 
mailboxSession) {
+        long position = 
messageListRequest.getPosition().map(Number::asLong).orElse(DEFAULT_POSITION);
+        long limit = position + 
messageListRequest.getLimit().map(Number::asLong).orElse(maximumLimit);
+
+        if (isListingContentInMailboxQuery(messageListRequest)) {
+            Filter filter = messageListRequest.getFilter().get();
+            FilterCondition condition = (FilterCondition) filter;
+            String mailboxIdAsString = 
condition.getInMailboxes().get().iterator().next();
+            MailboxId mailboxId = 
mailboxIdFactory.fromString(mailboxIdAsString);
+            Limit aLimit = Limit.from(Math.toIntExact(limit));
+
+            return Mono.fromCallable(() -> 
mailboxManager.getMailbox(mailboxId, mailboxSession))
+                .subscribeOn(Schedulers.elastic())
+                .then(emailQueryView.listMailboxContent(mailboxId, aLimit)
+                    .skip(position)
+                    .take(limit)
+                    .reduce(GetMessageListResponse.builder(), 
GetMessageListResponse.Builder::messageId)
+                    .map(GetMessageListResponse.Builder::build))
+                .onErrorResume(MailboxNotFoundException.class, e -> 
Mono.just(GetMessageListResponse.builder().build()));
+        }
+        if (isListingContentInMailboxAfterQuery(messageListRequest)) {
+            Filter filter = messageListRequest.getFilter().get();
+            FilterCondition condition = (FilterCondition) filter;
+            String mailboxIdAsString = 
condition.getInMailboxes().get().iterator().next();
+            MailboxId mailboxId = 
mailboxIdFactory.fromString(mailboxIdAsString);
+            ZonedDateTime after = condition.getAfter().get();
+            Limit aLimit = Limit.from(Math.toIntExact(limit));
+
+            return Mono.fromCallable(() -> 
mailboxManager.getMailbox(mailboxId, mailboxSession))
+                .subscribeOn(Schedulers.elastic())
+                
.then(emailQueryView.listMailboxContentSinceReceivedAt(mailboxId, after, aLimit)
+                    .skip(position)
+                    .take(limit)
+                    .reduce(GetMessageListResponse.builder(), 
GetMessageListResponse.Builder::messageId)
+                    .map(GetMessageListResponse.Builder::build))
+                .onErrorResume(MailboxNotFoundException.class, e -> 
Mono.just(GetMessageListResponse.builder().build()));
+        }
+        return querySearchBackend(messageListRequest, position, limit, 
mailboxSession);
+    }
+
+    private boolean isListingContentInMailboxQuery(GetMessageListRequest 
messageListRequest) {
+        return configuration.isEmailQueryViewEnabled()
+            && 
messageListRequest.getFilter().map(Filter::inMailboxFilterOnly).orElse(false)
+            && messageListRequest.getSort().equals(ImmutableList.of("date 
desc"));
+    }
+
+    private boolean isListingContentInMailboxAfterQuery(GetMessageListRequest 
messageListRequest) {
+        return configuration.isEmailQueryViewEnabled()
+            && 
messageListRequest.getFilter().map(Filter::inMailboxAndAfterFiltersOnly).orElse(false)
+            && messageListRequest.getSort().equals(ImmutableList.of("date 
desc"));
+    }
+
+    private Mono<GetMessageListResponse> 
querySearchBackend(GetMessageListRequest messageListRequest, long position, 
long limit, MailboxSession mailboxSession) {
         Mono<MultimailboxesSearchQuery> searchQuery = Mono.fromCallable(() -> 
convertToSearchQuery(messageListRequest))
             .subscribeOn(Schedulers.parallel());
-        Long positionValue = 
messageListRequest.getPosition().map(Number::asLong).orElse(DEFAULT_POSITION);
-        long limit = positionValue + 
messageListRequest.getLimit().map(Number::asLong).orElse(maximumLimit);
 
         return searchQuery
             .flatMapMany(Throwing.function(query -> 
mailboxManager.search(query, mailboxSession, limit)))
-            .skip(positionValue)
+            .skip(position)
             .reduce(GetMessageListResponse.builder(), 
GetMessageListResponse.Builder::messageId)
             .map(GetMessageListResponse.Builder::build);
     }
diff --git 
a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Filter.java
 
b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Filter.java
index 0f26d7c..d562634 100644
--- 
a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Filter.java
+++ 
b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/Filter.java
@@ -39,6 +39,14 @@ public interface Filter {
 
     String prettyPrint(String indentation);
 
+    default boolean inMailboxFilterOnly() {
+        return false;
+    }
+
+    default boolean inMailboxAndAfterFiltersOnly() {
+        return false;
+    }
+
     default List<FilterCondition> breadthFirstVisit() {
         return this.breadthFirstVisit(0)
             .collect(Guavate.toImmutableList());
diff --git 
a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/FilterCondition.java
 
b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/FilterCondition.java
index e61aba5..14d723e 100644
--- 
a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/FilterCondition.java
+++ 
b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/FilterCondition.java
@@ -279,6 +279,45 @@ public class FilterCondition implements Filter {
         this.attachmentFileName = attachmentFileName;
     }
 
+    @Override
+    public boolean inMailboxFilterOnly() {
+        return inMailboxes.filter(list -> list.size() == 1).isPresent()
+            && after.isEmpty()
+            && noOtherFiltersSet();
+    }
+
+    @Override
+    public boolean inMailboxAndAfterFiltersOnly() {
+        return inMailboxes.filter(list -> list.size() == 1).isPresent()
+            && after.isPresent()
+            && noOtherFiltersSet();
+    }
+
+    private boolean noOtherFiltersSet() {
+        return notInMailboxes.isEmpty()
+            && before.isEmpty()
+            && minSize.isEmpty()
+            && maxSize.isEmpty()
+            && isFlagged.isEmpty()
+            && isUnread.isEmpty()
+            && isAnswered.isEmpty()
+            && isDraft.isEmpty()
+            && isForwarded.isEmpty()
+            && hasAttachment.isEmpty()
+            && text.isEmpty()
+            && from.isEmpty()
+            && to.isEmpty()
+            && cc.isEmpty()
+            && bcc.isEmpty()
+            && subject.isEmpty()
+            && body.isEmpty()
+            && attachments.isEmpty()
+            && header.isEmpty()
+            && hasKeyword.isEmpty()
+            && notKeyword.isEmpty()
+            && attachmentFileName.isEmpty();
+    }
+
     public Optional<List<String>> getInMailboxes() {
         return inMailboxes;
     }
diff --git 
a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/resources/listeners.xml
 
b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/resources/listeners.xml
index ff2e517..1ff4055 100644
--- 
a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/resources/listeners.xml
+++ 
b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/resources/listeners.xml
@@ -46,4 +46,8 @@
       <name>second</name>
     </configuration>
   </listener>
+  <listener>
+    <class>org.apache.james.jmap.event.PopulateEmailQueryViewListener</class>
+    <async>true</async>
+  </listener>
 </listeners>
\ No newline at end of file
diff --git 
a/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/resources/listeners.xml
 
b/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/resources/listeners.xml
index 59e3fec..a1a139d 100644
--- 
a/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/resources/listeners.xml
+++ 
b/server/protocols/webadmin-integration-test/memory-webadmin-integration-test/src/test/resources/listeners.xml
@@ -43,4 +43,8 @@
       <name>second</name>
     </configuration>
   </listener>
+  <listener>
+    <class>org.apache.james.jmap.event.PopulateEmailQueryViewListener</class>
+    <async>true</async>
+  </listener>
 </listeners>
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to