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 <btell...@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-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org