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 3fac80d4ca7c3bf74aab86fb5781d83770c42f78
Author: Rene Cordier <[email protected]>
AuthorDate: Tue Sep 22 13:58:58 2020 +0700

    JAMES-3384 allInThreadHaveKeyword and someInThreadHaveKeyword parameters 
are unsupported for sorting on Email/query
---
 .../contract/EmailQueryMethodContract.scala        | 46 +++++++++++++++++++++-
 .../james/jmap/json/EmailQuerySerializer.scala     |  4 +-
 .../org/apache/james/jmap/mail/EmailQuery.scala    | 19 +++++++--
 .../james/jmap/method/EmailQueryMethod.scala       | 37 ++++++++++-------
 .../org/apache/james/jmap/model/Invocation.scala   |  4 ++
 5 files changed, 91 insertions(+), 19 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 099cbda..eba54dd 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
@@ -51,7 +51,7 @@ import org.awaitility.Awaitility
 import org.awaitility.Duration.ONE_HUNDRED_MILLISECONDS
 import org.junit.jupiter.api.{BeforeEach, Test}
 import org.junit.jupiter.params.ParameterizedTest
-import org.junit.jupiter.params.provider.{Arguments, MethodSource}
+import org.junit.jupiter.params.provider.{Arguments, MethodSource, ValueSource}
 import org.threeten.extra.Seconds
 
 object EmailQueryMethodContract {
@@ -867,6 +867,50 @@ trait EmailQueryMethodContract {
     }
   }
 
