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 6e8161ff71943d486f3717c2547ee4deb53d90fe
Author: Benoit Tellier <btell...@linagora.com>
AuthorDate: Fri Aug 14 14:06:05 2020 +0700

    JAMES-3356 Allow the use of creationIds in Mailbox/Get
---
 .../contract/MailboxGetMethodContract.scala        |  82 ++++++++++++++++
 .../contract/MailboxSetMethodContract.scala        | 105 +++++++++++++++++++++
 .../org/apache/james/jmap/json/Serializer.scala    |   4 +-
 .../org/apache/james/jmap/mail/MailboxGet.scala    |   6 +-
 .../org/apache/james/jmap/mail/MailboxSet.scala    |   9 +-
 .../james/jmap/method/MailboxGetMethod.scala       |  18 +++-
 .../jmap/json/MailboxGetSerializationTest.scala    |  17 +++-
 7 files changed, 226 insertions(+), 15 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/MailboxGetMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
index b2c2d31..7776b26 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
@@ -560,6 +560,88 @@ trait MailboxGetMethodContract {
   }
 
   @Test
+  def getMailboxesShouldReturnNotFoundWhenInvalid(): Unit = {
+    val response: String = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(s"""{
+               |  "using": [
+               |    "urn:ietf:params:jmap:core",
+               |    "urn:ietf:params:jmap:mail"],
+               |  "methodCalls": [[
+               |    "Mailbox/get",
+               |    {
+               |      "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |      "properties": ["id", "name", "rights"],
+               |      "ids": ["invalid"]
+               |    },
+               |    "c1"]]
+               |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |  "sessionState": "75128aab4b1b",
+         |  "methodResponses": [[
+         |    "Mailbox/get",
+         |    {
+         |      "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "state": "000001",
+         |      "list": [],
+         |      "notFound": ["invalid"]
+         |    },
+         |    "c1"]]
+         |}""".stripMargin)
+  }
+
+  @Test
+  def getMailboxesShouldReturnNotFoundWhenUnknownClientId(): Unit = {
+    val response: String = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(s"""{
+               |  "using": [
+               |    "urn:ietf:params:jmap:core",
+               |    "urn:ietf:params:jmap:mail"],
+               |  "methodCalls": [[
+               |    "Mailbox/get",
+               |    {
+               |      "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+               |      "properties": ["id", "name", "rights"],
+               |      "ids": ["#C42"]
+               |    },
+               |    "c1"]]
+               |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |  "sessionState": "75128aab4b1b",
+         |  "methodResponses": [[
+         |    "Mailbox/get",
+         |    {
+         |      "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "state": "000001",
+         |      "list": [],
+         |      "notFound": ["#C42"]
+         |    },
+         |    "c1"]]
+         |}""".stripMargin)
+  }
+
+  @Test
   def getMailboxesShouldReturnInvalidArgumentsErrorWhenInvalidProperty(server: 
GuiceJamesServer): Unit = {
     val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
       .createMailbox(MailboxPath.forUser(BOB, "custom"))
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/MailboxSetMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
index 28bd5fd..21e234e 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala
@@ -268,6 +268,111 @@ trait MailboxSetMethodContract {
   }
 
   @Test
+  def mailboxGetShouldAllowTheUseOfCreationIds(server: GuiceJamesServer): Unit 
= {
+    val request=
+      """
+        |{
+        |   "using": [ "urn:ietf:params:jmap:core", 
"urn:ietf:params:jmap:mail" ],
+        |   "methodCalls": [
+        |       [
+        |           "Mailbox/set",
+        |           {
+        |                "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+        |                "create": {
+        |                    "C42": {
+        |                      "name": "myMailbox"
+        |                    }
+        |                }
+        |           },
+        |    "c1"
+        |       ],
+        |       ["Mailbox/get",
+        |         {
+        |           "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+        |           "ids": ["#C42"]
+        |          },
+        |       "c2"]
+        |   ]
+        |}
+        |""".stripMargin
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .log().ifValidationFails()
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+      .getMailboxId("#private", BOB.asString(), "myMailbox")
+      .serialize()
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |     "sessionState": "75128aab4b1b",
+         |     "methodResponses": [
+         |             ["Mailbox/set", {
+         |                     "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                     "newState": "000001",
+         |                     "created": {
+         |                             "C42": {
+         |                                     "id": "$mailboxId",
+         |                                     "totalEmails": 0,
+         |                                     "unreadEmails": 0,
+         |                                     "totalThreads": 0,
+         |                                     "unreadThreads": 0,
+         |                                     "myRights": {
+         |                                             "mayReadItems": true,
+         |                                             "mayAddItems": true,
+         |                                             "mayRemoveItems": true,
+         |                                             "maySetSeen": true,
+         |                                             "maySetKeywords": true,
+         |                                             "mayCreateChild": true,
+         |                                             "mayRename": true,
+         |                                             "mayDelete": true,
+         |                                             "maySubmit": true
+         |                                     },
+         |                                     "isSubscribed": true
+         |                             }
+         |                     }
+         |             }, "c1"],
+         |             ["Mailbox/get", {
+         |                     "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                     "state": "000001",
+         |                     "list": [{
+         |                             "id": "$mailboxId",
+         |                             "name": "myMailbox",
+         |                             "sortOrder": 1000,
+         |                             "totalEmails": 0,
+         |                             "unreadEmails": 0,
+         |                             "totalThreads": 0,
+         |                             "unreadThreads": 0,
+         |                             "myRights": {
+         |                                     "mayReadItems": true,
+         |                                     "mayAddItems": true,
+         |                                     "mayRemoveItems": true,
+         |                                     "maySetSeen": true,
+         |                                     "maySetKeywords": true,
+         |                                     "mayCreateChild": true,
+         |                                     "mayRename": true,
+         |                                     "mayDelete": true,
+         |                                     "maySubmit": true
+         |                             },
+         |                             "isSubscribed": false
+         |                     }],
+         |      "notFound":[]
+         |             }, "c2"]
+         |     ]
+         |}""".stripMargin)
+  }
+
+  @Test
   def mailboxSetShouldReturnCreatedWhenOnlyName(server: GuiceJamesServer): 
Unit = {
     val request=
       """
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
index 5ba43fb..e635254 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
@@ -27,7 +27,7 @@ import eu.timepit.refined.auto._
 import javax.inject.Inject
 
 import org.apache.james.core.{Domain, Username}
-import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
+import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, 
UnparsedMailboxId}
 import org.apache.james.jmap.mail.{DelegatedNamespace, Ids, IsSubscribed, 
Mailbox, MailboxCreationRequest, MailboxCreationResponse, MailboxGetRequest, 
MailboxGetResponse, MailboxNamespace, MailboxPatchObject, MailboxRights, 
MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, 
MayAddItems, MayCreateChild, MayDelete, MayReadItems, MayRemoveItems, 
MayRename, MaySetKeywords, MaySetSeen, MaySubmit, NotFound, PersonalNamespace, 
Properties, Quota, QuotaId, QuotaRoot, Q [...]
 import org.apache.james.jmap.model
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
@@ -252,7 +252,7 @@ class Serializer @Inject() (mailboxIdFactory: 
MailboxId.Factory) {
 
   private implicit val mailboxSetRequestReads: Reads[MailboxSetRequest] = 
Json.reads[MailboxSetRequest]
 
-  private implicit def notFoundWrites(implicit mailboxIdWrites: 
Writes[MailboxId]): Writes[NotFound] =
+  private implicit def notFoundWrites(implicit mailboxIdWrites: 
Writes[UnparsedMailboxId]): Writes[NotFound] =
     notFound => JsArray(notFound.value.toList.map(mailboxIdWrites.writes))
 
   private implicit def mailboxGetResponseWrites(implicit mailboxWrites: 
Writes[Mailbox]): Writes[MailboxGetResponse] = Json.writes[MailboxGetResponse]
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
index 613d418..1169652 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
@@ -20,11 +20,11 @@
 package org.apache.james.jmap.mail
 
 import eu.timepit.refined.types.string.NonEmptyString
+import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
 import org.apache.james.jmap.model.AccountId
 import org.apache.james.jmap.model.State.State
-import org.apache.james.mailbox.model.MailboxId
 
-case class Ids(value: List[MailboxId])
+case class Ids(value: List[UnparsedMailboxId])
 
 case class Properties(value: List[NonEmptyString]) {
   def asSetOfString: Set[String] = value.map(_.toString()).toSet
@@ -35,7 +35,7 @@ case class MailboxGetRequest(accountId: AccountId,
                              ids: Option[Ids],
                              properties: Option[Properties])
 
-case class NotFound(value: Set[MailboxId]) {
+case class NotFound(value: Set[UnparsedMailboxId]) {
   def merge(other: NotFound): NotFound = NotFound(this.value ++ other.value)
 }
 
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
index fdf2d18..794a6a6 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
@@ -19,6 +19,7 @@
 
 package org.apache.james.jmap.mail
 
+import eu.timepit.refined
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.auto._
 import eu.timepit.refined.collection.NonEmpty
@@ -38,8 +39,14 @@ case class MailboxSetRequest(accountId: AccountId,
                              onDestroyRemoveEmails: 
Option[RemoveEmailsOnDestroy])
 
 object MailboxSetRequest {
+  type UnparsedMailboxIdConstraint = NonEmpty
   type MailboxCreationId = String Refined NonEmpty
-  type UnparsedMailboxId = String Refined NonEmpty
+  type UnparsedMailboxId = String Refined UnparsedMailboxIdConstraint
+
+  def asUnparsed(mailboxId: MailboxId): UnparsedMailboxId = 
refined.refineV[UnparsedMailboxIdConstraint](mailboxId.serialize()) match {
+    case Left(e) => throw new IllegalArgumentException(e)
+    case scala.Right(value) => value
+  }
 }
 
 case class RemoveEmailsOnDestroy(value: Boolean) extends AnyVal
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index 7e66b5e..992cb74 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -22,6 +22,7 @@ package org.apache.james.jmap.method
 import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.json.Serializer
+import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
 import org.apache.james.jmap.mail._
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
@@ -42,20 +43,25 @@ import reactor.core.scheduler.Schedulers
 class MailboxGetMethod @Inject() (serializer: Serializer,
                                   mailboxManager: MailboxManager,
                                   quotaFactory : 
QuotaLoaderWithPreloadedDefaultFactory,
+                                  mailboxIdFactory: MailboxId.Factory,
                                   mailboxFactory: MailboxFactory,
                                   metricFactory: MetricFactory) extends Method 
{
   override val methodName: MethodName = MethodName("Mailbox/get")
 
   object MailboxGetResults {
     def found(mailbox: Mailbox): MailboxGetResults = 
MailboxGetResults(Set(mailbox), NotFound(Set.empty))
-    def notFound(mailboxId: MailboxId): MailboxGetResults = 
MailboxGetResults(Set.empty, NotFound(Set(mailboxId)))
+    def notFound(mailboxId: UnparsedMailboxId): MailboxGetResults = 
MailboxGetResults(Set.empty, NotFound(Set(mailboxId)))
+    def notFound(mailboxId: MailboxId): MailboxGetResults = 
MailboxGetResults(Set.empty, 
NotFound(Set(MailboxSetRequest.asUnparsed(mailboxId))))
   }
 
   case class MailboxGetResults(mailboxes: Set[Mailbox], notFound: NotFound) {
     def merge(other: MailboxGetResults): MailboxGetResults = 
MailboxGetResults(this.mailboxes ++ other.mailboxes, 
this.notFound.merge(other.notFound))
   }
 
-  override def process(capabilities: Set[CapabilityIdentifier], invocation: 
Invocation, mailboxSession: MailboxSession, processingContext: 
ProcessingContext): Publisher[Invocation] = {
+  override def process(capabilities: Set[CapabilityIdentifier],
+                       invocation: Invocation,
+                       mailboxSession: MailboxSession,
+                       processingContext: ProcessingContext): 
Publisher[Invocation] = {
     metricFactory.decoratePublisherWithTimerMetricLogP99(JMAP_RFC8621_PREFIX + 
methodName.value,
       asMailboxGetRequest(invocation.arguments)
         .flatMap(mailboxGetRequest => {
@@ -64,7 +70,7 @@ class MailboxGetMethod @Inject() (serializer: Serializer,
               SMono.just(Invocation.error(errorCode = 
ErrorCode.InvalidArguments,
                 description = s"The following properties 
[${properties.asSetOfString.diff(Mailbox.allProperties).mkString(", ")}] do not 
exist.",
                 methodCallId = invocation.methodCallId))
-            case _ => getMailboxes(capabilities, mailboxGetRequest, 
mailboxSession)
+            case _ => getMailboxes(capabilities, mailboxGetRequest, 
processingContext, mailboxSession)
               .reduce(MailboxGetResults(Set.empty, NotFound(Set.empty)), 
(result1: MailboxGetResults, result2: MailboxGetResults) => 
result1.merge(result2))
               .map(mailboxes => MailboxGetResponse(
                 accountId = mailboxGetRequest.accountId,
@@ -89,12 +95,16 @@ class MailboxGetMethod @Inject() (serializer: Serializer,
 
   private def getMailboxes(capabilities: Set[CapabilityIdentifier],
                            mailboxGetRequest: MailboxGetRequest,
+                           processingContext: ProcessingContext,
                            mailboxSession: MailboxSession): 
SFlux[MailboxGetResults] =
+
     mailboxGetRequest.ids match {
       case None => getAllMailboxes(capabilities, mailboxSession)
         .map(MailboxGetResults.found)
       case Some(ids) => SFlux.fromIterable(ids.value)
-        .flatMap(id => getMailboxResultById(capabilities, id, mailboxSession))
+          .flatMap(id => processingContext.resolveMailboxId(id, 
mailboxIdFactory)
+            .fold(e => SMono.just(MailboxGetResults.notFound(id)),
+              mailboxId => getMailboxResultById(capabilities, mailboxId, 
mailboxSession)))
     }
 
   private def getMailboxResultById(capabilities: Set[CapabilityIdentifier],
diff --git 
a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
 
b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
index aa22699..3fed3f5 100644
--- 
a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
@@ -24,12 +24,13 @@ import 
net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
 import org.apache.james.jmap.json.Fixture._
 import org.apache.james.jmap.json.MailboxGetSerializationTest._
 import org.apache.james.jmap.json.MailboxSerializationTest.MAILBOX
+import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
 import org.apache.james.jmap.mail._
 import org.apache.james.jmap.model.AccountId
 import org.apache.james.mailbox.model.{MailboxId, TestId}
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
-import play.api.libs.json.{JsError, JsSuccess, Json}
+import play.api.libs.json.{JsSuccess, Json}
 
 object MailboxGetSerializationTest {
   private val FACTORY: MailboxId.Factory = new TestId.Factory
@@ -38,22 +39,28 @@ object MailboxGetSerializationTest {
 
   private val ACCOUNT_ID: AccountId = AccountId(id)
 
-  private val MAILBOX_ID_1: MailboxId = FACTORY.fromString("1")
-  private val MAILBOX_ID_2: MailboxId = FACTORY.fromString("2")
+  private val MAILBOX_ID_1: UnparsedMailboxId = "1"
+  private val MAILBOX_ID_2: UnparsedMailboxId = "2"
 
   private val PROPERTIES: Properties = Properties(List("name", "role"))
 }
 
 class MailboxGetSerializationTest extends AnyWordSpec with Matchers {
   "Deserialize MailboxGetRequest" should {
-    "fail when MailboxId.Factory can't deserialize MailboxId" in {
+    "succeed on invalid mailboxId" in {
+      // as they are unparsed
+      val expectedRequestObject = MailboxGetRequest(
+        accountId = ACCOUNT_ID,
+        ids = Some(Ids(List("ab#?"))),
+        properties = None)
+
       SERIALIZER.deserializeMailboxGetRequest(
         """
           |{
           |  "accountId": "aHR0cHM6Ly93d3cuYmFzZTY0ZW5jb2RlLm9yZy8",
           |  "ids": ["ab#?"]
           |}
-          |""".stripMargin) shouldBe a [JsError]
+          |""".stripMargin) should equal(JsSuccess(expectedRequestObject))
     }
 
     "succeed when properties are missing" in {


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org
For additional commands, e-mail: server-dev-h...@james.apache.org

Reply via email to