You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2021/04/09 12:00:43 UTC
[james-project] 01/03: JAMES-3417 JMAP Email/query sort support for
size, subject, from, to
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 7c8179d847956faccbf8489a36b59b75ca1330d0
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Apr 5 21:11:00 2021 +0700
JAMES-3417 JMAP Email/query sort support for size, subject, from, to
---
.../contract/EmailQueryMethodContract.scala | 278 ++++++++++++++++++++-
.../org/apache/james/jmap/mail/EmailQuery.scala | 10 +-
2 files changed, 278 insertions(+), 10 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 2bc6e24..24942d5 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
@@ -1860,6 +1860,280 @@ trait EmailQueryMethodContract {
}
@Test
+ def listMailsShouldBeSortedWhenUsingSize(server: GuiceJamesServer): Unit = {
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
+ val otherMailboxPath = MailboxPath.forUser(BOB, "other")
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
+
+ val message1: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setBody("short", StandardCharsets.UTF_8)
+ .build
+ val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message1))
+ .getMessageId
+
+ val message2: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setBody("definitly looooooooooooooooooooooooooooooooooooong", StandardCharsets.UTF_8)
+ .build
+ val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message2))
+ .getMessageId
+
+ val message3: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setBody("not that short", StandardCharsets.UTF_8)
+ .build
+ val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message3))
+ .getMessageId
+
+ val request =
+ s"""{
+ | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "sort": [{"property":"size"}]
+ | },
+ | "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"""["${messageId1.serialize}", "${messageId3.serialize}", "${messageId2.serialize}"]""")
+ }
+ }
+
+ @Test
+ def listMailsShouldBeSortedWhenUsingSubject(server: GuiceJamesServer): Unit = {
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
+ val otherMailboxPath = MailboxPath.forUser(BOB, "other")
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
+
+ val message1: Message = Message.Builder
+ .of
+ .setSubject("aba")
+ .setBody("any body", StandardCharsets.UTF_8)
+ .build
+ val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message1))
+ .getMessageId
+
+ val message2: Message = Message.Builder
+ .of
+ .setSubject("aaa")
+ .setBody("any body", StandardCharsets.UTF_8)
+ .build
+ val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message2))
+ .getMessageId
+
+ val message3: Message = Message.Builder
+ .of
+ .setSubject("ccc")
+ .setBody("any body", StandardCharsets.UTF_8)
+ .build
+ val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message3))
+ .getMessageId
+
+ val request =
+ s"""{
+ | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "sort": [{"property":"subject"}]
+ | },
+ | "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}", "${messageId1.serialize}", "${messageId3.serialize}"]""")
+ }
+ }
+
+ @Test
+ def listMailsShouldBeSortedWhenUsingTo(server: GuiceJamesServer): Unit = {
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
+ val otherMailboxPath = MailboxPath.forUser(BOB, "other")
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
+
+ val message1: Message = Message.Builder
+ .of
+ .setTo("aaa@domain.tld")
+ .setSubject("subject")
+ .setBody("any body", StandardCharsets.UTF_8)
+ .build
+ val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message1))
+ .getMessageId
+
+ val message2: Message = Message.Builder
+ .of
+ .setTo("ccc@domain.tld")
+ .setSubject("subject")
+ .setBody("any body", StandardCharsets.UTF_8)
+ .build
+ val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message2))
+ .getMessageId
+
+ val message3: Message = Message.Builder
+ .of
+ .setTo("aba@domain.tld")
+ .setSubject("subject")
+ .setBody("any body", StandardCharsets.UTF_8)
+ .build
+ val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message3))
+ .getMessageId
+
+ val request =
+ s"""{
+ | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "sort": [{"property":"to"}]
+ | },
+ | "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"""["${messageId1.serialize}", "${messageId3.serialize}", "${messageId2.serialize}"]""")
+ }
+ }
+
+ @Test
+ def listMailsShouldBeSortedWhenUsingFrom(server: GuiceJamesServer): Unit = {
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
+ val otherMailboxPath = MailboxPath.forUser(BOB, "other")
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
+
+ val message1: Message = Message.Builder
+ .of
+ .setFrom("aaa@domain.tld")
+ .setSubject("subject")
+ .setBody("any body", StandardCharsets.UTF_8)
+ .build
+ val messageId1: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message1))
+ .getMessageId
+
+ val message2: Message = Message.Builder
+ .of
+ .setFrom("ccc@domain.tld")
+ .setSubject("subject")
+ .setBody("any body", StandardCharsets.UTF_8)
+ .build
+ val messageId2 = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message2))
+ .getMessageId
+
+ val message3: Message = Message.Builder
+ .of
+ .setFrom("aba@domain.tld")
+ .setSubject("subject")
+ .setBody("any body", StandardCharsets.UTF_8)
+ .build
+ val messageId3 = server.getProbe(classOf[MailboxProbeImpl])
+ .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+ AppendCommand.builder().build(message3))
+ .getMessageId
+
+ val request =
+ s"""{
+ | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail"],
+ | "methodCalls": [[
+ | "Email/query",
+ | {
+ | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+ | "sort": [{"property":"from"}]
+ | },
+ | "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"""["${messageId1.serialize}", "${messageId3.serialize}", "${messageId2.serialize}"]""")
+ }
+ }
+
+ @Test
def listMailsShouldBeSortedByAscendingOrderOfInternalDateByDefaultWhenNoDateInHeader(server: GuiceJamesServer): Unit = {
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
val otherMailboxPath = MailboxPath.forUser(BOB, "other")
@@ -2291,10 +2565,6 @@ trait EmailQueryMethodContract {
@ValueSource(strings = Array(
"allInThreadHaveKeyword",
"someInThreadHaveKeyword",
- "size",
- "from",
- "to",
- "subject",
"hasKeyword"
))
def listMailsShouldReturnUnsupportedSortWhenPropertyFieldInComparatorIsValidButUnsupported(unsupported: String): Unit = {
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 b6358e1..1a7a676 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
@@ -158,7 +158,6 @@ sealed trait SortProperty {
case object ReceivedAtSortProperty extends SortProperty {
override def toSortClause: Either[UnsupportedSortException, SortClause] = scala.Right(SortClause.Arrival)
}
-
case object AllInThreadHaveKeywordSortProperty extends SortProperty {
override def toSortClause: Either[UnsupportedSortException, SortClause] = Left(UnsupportedSortException("allInThreadHaveKeyword"))
}
@@ -166,21 +165,20 @@ case object SomeInThreadHaveKeywordSortProperty extends SortProperty {
override def toSortClause: Either[UnsupportedSortException, SortClause] = Left(UnsupportedSortException("someInThreadHaveKeyword"))
}
case object SizeSortProperty extends SortProperty {
- override def toSortClause: Either[UnsupportedSortException, SortClause] = Left(UnsupportedSortException("size"))
+ override def toSortClause: Either[UnsupportedSortException, SortClause] = scala.Right(SortClause.Size)
}
case object FromSortProperty extends SortProperty {
- override def toSortClause: Either[UnsupportedSortException, SortClause] = Left(UnsupportedSortException("from"))
+ override def toSortClause: Either[UnsupportedSortException, SortClause] = scala.Right(SortClause.MailboxFrom)
}
case object ToSortProperty extends SortProperty {
- override def toSortClause: Either[UnsupportedSortException, SortClause] = Left(UnsupportedSortException("to"))
+ override def toSortClause: Either[UnsupportedSortException, SortClause] = scala.Right(SortClause.MailboxTo)
}
case object SubjectSortProperty extends SortProperty {
- override def toSortClause: Either[UnsupportedSortException, SortClause] = Left(UnsupportedSortException("subject"))
+ override def toSortClause: Either[UnsupportedSortException, SortClause] = scala.Right(SortClause.BaseSubject)
}
case object HasKeywordSortProperty extends SortProperty {
override def toSortClause: Either[UnsupportedSortException, SortClause] = Left(UnsupportedSortException("hasKeyword"))
}
-
case object SentAtSortProperty extends SortProperty {
override def toSortClause: Either[UnsupportedSortException, SortClause] = scala.Right(SortClause.SentDate)
}
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org