+  @ParameterizedTest
+  @ValueSource(strings = Array(
+    "allInThreadHaveKeyword",
+    "someInThreadHaveKeyword"
+  ))
+  def 
listMailsShouldReturnUnsupportedSortWhenPropertyFieldInComparatorIsValidButUnsupported(unsupported:
 String): Unit = {
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "comparator": [{
+         |        "property":"$unsupported"
+         |      }]
+         |    },
+         |    "c1"]]
+         |}""".stripMargin
+
+    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]")
+      .isEqualTo(s"""
+       {
+          "type": "unsupportedSort",
+          "description": "The sort $unsupported is syntactically valid, but it 
includes a property the server does not support sorting on or a collation 
method it does not recognise."
+       }
+       """)
+  }
+
   @Test
   def 
shouldReturnIllegalArgumentErrorForAnUnknownSpecificUserMailboxes(server: 
GuiceJamesServer): Unit = {
     val message: Message = Message.Builder
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 c8f2fb4..3fac550 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.{Collation, Comparator, EmailQueryRequest, 
EmailQueryResponse, FilterCondition, HasAttachment, IsAscending, 
ReceivedAtSortProperty, SortProperty}
+import org.apache.james.jmap.mail.{AllInThreadHaveKeywordSortProperty, 
Collation, Comparator, EmailQueryRequest, EmailQueryResponse, FilterCondition, 
HasAttachment, IsAscending, ReceivedAtSortProperty, 
SomeInThreadHaveKeywordSortProperty, SortProperty}
 import org.apache.james.jmap.model.{AccountId, CanCalculateChanges, Keyword, 
LimitUnparsed, PositionUnparsed, QueryState}
 import org.apache.james.mailbox.model.{MailboxId, MessageId}
 import play.api.libs.json._
@@ -59,6 +59,8 @@ class EmailQuerySerializer @Inject()(mailboxIdFactory: 
MailboxId.Factory) {
 
   private implicit val sortPropertyReads: Reads[SortProperty] = {
     case JsString("receivedAt") => JsSuccess(ReceivedAtSortProperty)
+    case JsString("allInThreadHaveKeyword") => 
JsSuccess(AllInThreadHaveKeywordSortProperty)
+    case JsString("someInThreadHaveKeyword") => 
JsSuccess(SomeInThreadHaveKeywordSortProperty)
     case JsString(others) => JsError(s"'$others' is not a supported sort 
property")
     case _ => JsError(s"Expecting a JsString to represent a sort property")
   }
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 1318819..e2ab4ea 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
@@ -27,6 +27,8 @@ import 
org.apache.james.mailbox.model.SearchQuery.Sort.Order.{NATURAL, REVERSE}
 import org.apache.james.mailbox.model.SearchQuery.Sort.SortClause
 import org.apache.james.mailbox.model.{MailboxId, MessageId, SearchQuery}
 
+case class UnsupportedSortException(unsupportedSort: String) extends 
UnsupportedOperationException
+
 case class FilterCondition(inMailbox: Option[MailboxId],
                            inMailboxOtherThan: Option[Seq[MailboxId]],
                            before: Option[UTCDate],
@@ -40,10 +42,18 @@ case class FilterCondition(inMailbox: Option[MailboxId],
 case class EmailQueryRequest(accountId: AccountId, position: 
Option[PositionUnparsed], limit: Option[LimitUnparsed], filter: 
Option[FilterCondition], comparator: Option[Set[Comparator]])
 
 sealed trait SortProperty {
-  def toSortClause: SortClause
+  def toSortClause: Either[UnsupportedSortException, SortClause]
 }
 case object ReceivedAtSortProperty extends SortProperty {
-  override def toSortClause: SortClause = SortClause.Arrival
+  override def toSortClause: Either[UnsupportedSortException, SortClause] = 
scala.Right(SortClause.Arrival)
+}
+
+case object AllInThreadHaveKeywordSortProperty extends SortProperty {
+  override def toSortClause: Either[UnsupportedSortException, SortClause] = 
Left(UnsupportedSortException("allInThreadHaveKeyword"))
+}
+
+case object SomeInThreadHaveKeywordSortProperty extends SortProperty {
+  override def toSortClause: Either[UnsupportedSortException, SortClause] = 
Left(UnsupportedSortException("someInThreadHaveKeyword"))
 }
 
 object IsAscending {
@@ -67,7 +77,10 @@ case class Collation(value: String) extends AnyVal
 case class Comparator(property: SortProperty,
                       isAscending: Option[IsAscending],
                       collation: Option[Collation]) {
-  def toSort: SearchQuery.Sort = new SearchQuery.Sort(property.toSortClause, 
isAscending.getOrElse(ASCENDING).toSortOrder)
+  def toSort: Either[UnsupportedSortException, SearchQuery.Sort] =
+    for {
+      sortClause <- property.toSortClause
+    } yield new SearchQuery.Sort(sortClause, 
isAscending.getOrElse(ASCENDING).toSortOrder)
 }
 
 case class EmailQueryResponse(accountId: AccountId,
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 3f76023..196b3f8 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
@@ -18,21 +18,22 @@
  ****************************************************************/
 package org.apache.james.jmap.method
 
+import cats.implicits._
 import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.json.{EmailQuerySerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.{Comparator, EmailQueryRequest, 
EmailQueryResponse}
+import org.apache.james.jmap.mail.{Comparator, EmailQueryRequest, 
EmailQueryResponse, UnsupportedSortException}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, 
MAIL_CAPABILITY}
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
-import org.apache.james.jmap.model.{CanCalculateChanges, Capabilities, 
ErrorCode, Invocation, Limit, Position, QueryState}
 import org.apache.james.jmap.model.Limit.Limit
 import org.apache.james.jmap.model.Position.Position
+import org.apache.james.jmap.model.{CanCalculateChanges, Capabilities, 
ErrorCode, Invocation, Limit, Position, QueryState}
 import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.jmap.utils.search.MailboxFilter
 import org.apache.james.jmap.utils.search.MailboxFilter.QueryFilter
 import org.apache.james.mailbox.exception.MailboxNotFoundException
-import org.apache.james.mailbox.model.{MultimailboxesSearchQuery, SearchQuery}
+import org.apache.james.mailbox.model.MultimailboxesSearchQuery
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
 import org.apache.james.metrics.api.MetricFactory
 import org.reactivestreams.Publisher
@@ -52,6 +53,10 @@ class EmailQueryMethod @Inject() (serializer: 
EmailQuerySerializer,
       asEmailQueryRequest(invocation.arguments)
         .flatMap(processRequest(mailboxSession, invocation, _))
         .onErrorResume {
+          case e: UnsupportedSortException => SMono.just(Invocation.error(
+            ErrorCode.UnsupportedSort,
+            s"The sort ${e.unsupportedSort} is syntactically valid, but it 
includes a property the server does not support sorting on or a collation 
method it does not recognise.",
+            invocation.methodCallId))
           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)
@@ -59,12 +64,14 @@ class EmailQueryMethod @Inject() (serializer: 
EmailQuerySerializer,
         .map(invocationResult => (invocationResult, processingContext)))
 
   private def processRequest(mailboxSession: MailboxSession, invocation: 
Invocation, request: EmailQueryRequest): SMono[Invocation] = {
-    val searchQuery: MultimailboxesSearchQuery = 
searchQueryFromRequest(request)
-    for {
-      positionToUse <- Position.validateRequestPosition(request.position)
-      limitToUse <- Limit.validateRequestLimit(request.limit)
-      response <- executeQuery(mailboxSession, request, searchQuery, 
positionToUse, limitToUse)
-    } yield Invocation(methodName = methodName, arguments = 
Arguments(serializer.serialize(response)), methodCallId = 
invocation.methodCallId)
+    searchQueryFromRequest(request) match {
+      case Left(error) => SMono.raiseError(error)
+      case Right(searchQuery) => for {
+        positionToUse <- Position.validateRequestPosition(request.position)
+        limitToUse <- Limit.validateRequestLimit(request.limit)
+        response <- executeQuery(mailboxSession, request, searchQuery, 
positionToUse, limitToUse)
+      } yield Invocation(methodName = methodName, arguments = 
Arguments(serializer.serialize(response)), methodCallId = 
invocation.methodCallId)
+    }
   }
 
   private def executeQuery(mailboxSession: MailboxSession, request: 
EmailQueryRequest, searchQuery: MultimailboxesSearchQuery, position: Position, 
limitToUse: Limit): SMono[EmailQueryResponse] = {
@@ -79,13 +86,15 @@ class EmailQueryMethod @Inject() (serializer: 
EmailQuerySerializer,
         limit = Some(limitToUse).filterNot(used => 
request.limit.map(_.value).contains(used.value))))
   }
 
-  private def searchQueryFromRequest(request: EmailQueryRequest): 
MultimailboxesSearchQuery = {
+  private def searchQueryFromRequest(request: EmailQueryRequest): 
Either[UnsupportedSortException, MultimailboxesSearchQuery] = {
     val comparators: List[Comparator] = 
request.comparator.getOrElse(Set(Comparator.default)).toList
-    val sortedSearchQuery: SearchQuery = QueryFilter.buildQuery(request)
-      .sorts(comparators.map(_.toSort).asJava)
-      .build()
 
-    MailboxFilter.buildQuery(request, sortedSearchQuery)
+    comparators.map(_.toSort)
+      .sequence
+      .map(sorts => QueryFilter.buildQuery(request)
+        .sorts(sorts.asJava)
+        .build())
+      .map(MailboxFilter.buildQuery(request, _))
   }
 
   private def asEmailQueryRequest(arguments: Arguments): 
SMono[EmailQueryRequest] =
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala
index 81b517d..6bfdd43 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala
@@ -69,4 +69,8 @@ object ErrorCode {
   case object InvalidResultReference extends ErrorCode {
     override def code: String = "invalidResultReference"
   }
+
+  case object UnsupportedSort extends ErrorCode {
+    override def code: String = "unsupportedSort"
+  }
 }


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

Reply via email to