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

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

commit 06e33c574d0928efb18b9d2e8f0a4f15c03b182f
Author: RĂ©mi Kowalski <[email protected]>
AuthorDate: Fri Sep 11 10:45:45 2020 +0200

    JAMES-3375 filter message in a mailbox
---
 .../contract/EmailQueryMethodContract.scala        | 119 ++++++++++++++++++++-
 .../rfc8621/memory/MemoryEmailQueryMethodTest.java |   9 --
 .../james/jmap/json/EmailQuerySerializer.scala     |   3 +-
 .../org/apache/james/jmap/mail/EmailQuery.scala    |   4 +-
 .../james/jmap/method/EmailQueryMethod.scala       |  19 +++-
 5 files changed, 140 insertions(+), 14 deletions(-)

diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index 3a9fd36..312388f 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -238,7 +238,7 @@ trait EmailQueryMethodContract {
 
     assertThatJson(response)
       .inPath("$.methodResponses[0][1].ids")
-      .isEqualTo(Json.stringify(Json.toJson(List(messageId3, messageId2, 
messageId1).map(_.serialize()))))
+      .isEqualTo(s"""["${messageId3.serialize()}", 
"${messageId2.serialize()}", "${messageId1.serialize()}"]""")
     }
   }
 
@@ -301,6 +301,123 @@ trait EmailQueryMethodContract {
     }
   }
 
