You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rc...@apache.org on 2020/08/21 02:26:52 UTC

[james-project] 04/14: JAMES-3359 Mailbox/set update should accept creationIds

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 2b1d2e71090b9fc65ff72ad6c1c02bd711e412a7
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Aug 17 14:02:37 2020 +0700

    JAMES-3359 Mailbox/set update should accept creationIds
---
 .../contract/MailboxSetMethodContract.scala        | 89 +++++++++++++++++++++-
 .../org/apache/james/jmap/json/Serializer.scala    | 21 ++++-
 .../org/apache/james/jmap/mail/MailboxSet.scala    |  4 +-
 .../james/jmap/method/MailboxSetMethod.scala       | 13 ++--
 4 files changed, 116 insertions(+), 11 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/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 b1853b9..180a198 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
@@ -2077,7 +2077,94 @@ trait MailboxSetMethodContract {
          |}""".stripMargin)
   }
 
-  // TODO update using creation ids
+  @Test
+  def updateShouldAcceptCreationIds(server: GuiceJamesServer): Unit = {
+    val request =
+      s"""
+        |{
+        |   "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+        |   "methodCalls": [
+        |       ["Mailbox/set",
+        |           {
+        |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+        |                "create": {
+        |                    "C43": {
+        |                      "name": "mailbox"
+        |                    }
+        |                }
+        |           },
+        |    "c1"],
+        |      ["Mailbox/set",
+        |          {
+        |               "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+        |               "update": {
+        |                 "#C43" : {
+        |                   "/name": "newName"
+        |                 }
+        |               }
+        |          },
+        |     "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(), "newName")
+      .serialize()
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |	"sessionState": "75128aab4b1b",
+         |	"methodResponses": [
+         |		["Mailbox/set", {
+         |			"accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |			"newState": "000001",
+         |			"created": {
+         |				"C43": {
+         |					"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/set", {
+         |			"accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |			"newState": "000001",
+         |			"updated": {
+         |				"$mailboxId": {}
+         |			}
+         |		}, "c2"]
+         |	]
+         |}""".stripMargin)
+  }
+
   // TODO invalid path handling (unknown property, invalid name)
   // TODO mailbox already exists, too long name
   // TODO disable destroy / rename of sustem mailbox
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 b655527..a813bf3 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
@@ -241,9 +241,24 @@ class Serializer @Inject() (mailboxIdFactory: MailboxId.Factory) {
   implicit val mailboxCreationRequest: Reads[MailboxCreationRequest] = Json.reads[MailboxCreationRequest]
   private implicit val mailboxPatchObject: Reads[MailboxPatchObject] = Json.valueReads[MailboxPatchObject]
 
-  private implicit val mapPatchObjectByMailboxIdReads: Reads[Map[MailboxId, MailboxPatchObject]] = _.validate[Map[String, MailboxPatchObject]]
-    .map(mapWithStringKey => mapWithStringKey
-      .map(keyValue => (mailboxIdFactory.fromString(keyValue._1), keyValue._2)))
+  private implicit val mapPatchObjectByMailboxIdReads: Reads[Map[UnparsedMailboxId, MailboxPatchObject]] = _.validate[Map[String, MailboxPatchObject]]
+    .flatMap(mapWithStringKey =>{
+      mapWithStringKey
+        .foldLeft[Either[JsError, Map[UnparsedMailboxId, MailboxPatchObject]]](scala.util.Right[JsError, Map[UnparsedMailboxId, MailboxPatchObject]](Map.empty))((acc: Either[JsError, Map[UnparsedMailboxId, MailboxPatchObject]], keyValue) => {
+          acc match {
+            case error@Left(_) => error
+            case scala.util.Right(validatedAcc) =>
+              val refinedKey: Either[String, UnparsedMailboxId] = refineV(keyValue._1)
+              refinedKey match {
+                case Left(error) => Left(JsError(error))
+                case scala.util.Right(unparsedMailboxId) => scala.util.Right(validatedAcc + (unparsedMailboxId -> keyValue._2))
+              }
+          }
+        }) match {
+        case Left(jsError) => jsError
+        case scala.util.Right(value) => JsSuccess(value)
+      }
+    })
 
   private implicit val mapCreationRequestByMailBoxCreationId: Reads[Map[MailboxCreationId, JsObject]] = _.validate[Map[String, JsObject]]
     .flatMap(mapWithStringKey => {
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 51821dd..3452209 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
@@ -34,7 +34,7 @@ import play.api.libs.json.{JsObject, JsString, JsValue}
 case class MailboxSetRequest(accountId: AccountId,
                              ifInState: Option[State],
                              create: Option[Map[MailboxCreationId, JsObject]],
-                             update: Option[Map[MailboxId, MailboxPatchObject]],
+                             update: Option[Map[UnparsedMailboxId, MailboxPatchObject]],
                              destroy: Option[Seq[UnparsedMailboxId]],
                              onDestroyRemoveEmails: Option[RemoveEmailsOnDestroy])
 
@@ -71,7 +71,7 @@ case class MailboxSetResponse(accountId: AccountId,
                               updated: Option[Map[MailboxId, MailboxUpdateResponse]],
                               destroyed: Option[Seq[MailboxId]],
                               notCreated: Option[Map[MailboxCreationId, MailboxSetError]],
-                              notUpdated: Option[Map[MailboxId, MailboxSetError]],
+                              notUpdated: Option[Map[UnparsedMailboxId, MailboxSetError]],
                               notDestroyed: Option[Map[UnparsedMailboxId, MailboxSetError]])
 
 object MailboxSetError {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
index 81fc67d..e774a0b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetMethod.scala
@@ -116,7 +116,7 @@ case class DeletionResults(results: Seq[DeletionResult]) {
 
 sealed trait UpdateResult
 case class UpdateSuccess(mailboxId: MailboxId) extends UpdateResult
-case class UpdateFailure(mailboxId: MailboxId, exception: Throwable) extends UpdateResult {
+case class UpdateFailure(mailboxId: UnparsedMailboxId, exception: Throwable) extends UpdateResult {
   def asMailboxSetError: MailboxSetError = exception match {
     case _ => MailboxSetError.serverFail(Some(SetErrorDescription(exception.getMessage)), None)
   }
@@ -127,7 +127,7 @@ case class UpdateResults(results: Seq[UpdateResult]) {
       case success: UpdateSuccess => Some((success.mailboxId, MailboxSetResponse.empty))
       case _ => None
     }).toMap
-  def notUpdated: Map[MailboxId, MailboxSetError] = results.flatMap(result => result match {
+  def notUpdated: Map[UnparsedMailboxId, MailboxSetError] = results.flatMap(result => result match {
     case failure: UpdateFailure => Some(failure.mailboxId, failure.asMailboxSetError)
     case _ => None
   }).toMap
@@ -157,8 +157,11 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
                               processingContext: ProcessingContext): SMono[UpdateResults] = {
     SFlux.fromIterable(mailboxSetRequest.update.getOrElse(Seq()))
       .flatMap({
-        case (mailboxId: MailboxId, patch: MailboxPatchObject) => updateMailbox(mailboxSession, mailboxId, patch)
-          .onErrorResume(e => SMono.just(UpdateFailure(mailboxId, e)))
+        case (unparsedMailboxId: UnparsedMailboxId, patch: MailboxPatchObject) =>
+          processingContext.resolveMailboxId(unparsedMailboxId, mailboxIdFactory).fold(
+              e => SMono.just(UpdateFailure(unparsedMailboxId, e)),
+              mailboxId => updateMailbox(mailboxSession, mailboxId, patch))
+            .onErrorResume(e => SMono.just(UpdateFailure(unparsedMailboxId, e)))
       })
       .collectSeq()
       .map(UpdateResults)
@@ -181,7 +184,7 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
 
     def renameMailbox: SMono[UpdateResult] = updateMailboxPath(maiboxId, maybeNameUpdate, mailboxSession)
 
-    maybeParseException.map(e => SMono.just[UpdateResult](UpdateFailure(maiboxId, e)))
+    maybeParseException.map(e => SMono.raiseError[UpdateResult](e))
       .getOrElse(renameMailbox)
   }
 


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