+  @Test
+  def shouldListMailsInASpecificUserMailboxes(server: GuiceJamesServer): Unit 
= {
+    val message: Message = Message.Builder
+      .of
+      .setSubject("test")
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+    
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
+    val otherMailboxPath = MailboxPath.forUser(BOB, "other")
+    val otherMailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, MailboxPath.inbox(BOB), 
AppendCommand.from(message))
+      .getMessageId
+    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, otherMailboxPath, 
AppendCommand.from(message))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "inMailbox": "${otherMailboxId.serialize()}"
+         |        }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response)
+        .inPath("$.methodResponses[0][1].ids")
+        .isEqualTo(s"""["${messageId2.serialize()}"]""")
+    }
+  }
+
+  @Test
+  def 
shouldReturnIllegalArgumentErrorForAnUnknownSpecificUserMailboxes(server: 
GuiceJamesServer): Unit = {
+    val message: Message = Message.Builder
+      .of
+      .setSubject("test")
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+    
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
+    val otherMailboxPath = MailboxPath.forUser(BOB, "other")
+    val otherMailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, MailboxPath.inbox(BOB), 
AppendCommand.from(message))
+      .getMessageId
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, otherMailboxPath, 
AppendCommand.from(message))
+      .getMessageId
+
+    
server.getProbe(classOf[MailboxProbeImpl]).deleteMailbox(otherMailboxPath.getNamespace,
 BOB.asString(), otherMailboxPath.getName)
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {
+         |        "inMailbox": "${otherMailboxId.serialize()}"
+         |      }
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    awaitAtMostTenSeconds.untilAsserted { () =>
+      val response = `given`
+        .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+        .body(request)
+      .when
+        .post
+      .`then`
+        .statusCode(SC_OK)
+        .contentType(JSON)
+        .extract
+        .body
+        .asString
+
+      assertThatJson(response).isEqualTo(
+        s"""{
+           |    "sessionState": "75128aab4b1b",
+           |    "methodResponses": [
+           |        [
+           |            "error",
+           |            {
+           |                "type": "invalidArguments",
+           |                "description": "${otherMailboxId.serialize()} can 
not be found"
+           |            },
+           |            "c1"
+           |        ]
+           |    ]
+           |}""".stripMargin)
+    }
+  }
+
   private def generateQueryState(messages: MessageId*): String = {
     Hashing.murmur3_32()
       .hashUnencodedChars(messages.toList.map(_.serialize()).mkString(" "))
diff --git 
a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java
 
b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java
index 6d17a60..78b50d1 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryEmailQueryMethodTest.java
@@ -26,8 +26,6 @@ import org.apache.james.JamesServerBuilder;
 import org.apache.james.JamesServerExtension;
 import org.apache.james.jmap.rfc8621.contract.EmailQueryMethodContract;
 import org.apache.james.modules.TestJMAPServerModule;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 public class MemoryEmailQueryMethodTest implements EmailQueryMethodContract {
@@ -38,11 +36,4 @@ public class MemoryEmailQueryMethodTest implements 
EmailQueryMethodContract {
             .overrideWith(new TestJMAPServerModule()))
         .build();
 
-    @Disabled("Can't be done in memory because the SimpleMessageSearchIndex 
doesn't handle sorting")
-    @Test
-    public void shouldListMailsInAllUserMailboxes(GuiceJamesServer server)  {}
-
-    @Disabled("Can't be done in memory because the SimpleMessageSearchIndex 
doesn't handle sorting")
-    @Test
-    public void 
listMailsShouldBeSortedByDescendingOrderOfArrivalByDefault(GuiceJamesServer 
server)  {}
 }
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala
index 01e8afe..6c7cfb8 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailQuerySerializer.scala
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.json
 
 import javax.inject.Inject
-import org.apache.james.jmap.mail.{EmailQueryRequest, EmailQueryResponse, 
Limit, Position, QueryState}
+import org.apache.james.jmap.mail.{EmailQueryRequest, EmailQueryResponse, 
FilterCondition, Limit, Position, QueryState}
 import org.apache.james.jmap.model._
 import org.apache.james.mailbox.model.{MailboxId, MessageId}
 import play.api.libs.json._
@@ -37,6 +37,7 @@ class EmailQuerySerializer @Inject()(mailboxIdFactory: 
MailboxId.Factory) {
     case _ => JsError()
   }
 
+  private implicit val filterConditionReads: Reads[FilterCondition] = 
Json.reads[FilterCondition]
   private implicit val emailQueryRequestReads: Reads[EmailQueryRequest] = 
Json.reads[EmailQueryRequest]
   private implicit val queryStateWrites: Writes[QueryState] = 
Json.valueWrites[QueryState]
   private implicit val positionFormat: Format[Position] = 
Json.valueFormat[Position]
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala
index ad15604..4ac82aa 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailQuery.scala
@@ -23,7 +23,9 @@ import com.google.common.hash.Hashing
 import org.apache.james.jmap.model.AccountId
 import org.apache.james.mailbox.model.{MailboxId, MessageId}
 
-case class EmailQueryRequest(accountId: AccountId, inMailbox: 
Option[MailboxId])
+case class FilterCondition(inMailbox: Option[MailboxId])
+
+case class EmailQueryRequest(accountId: AccountId, filter: 
Option[FilterCondition])
 
 case class Position(value: Int) extends AnyVal
 object Position{
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
index 5ba6543..cdb370e 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala
@@ -27,6 +27,7 @@ import 
org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CA
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model._
 import org.apache.james.jmap.routes.ProcessingContext
+import org.apache.james.mailbox.exception.{MailboxNotFoundException}
 import org.apache.james.mailbox.model.SearchQuery.Sort.SortClause
 import org.apache.james.mailbox.model.{MultimailboxesSearchQuery, SearchQuery}
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
@@ -47,12 +48,13 @@ class EmailQueryMethod @Inject() (serializer: 
EmailQuerySerializer,
         .flatMap(processRequest(mailboxSession, invocation, _))
         .onErrorResume {
           case e: IllegalArgumentException => 
SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, 
invocation.methodCallId))
+          case e: MailboxNotFoundException => 
SMono.just(Invocation.error(ErrorCode.InvalidArguments, e.getMessage, 
invocation.methodCallId))
           case e: Throwable => SMono.raiseError(e)
         }
         .map(invocationResult => (invocationResult, processingContext)))
 
   private def processRequest(mailboxSession: MailboxSession, invocation: 
Invocation, request: EmailQueryRequest): SMono[Invocation] = {
-    val searchQuery = MultimailboxesSearchQuery.from(new 
SearchQuery.Builder().sorts(new SearchQuery.Sort(SortClause.Arrival, 
SearchQuery.Sort.Order.REVERSE)).build()).build()
+    val searchQuery = searchQueryFromRequest(request)
     SFlux.fromPublisher(mailboxManager.search(searchQuery, mailboxSession, 
Limit.default.value))
       .collectSeq()
       .map(ids => EmailQueryResponse(accountId = request.accountId,
@@ -64,10 +66,23 @@ class EmailQueryMethod @Inject() (serializer: 
EmailQuerySerializer,
       .map(response => Invocation(methodName = methodName, arguments = 
Arguments(serializer.serialize(response)), methodCallId = 
invocation.methodCallId))
   }
 
+  private def searchQueryFromRequest(request: EmailQueryRequest): 
MultimailboxesSearchQuery = {
+    val query = new SearchQuery.Builder()
+    val defaultSort = new SearchQuery.Sort(SortClause.Arrival, 
SearchQuery.Sort.Order.REVERSE)
+    val querySorted = query.sorts(defaultSort)
+
+    val multiMailboxQueryBuilder = 
MultimailboxesSearchQuery.from(querySorted.build())
+
+    val multiMailboxQueryBuilderWithInMailboxFilter = request.inMailbox match {
+      case Some(mailboxId) => multiMailboxQueryBuilder.inMailboxes(mailboxId)
+      case None => multiMailboxQueryBuilder
+    }
+    multiMailboxQueryBuilderWithInMailboxFilter.build()
+  }
+
   private def asEmailQueryRequest(arguments: Arguments): 
SMono[EmailQueryRequest] =
     serializer.deserializeEmailQueryRequest(arguments.value) match {
       case JsSuccess(emailQueryRequest, _) => SMono.just(emailQueryRequest)
       case errors: JsError => SMono.raiseError(new 
IllegalArgumentException(ResponseSerializer.serialize(errors).toString))
     }
-
 }
\ No newline at end of file


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

Reply via email to