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 bt...@apache.org on 2020/08/25 02:19:51 UTC

[james-project] branch master updated (68a9b97 -> 9b0d56c)

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git.


    from 68a9b97  Redirect notifications to the new notifications@james.apache.org ML
     new 8160997  JAMES-3359 Mailbox/set rights partial update
     new 83e0478  JAMES-3359 Mailbox/set partial rights update validation
     new a41445c  JAMES-3359 Mailbox/set delegation & partial rights updates
     new 5f56746  JAMES-3359 add tests for applyRightCommand by MailboxId
     new a910672  JAMES-3359 Add Javadoc for applyRightsCommand by MailboxId
     new 7245b4e  [Refactoring] Use properties field of Mailbox/get in integration tests when no need to check all fields
     new 9b0d56c  [Refactoring] Fix issues in DistributedMailboxSetMethodTest

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../org/apache/james/mailbox/RightManager.java     |  11 +-
 .../apache/james/mailbox/MailboxManagerTest.java   |  30 +
 .../james/mailbox/store/StoreMailboxManager.java   |   5 +
 .../james/mailbox/store/StoreRightManager.java     |   9 +
 .../DistributedMailboxSetMethodTest.java           |   5 +
 .../contract/MailboxGetMethodContract.scala        |   2 -
 .../contract/MailboxSetMethodContract.scala        | 697 +++++++++++++--------
 .../rfc8621/memory/MemoryMailboxSetMethodTest.java |   5 +
 .../org/apache/james/jmap/json/Serializer.scala    |   7 +-
 .../org/apache/james/jmap/mail/MailboxSet.scala    |  87 ++-
 .../james/jmap/method/MailboxSetMethod.scala       |  99 ++-
 11 files changed, 629 insertions(+), 328 deletions(-)


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


[james-project] 05/07: JAMES-3359 Add Javadoc for applyRightsCommand by MailboxId

Posted by bt...@apache.org.
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 a91067299ff2566017a7f6a932a9d7f3a6d17de2
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Wed Aug 19 17:20:55 2020 +0700

    JAMES-3359 Add Javadoc for applyRightsCommand by MailboxId
---
 .../api/src/main/java/org/apache/james/mailbox/RightManager.java | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/RightManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/RightManager.java
index 84eda2b..deade96 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/RightManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/RightManager.java
@@ -98,7 +98,6 @@ public interface RightManager {
      */
     Rfc4314Rights myRights(MailboxPath mailboxPath, MailboxSession session) throws MailboxException;
 
-
     /**
      * Returns the rights applicable to the user who has sent the current
      * request on the mailbox designated by this mailboxPath.
@@ -135,6 +134,14 @@ public interface RightManager {
      */
     void applyRightsCommand(MailboxPath mailboxPath, MailboxACL.ACLCommand mailboxACLCommand, MailboxSession session) throws MailboxException;
 
+    /**
+     * Update the Mailbox ACL of the designated mailbox. We can either ADD REPLACE or REMOVE entries.
+     *
+     * @param mailboxId Id of the mailbox you want to apply rights on.
+     * @param mailboxACLCommand Update to perform.
+     * @param session The session used to determine the user used to apply rights.
+     * @throws MailboxException in case of unknown mailbox or unsupported right
+     */
     void applyRightsCommand(MailboxId mailboxId, MailboxACL.ACLCommand mailboxACLCommand, MailboxSession session) throws MailboxException;
 
     /**


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


[james-project] 01/07: JAMES-3359 Mailbox/set rights partial update

Posted by bt...@apache.org.
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 81609975976d55fe5f023543380cf27a0fc945fd
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Wed Aug 19 09:56:31 2020 +0700

    JAMES-3359 Mailbox/set rights partial update
---
 .../org/apache/james/mailbox/RightManager.java     |  2 +
 .../james/mailbox/store/StoreMailboxManager.java   |  5 ++
 .../james/mailbox/store/StoreRightManager.java     |  9 +++
 .../contract/MailboxSetMethodContract.scala        | 94 ++++++++++++++++++++++
 .../org/apache/james/jmap/json/Serializer.scala    |  7 +-
 .../org/apache/james/jmap/mail/MailboxSet.scala    | 30 ++++++-
 .../james/jmap/method/MailboxSetMethod.scala       | 32 ++++++--
 7 files changed, 169 insertions(+), 10 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/RightManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/RightManager.java
index 4e5c705..84eda2b 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/RightManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/RightManager.java
@@ -135,6 +135,8 @@ public interface RightManager {
      */
     void applyRightsCommand(MailboxPath mailboxPath, MailboxACL.ACLCommand mailboxACLCommand, MailboxSession session) throws MailboxException;
 
+    void applyRightsCommand(MailboxId mailboxId, MailboxACL.ACLCommand mailboxACLCommand, MailboxSession session) throws MailboxException;
+
     /**
      * Reset the Mailbox ACL of the designated mailbox.
      *
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
index 091e40b..faf4d50 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
@@ -797,6 +797,11 @@ public class StoreMailboxManager implements MailboxManager {
     }
 
     @Override
+    public void applyRightsCommand(MailboxId mailboxId, MailboxACL.ACLCommand mailboxACLCommand, MailboxSession session) throws MailboxException {
+        storeRightManager.applyRightsCommand(mailboxId, mailboxACLCommand, session);
+    }
+
+    @Override
     public void setRights(MailboxPath mailboxPath, MailboxACL mailboxACL, MailboxSession session) throws MailboxException {
         storeRightManager.setRights(mailboxPath, mailboxACL, session);
     }
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java
index afd7b90..2845ed8 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java
@@ -163,6 +163,15 @@ public class StoreRightManager implements RightManager {
             }).sneakyThrow()));
     }
 
+    @Override
+    public void applyRightsCommand(MailboxId mailboxId, ACLCommand mailboxACLCommand, MailboxSession session) throws MailboxException {
+        MailboxMapper mapper = mailboxSessionMapperFactory.getMailboxMapper(session);
+        Mailbox mailbox = blockOptional(mapper.findMailboxById(mailboxId))
+            .orElseThrow(() -> new MailboxNotFoundException(mailboxId));
+
+        applyRightsCommand(mailbox.generateAssociatedPath(), mailboxACLCommand, session);
+    }
+
     private void assertSharesBelongsToUserDomain(Username user, ACLCommand mailboxACLCommand) throws DifferentDomainException {
         assertSharesBelongsToUserDomain(user, ImmutableMap.of(mailboxACLCommand.getEntryKey(), mailboxACLCommand.getRights()));
     }
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 3d197e8..b18f8db 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
@@ -5073,4 +5073,98 @@ trait MailboxSetMethodContract {
          |  ]
          |}""".stripMargin)
   }
+
+  @Test
+  def updateShouldAllowPerRightsSetting(server: GuiceJamesServer): Unit = {
+    val path = MailboxPath.forUser(BOB, "mailbox")
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(path, DAVID.asString(), new MailboxACL.Rfc4314Rights(Right.Lookup))
+
+    val request =
+      s"""
+         |{
+         |   "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+         |   "methodCalls": [
+         |       [
+         |           "Mailbox/set",
+         |           {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "update": {
+         |                    "${mailboxId.serialize()}": {
+         |                      "/sharedWith/${ANDRE.asString()}": ["r", "l"]
+         |                    }
+         |                }
+         |           },
+         |    "c1"
+         |       ],
+         |       ["Mailbox/get",
+         |         {
+         |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |           "ids": ["${mailboxId.serialize()}"]
+         |          },
+         |       "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
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |  "sessionState": "75128aab4b1b",
+         |  "methodResponses": [
+         |    ["Mailbox/set", {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "newState": "000001",
+         |      "updated": {
+         |        "${mailboxId.serialize()}": {}
+         |      }
+         |    }, "c1"],
+         |    ["Mailbox/get", {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "state": "000001",
+         |      "list": [{
+         |        "id": "${mailboxId.serialize()}",
+         |        "name": "mailbox",
+         |        "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,
+         |        "namespace": "Personal",
+         |        "rights": {
+         |          "${ANDRE.asString()}": ["l", "r"],
+         |          "${DAVID.asString()}": ["l"]
+         |        }
+         |      }],
+         |      "notFound": []
+         |    }, "c2"]
+         |  ]
+         |}""".stripMargin)
+  }
 }
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 2f8a2a8..ae37616 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,12 +27,13 @@ import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.core.{Domain, Username}
 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.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
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
 import org.apache.james.jmap.model.{Account, Invocation, Session, _}
 import org.apache.james.mailbox.Role
+import org.apache.james.mailbox.model.MailboxACL.{Right => JavaRight}
 import org.apache.james.mailbox.model.{MailboxACL, MailboxId}
 import play.api.libs.functional.syntax._
 import play.api.libs.json._
@@ -189,6 +190,8 @@ class Serializer @Inject() (mailboxIdFactory: MailboxId.Factory) {
       }
     case _ => JsError("Right must be represented as a String")
   }
+  private implicit val mailboxJavaRightReads: Reads[JavaRight] = value => rightRead.reads(value).map(right => right.toMailboxRight)
+  private implicit val mailboxRfc4314RightsReads: Reads[Rfc4314Rights] = Json.valueReads[Rfc4314Rights]
   private implicit val rightsWrites: Writes[Rights] = Json.valueWrites[Rights]
 
   private implicit val mapRightsReads: Reads[Map[Username, Seq[Right]]] = _.validate[Map[String, Seq[Right]]]
@@ -403,4 +406,6 @@ class Serializer @Inject() (mailboxIdFactory: MailboxId.Factory) {
   def deserializeMailboxSetRequest(input: JsValue): JsResult[MailboxSetRequest] = Json.fromJson[MailboxSetRequest](input)
 
   def deserializeRights(input: JsValue): JsResult[Rights] = Json.fromJson[Rights](input)
+
+  def deserializeRfc4314Rights(input: JsValue): JsResult[Rfc4314Rights] = Json.fromJson[Rfc4314Rights](input)
 }
\ No newline at end of file
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 4fc8b92..e5e5525 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
@@ -26,6 +26,7 @@ import eu.timepit.refined.boolean.And
 import eu.timepit.refined.collection.NonEmpty
 import eu.timepit.refined.refineV
 import eu.timepit.refined.string.StartsWith
+import org.apache.james.core.Username
 import org.apache.james.jmap.json.Serializer
 import org.apache.james.jmap.mail.MailboxName.MailboxName
 import org.apache.james.jmap.mail.MailboxPatchObject.MailboxPatchObjectKey
@@ -34,8 +35,8 @@ import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.State.State
 import org.apache.james.jmap.model.{AccountId, CapabilityIdentifier}
 import org.apache.james.mailbox.Role
-import org.apache.james.mailbox.model.MailboxId
 import play.api.libs.json.{JsBoolean, JsError, JsNull, JsObject, JsString, JsSuccess, JsValue}
+import org.apache.james.mailbox.model.{MailboxId, MailboxACL => JavaMailboxACL}
 
 case class MailboxSetRequest(accountId: AccountId,
                              ifInState: Option[State],
@@ -74,6 +75,7 @@ object MailboxPatchObject {
   val unreadEmailsProperty: MailboxPatchObjectKey = "/unreadEmails"
   val totalEmailsProperty: MailboxPatchObjectKey = "/totalEmails"
   val myRightsProperty: MailboxPatchObjectKey = "/myRights"
+  val sharedWithPrefix = "/sharedWith/"
 }
 
 case class MailboxPatchObject(value: Map[String, JsValue]) {
@@ -91,6 +93,7 @@ case class MailboxPatchObject(value: Map[String, JsValue]) {
       case "/totalEmails" => Left(ServerSetPropertyException(MailboxPatchObject.totalEmailsProperty))
       case "/myRights" => Left(ServerSetPropertyException(MailboxPatchObject.myRightsProperty))
       case "/isSubscribed" => IsSubscribedUpdate.parse(newValue)
+      case property: String if property.startsWith(MailboxPatchObject.sharedWithPrefix) => SharedWithPartialUpdate.parse(newValue, property, serializer)
       case property =>
         val refinedKey: Either[String, MailboxPatchObjectKey] = refineV(property)
         refinedKey.fold[Either[PatchUpdateValidationException, Update]](
@@ -186,10 +189,35 @@ object IsSubscribedUpdate {
   }
 }
 
+object SharedWithPartialUpdate {
+  def parse(newValue: JsValue, property: String, serializer: Serializer): Either[PatchUpdateValidationException, Update] =
+    parseUsername(property)
+      .flatMap(username => parseRights(newValue, property, serializer)
+        .map(rights => SharedWithPartialUpdate(username, rights)))
+
+  def parseUsername(property: String): Either[PatchUpdateValidationException, Username] = try {
+    scala.Right(Username.of(property.substring(MailboxPatchObject.sharedWithPrefix.length)))
+  } catch {
+    case e: Exception => Left(InvalidPropertyException(property, e.getMessage))
+  }
+
+  def parseRights(newValue: JsValue, property: String, serializer: Serializer): Either[PatchUpdateValidationException, Rfc4314Rights] = serializer.deserializeRfc4314Rights(newValue) match {
+    case JsSuccess(rights, _) => scala.Right(rights)
+    case JsError(errors) =>
+      val refinedKey: Either[String, MailboxPatchObjectKey] = refineV(property)
+      refinedKey.fold(
+        refinedError => Left(InvalidPropertyException(property = property, cause = s"Invalid property specified in a patch object: $refinedError")),
+        refinedProperty => Left(InvalidUpdateException(refinedProperty, s"Specified value do not match the expected JSON format: $errors")))
+  }
+}
+
 sealed trait Update
 case class NameUpdate(newName: String) extends Update
 case class SharedWithResetUpdate(rights: Rights) extends Update
 case class IsSubscribedUpdate(isSubscribed: Option[IsSubscribed]) extends Update
+case class SharedWithPartialUpdate(username: Username, rights: Rfc4314Rights) extends Update {
+  def asACLCommand(): JavaMailboxACL.ACLCommand = JavaMailboxACL.command().forUser(username).rights(rights.asJava).asReplacement()
+}
 
 class PatchUpdateValidationException() extends IllegalArgumentException
 case class UnsupportedPropertyUpdatedException(property: MailboxPatchObjectKey) extends PatchUpdateValidationException
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 0ecc29a..2f523aa 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
@@ -23,7 +23,7 @@ import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.json.Serializer
 import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, UnparsedMailboxId}
-import org.apache.james.jmap.mail.{InvalidPropertyException, InvalidUpdateException, IsSubscribed, IsSubscribedUpdate, MailboxCreationRequest, MailboxCreationResponse, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, PatchUpdateValidationException, Properties, RemoveEmailsOnDestroy, ServerSetPropertyException, SetErrorDescription, SharedWithResetUpdate, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadT [...]
+import org.apache.james.jmap.mail.{InvalidPropertyException, InvalidUpdateException, IsSubscribed, IsSubscribedUpdate, MailboxCreationRequest, MailboxCreationResponse, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, PatchUpdateValidationException, Properties, RemoveEmailsOnDestroy, ServerSetPropertyException, SetErrorDescription, SharedWithPartialUpdate, SharedWithResetUpdate, SortOrder, TotalEmails, TotalThrea [...]
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.{ClientId, Id, Invocation, ServerId, State}
@@ -195,9 +195,14 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
         case _ => None
       }).headOption
 
+    val partialRightsUpdates: Seq[SharedWithPartialUpdate] = updates.flatMap(x => x match {
+      case Right(SharedWithPartialUpdate(username, rights)) => Some(SharedWithPartialUpdate(username, rights))
+      case _ => None
+    }).toSeq
+
     maybeParseException.map(e => SMono.raiseError[UpdateResult](e))
       .getOrElse(updateMailboxPath(maiboxId, maybeNameUpdate, mailboxSession)
-        .`then`(updateMailboxRights(maiboxId, maybeSharedWithResetUpdate, mailboxSession))
+        .`then`(updateMailboxRights(maiboxId, maybeSharedWithResetUpdate, partialRightsUpdates, mailboxSession))
         .`then`(updateSubscription(maiboxId, maybeIsSubscribedUpdate, mailboxSession)))
   }
 
@@ -239,15 +244,26 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
 
   private def updateMailboxRights(mailboxId: MailboxId,
                                   maybeSharedWithResetUpdate: Option[SharedWithResetUpdate],
+                                  partialUpdates: Seq[SharedWithPartialUpdate],
                                   mailboxSession: MailboxSession): SMono[UpdateResult] = {
-   maybeSharedWithResetUpdate.map(sharedWithResetUpdate => {
+
+    val resetOperation: SMono[Unit] = maybeSharedWithResetUpdate.map(sharedWithResetUpdate => {
       SMono.fromCallable(() => {
         mailboxManager.setRights(mailboxId, sharedWithResetUpdate.rights.toMailboxAcl.asJava, mailboxSession)
-      }).`then`(SMono.just[UpdateResult](UpdateSuccess(mailboxId)))
-        .subscribeOn(Schedulers.elastic())
-    })
-      // No updated properties passed. Noop.
-      .getOrElse(SMono.just[UpdateResult](UpdateSuccess(mailboxId)))
+      }).`then`()
+    }).getOrElse(SMono.empty)
+
+    val partialUpdatesOperation: SMono[Unit] = SFlux.fromIterable(partialUpdates)
+      .flatMap(partialUpdate => SMono.fromCallable(() => {
+        mailboxManager.applyRightsCommand(mailboxId, partialUpdate.asACLCommand(), mailboxSession)
+      }))
+      .`then`()
+
+    SFlux.merge(Seq(resetOperation, partialUpdatesOperation))
+      .`then`()
+      .`then`(SMono.just[UpdateResult](UpdateSuccess(mailboxId)))
+      .subscribeOn(Schedulers.elastic())
+
   }
 
   private def computeMailboxPath(mailbox: MessageManager, nameUpdate: NameUpdate, mailboxSession: MailboxSession): MailboxPath = {


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


[james-project] 06/07: [Refactoring] Use properties field of Mailbox/get in integration tests when no need to check all fields

Posted by bt...@apache.org.
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 7245b4e325195139970070172f30d67b1fdeb803
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Fri Aug 21 15:26:32 2020 +0700

    [Refactoring] Use properties field of Mailbox/get in integration tests when no need to check all fields
---
 .../contract/MailboxGetMethodContract.scala        |   2 -
 .../contract/MailboxSetMethodContract.scala        | 283 ++-------------------
 2 files changed, 22 insertions(+), 263 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 7776b26..b819316 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
@@ -571,7 +571,6 @@ trait MailboxGetMethodContract {
                |    "Mailbox/get",
                |    {
                |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-               |      "properties": ["id", "name", "rights"],
                |      "ids": ["invalid"]
                |    },
                |    "c1"]]
@@ -612,7 +611,6 @@ trait MailboxGetMethodContract {
                |    "Mailbox/get",
                |    {
                |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
-               |      "properties": ["id", "name", "rights"],
                |      "ids": ["#C42"]
                |    },
                |    "c1"]]
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 0398c0e..12f1a4d 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
@@ -852,7 +852,7 @@ trait MailboxSetMethodContract {
   }
 
   @Test
-  def createSHouldNotReturnSubscribeWhenSpecified(server: GuiceJamesServer): Unit = {
+  def createShouldNotReturnSubscribeWhenSpecified(server: GuiceJamesServer): Unit = {
     val request =
       """
         |{
@@ -1031,6 +1031,7 @@ trait MailboxSetMethodContract {
         |       ["Mailbox/get",
         |         {
         |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+        |           "properties": ["id", "name", "rights"],
         |           "ids": ["#C42"]
         |          },
         |       "c2"]
@@ -1091,24 +1092,6 @@ trait MailboxSetMethodContract {
          |			"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": true,
-         |        "namespace":"Personal",
          |				"rights": {
          |					"andre@domain.tld": ["l", "r"]
          |				}
@@ -1255,6 +1238,7 @@ trait MailboxSetMethodContract {
         |       ["Mailbox/get",
         |         {
         |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+        |           "properties": ["id", "name"],
         |           "ids": ["#C42"]
         |          },
         |       "c2"]
@@ -1314,24 +1298,7 @@ trait MailboxSetMethodContract {
          |      "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": true
+         |        "name": "myMailbox"
          |      }],
          |      "notFound":[]
          |    }, "c2"]
@@ -2716,6 +2683,7 @@ trait MailboxSetMethodContract {
         |      ["Mailbox/get",
         |         {
         |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+        |           "properties": ["id", "name"],
         |           "ids": ["${mailboxId.serialize()}"]
         |          },
         |       "c2"]
@@ -2752,24 +2720,7 @@ trait MailboxSetMethodContract {
          |      "state": "000001",
          |      "list": [{
          |        "id": "1",
-         |        "name": "newName",
-         |        "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
+         |        "name": "newName"
          |      }],
          |      "notFound": []
          |    }, "c2"]
@@ -3462,6 +3413,7 @@ trait MailboxSetMethodContract {
         |       ["Mailbox/get",
         |         {
         |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+        |           "properties": ["id", "name", "parentId"],
         |           "ids": ["${childId.serialize()}"]
         |          },
         |       "c2"]
@@ -3499,24 +3451,7 @@ trait MailboxSetMethodContract {
          |      "list": [{
          |        "id": "${childId.serialize()}",
          |        "name": "newChild",
-         |        "parentId": "${parentId.serialize()}",
-         |        "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
+         |        "parentId": "${parentId.serialize()}"
          |      }],
          |      "notFound": []
          |    }, "c2"]
@@ -3550,6 +3485,7 @@ trait MailboxSetMethodContract {
         |       ["Mailbox/get",
         |         {
         |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+        |           "properties": ["id", "name", "parentId"],
         |           "ids": ["${childId.serialize()}"]
         |          },
         |       "c2"]
@@ -3587,24 +3523,7 @@ trait MailboxSetMethodContract {
          |      "list": [{
          |        "id": "${childId.serialize()}",
          |        "name": "child",
-         |        "parentId": "${parentId.serialize()}",
-         |        "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
+         |        "parentId": "${parentId.serialize()}"
          |      }],
          |      "notFound": []
          |    }, "c2"]
@@ -3890,6 +3809,7 @@ trait MailboxSetMethodContract {
          |      ["Mailbox/get",
          |         {
          |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |           "properties": ["id", "isSubscribed"],
          |           "ids": ["${mailboxId.serialize()}"]
          |          },
          |       "c2"]
@@ -3926,23 +3846,6 @@ trait MailboxSetMethodContract {
          |      "state": "000001",
          |      "list": [{
          |        "id": "${mailboxId.serialize()}",
-         |        "name": "mailbox",
-         |        "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": true
          |      }],
          |      "notFound": []
@@ -3983,6 +3886,7 @@ trait MailboxSetMethodContract {
          |      ["Mailbox/get",
          |         {
          |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |           "properties": ["id", "isSubscribed"],
          |           "ids": ["${mailboxId.serialize()}"]
          |          },
          |       "c4"]
@@ -4026,23 +3930,6 @@ trait MailboxSetMethodContract {
          |      "state": "000001",
          |      "list": [{
          |        "id": "${mailboxId.serialize()}",
-         |        "name": "mailbox",
-         |        "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": []
@@ -4073,6 +3960,7 @@ trait MailboxSetMethodContract {
          |      ["Mailbox/get",
          |         {
          |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |           "properties": ["id", "isSubscribed"],
          |           "ids": ["${mailboxId.serialize()}"]
          |          },
          |       "c4"]
@@ -4109,23 +3997,6 @@ trait MailboxSetMethodContract {
          |      "state": "000001",
          |      "list": [{
          |        "id": "${mailboxId.serialize()}",
-         |        "name": "mailbox",
-         |        "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": true
          |      }],
          |      "notFound": []
@@ -4161,6 +4032,7 @@ trait MailboxSetMethodContract {
          |      ["Mailbox/get",
          |         {
          |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |           "properties": ["id", "isSubscribed", "namespace"],
          |           "ids": ["${mailboxId.serialize()}"]
          |          },
          |       "c4"]
@@ -4197,28 +4069,8 @@ trait MailboxSetMethodContract {
          |      "state": "000001",
          |      "list": [{
          |        "id": "${mailboxId.serialize()}",
-         |        "name": "mailbox",
-         |        "sortOrder": 1000,
-         |        "totalEmails": 0,
-         |        "unreadEmails": 0,
-         |        "totalThreads": 0,
-         |        "unreadThreads": 0,
-         |        "myRights": {
-         |          "mayReadItems": false,
-         |          "mayAddItems": false,
-         |          "mayRemoveItems": false,
-         |          "maySetSeen": false,
-         |          "maySetKeywords": false,
-         |          "mayCreateChild": false,
-         |          "mayRename": false,
-         |          "mayDelete": false,
-         |          "maySubmit": false
-         |        },
          |        "isSubscribed": true,
-         |        "namespace": "Delegated[andre@domain.tld]",
-         |        "rights": {
-         |          "bob@domain.tld": ["l"]
-         |        }
+         |        "namespace": "Delegated[andre@domain.tld]"
          |      }],
          |      "notFound": []
          |    }, "c4"]
@@ -4262,6 +4114,7 @@ trait MailboxSetMethodContract {
          |      ["Mailbox/get",
          |         {
          |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |           "properties": ["id", "isSubscribed", "namespace"],
          |           "ids": ["${mailboxId.serialize()}"]
          |          },
          |       "c4"]
@@ -4305,28 +4158,8 @@ trait MailboxSetMethodContract {
          |      "state": "000001",
          |      "list": [{
          |        "id": "${mailboxId.serialize()}",
-         |        "name": "mailbox",
-         |        "sortOrder": 1000,
-         |        "totalEmails": 0,
-         |        "unreadEmails": 0,
-         |        "totalThreads": 0,
-         |        "unreadThreads": 0,
-         |        "myRights": {
-         |          "mayReadItems": false,
-         |          "mayAddItems": false,
-         |          "mayRemoveItems": false,
-         |          "maySetSeen": false,
-         |          "maySetKeywords": false,
-         |          "mayCreateChild": false,
-         |          "mayRename": false,
-         |          "mayDelete": false,
-         |          "maySubmit": false
-         |        },
          |        "isSubscribed": false,
-         |        "namespace": "Delegated[andre@domain.tld]",
-         |        "rights": {
-         |          "bob@domain.tld": ["l"]
-         |        }
+         |        "namespace": "Delegated[andre@domain.tld]"
          |      }],
          |      "notFound": []
          |    }, "c4"]
@@ -4449,6 +4282,7 @@ trait MailboxSetMethodContract {
          |      ["Mailbox/get",
          |         {
          |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |           "properties": ["id", "isSubscribed", "namespace"],
          |           "ids": ["${mailboxId.serialize()}"]
          |          },
          |       "c4"]
@@ -4492,28 +4326,8 @@ trait MailboxSetMethodContract {
          |      "state": "000001",
          |      "list": [{
          |        "id": "${mailboxId.serialize()}",
-         |        "name": "mailbox",
-         |        "sortOrder": 1000,
-         |        "totalEmails": 0,
-         |        "unreadEmails": 0,
-         |        "totalThreads": 0,
-         |        "unreadThreads": 0,
-         |        "myRights": {
-         |          "mayReadItems": false,
-         |          "mayAddItems": false,
-         |          "mayRemoveItems": false,
-         |          "maySetSeen": false,
-         |          "maySetKeywords": false,
-         |          "mayCreateChild": false,
-         |          "mayRename": false,
-         |          "mayDelete": false,
-         |          "maySubmit": false
-         |        },
          |        "isSubscribed": false,
-         |        "namespace": "Delegated[andre@domain.tld]",
-         |        "rights": {
-         |          "bob@domain.tld": ["l"]
-         |        }
+         |        "namespace": "Delegated[andre@domain.tld]"
          |      }],
          |      "notFound": []
          |    }, "c4"]
@@ -4686,6 +4500,7 @@ trait MailboxSetMethodContract {
          |       ["Mailbox/get",
          |         {
          |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |           "properties": ["id", "rights"],
          |           "ids": ["${mailboxId.serialize()}"]
          |          },
          |       "c2"]
@@ -4722,25 +4537,6 @@ trait MailboxSetMethodContract {
          |      "state": "000001",
          |      "list": [{
          |        "id": "${mailboxId.serialize()}",
-         |        "name": "mailbox",
-         |        "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,
-         |        "namespace": "Personal",
          |        "rights": {
          |          "${ANDRE.asString()}": ["l", "r"]
          |        }
@@ -4784,6 +4580,7 @@ trait MailboxSetMethodContract {
          |       ["Mailbox/get",
          |         {
          |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |           "properties": ["id", "rights"],
          |           "ids": ["${mailboxId.serialize()}"]
          |          },
          |       "c2"]
@@ -4820,25 +4617,6 @@ trait MailboxSetMethodContract {
          |      "state": "000001",
          |      "list": [{
          |        "id": "${mailboxId.serialize()}",
-         |        "name": "mailbox",
-         |        "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,
-         |        "namespace": "Personal",
          |        "rights": {
          |          "${ANDRE.asString()}": ["l", "r"],
          |          "${DAVID.asString()}": ["l", "r", "w"]
@@ -4881,6 +4659,7 @@ trait MailboxSetMethodContract {
          |       ["Mailbox/get",
          |         {
          |           "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |           "properties": ["id", "namespace", "rights"],
          |           "ids": ["${mailboxId.serialize()}"]
          |          },
          |       "c2"]
@@ -4920,24 +4699,6 @@ trait MailboxSetMethodContract {
          |      "state": "000001",
          |      "list": [{
          |        "id": "${mailboxId.serialize()}",
-         |        "name": "mailbox",
-         |        "sortOrder": 1000,
-         |        "totalEmails": 0,
-         |        "unreadEmails": 0,
-         |        "totalThreads": 0,
-         |        "unreadThreads": 0,
-         |        "myRights": {
-         |          "mayReadItems": false,
-         |          "mayAddItems": false,
-         |          "mayRemoveItems": false,
-         |          "maySetSeen": false,
-         |          "maySetKeywords": false,
-         |          "mayCreateChild": false,
-         |          "mayRename": false,
-         |          "mayDelete": false,
-         |          "maySubmit": false
-         |        },
-         |        "isSubscribed": false,
          |        "namespace": "Delegated[andre@domain.tld]",
          |        "rights": {
          |          "bob@domain.tld": ["l"]


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


[james-project] 03/07: JAMES-3359 Mailbox/set delegation & partial rights updates

Posted by bt...@apache.org.
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 a41445c0282e24c8aaa219b52d9eeff97099b0c7
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Wed Aug 19 10:56:20 2020 +0700

    JAMES-3359 Mailbox/set delegation & partial rights updates
---
 .../contract/MailboxSetMethodContract.scala        | 128 ++++++++++++++++++++-
 1 file changed, 127 insertions(+), 1 deletion(-)

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 90828ee..b624f3b 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
@@ -5168,7 +5168,6 @@ trait MailboxSetMethodContract {
          |}""".stripMargin)
   }
 
-  // TODO testing delegation & partial updates
   // TODO write test for rights store manager applyCommandById in MailboxManagerTest
 
   @Test
@@ -5355,4 +5354,131 @@ trait MailboxSetMethodContract {
          |    ]
          |}""".stripMargin)
   }
+
+  @Test
+  def partialUpdateShouldFailOnOthersMailbox(server: GuiceJamesServer): Unit = {
+    val path = MailboxPath.forUser(ANDRE, "mailbox")
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+
+    val request =
+      s"""
+         |{
+         |   "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+         |   "methodCalls": [
+         |       [
+         |           "Mailbox/set",
+         |           {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "update": {
+         |                    "${mailboxId.serialize()}": {
+         |                          "/sharedWith/${BOB.asString()}": ["r"],
+         |                          "/sharedWith/${DAVID.asString()}": ["r"]
+         |                      }
+         |                }
+         |           },
+         |    "c1"
+         |       ]
+         |   ]
+         |}
+         |""".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
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |  "sessionState": "75128aab4b1b",
+         |  "methodResponses": [
+         |    ["Mailbox/set", {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "newState": "000001",
+         |      "notUpdated": {
+         |        "${mailboxId.serialize()}": {
+         |          "type": "notFound",
+         |          "description": "${mailboxId.serialize()} can not be found"
+         |        }
+         |      }
+         |    }, "c1"]
+         |  ]
+         |}""".stripMargin)
+
+    assertThat(server.getProbe(classOf[ACLProbeImpl]).retrieveRights(path)
+      .getEntries)
+      .doesNotContainKeys(EntryKey.createUserEntryKey(DAVID))
+  }
+
+  @Test
+  def partialUpdateShouldFailWhenDelegatedMailbox(server: GuiceJamesServer): Unit = {
+    val path = MailboxPath.forUser(ANDRE, "mailbox")
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+
+    server.getProbe(classOf[ACLProbeImpl])
+      .replaceRights(path, BOB.asString(), new MailboxACL.Rfc4314Rights(Right.Lookup))
+
+    val request =
+      s"""
+         |{
+         |   "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+         |   "methodCalls": [
+         |       [
+         |           "Mailbox/set",
+         |           {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "update": {
+         |                    "${mailboxId.serialize()}": {
+         |                          "/sharedWith/${BOB.asString()}": ["r"],
+         |                          "/sharedWith/${DAVID.asString()}": ["r"]
+         |                      }
+         |                }
+         |           },
+         |    "c1"
+         |       ]
+         |   ]
+         |}
+         |""".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
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |  "sessionState": "75128aab4b1b",
+         |  "methodResponses": [
+         |    ["Mailbox/set", {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "newState": "000001",
+         |      "notUpdated": {
+         |        "${mailboxId.serialize()}": {
+         |          "type": "invalidArguments",
+         |          "description": "Invalid change to a delegated mailbox"
+         |        }
+         |      }
+         |    }, "c1"]
+         |  ]
+         |}""".stripMargin)
+
+    assertThat(server.getProbe(classOf[ACLProbeImpl]).retrieveRights(path)
+      .getEntries)
+      .doesNotContainKeys(EntryKey.createUserEntryKey(DAVID))
+  }
 }


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


[james-project] 07/07: [Refactoring] Fix issues in DistributedMailboxSetMethodTest

Posted by bt...@apache.org.
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 9b0d56ce676d51c54e0f851e48f902e21a84bb65
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Mon Aug 24 10:34:15 2020 +0700

    [Refactoring] Fix issues in DistributedMailboxSetMethodTest
---
 .../jmap/rfc8621/distributed/DistributedMailboxSetMethodTest.java | 5 +++++
 .../james/jmap/rfc8621/contract/MailboxSetMethodContract.scala    | 8 ++++----
 .../james/jmap/rfc8621/memory/MemoryMailboxSetMethodTest.java     | 5 +++++
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxSetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxSetMethodTest.java
index e2842c1..44d55ac 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxSetMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxSetMethodTest.java
@@ -59,4 +59,9 @@ public class DistributedMailboxSetMethodTest implements MailboxSetMethodContract
     public MailboxId randomMailboxId() {
         return CassandraId.of(UUIDs.timeBased());
     }
+
+    @Override
+    public String errorInvalidMailboxIdMessage(String value) {
+        return String.format("%s is not a mailboxId: Invalid UUID string: %s", value, value);
+    }
 }
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 12f1a4d..f968913 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
@@ -58,6 +58,7 @@ trait MailboxSetMethodContract {
   }
 
   def randomMailboxId: MailboxId
+  def errorInvalidMailboxIdMessage(value: String): String
 
   @Test
   def updateShouldFailWhenModifyingRole(server: GuiceJamesServer): Unit = {
@@ -2295,7 +2296,6 @@ trait MailboxSetMethodContract {
         .body
         .asString
 
-    val message: String = "invalid is not a mailboxId: For input string: \\\"invalid\\\""
     assertThatJson(response).isEqualTo(
       s"""{
          |  "sessionState": "75128aab4b1b",
@@ -2307,7 +2307,7 @@ trait MailboxSetMethodContract {
          |      "notDestroyed": {
          |        "invalid": {
          |          "type": "invalidArguments",
-         |          "description": "$message"
+         |          "description": "${errorInvalidMailboxIdMessage("invalid")}"
          |        }
          |      }
          |    },
@@ -2712,14 +2712,14 @@ trait MailboxSetMethodContract {
          |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
          |      "newState": "000001",
          |      "updated": {
-         |        "1": {}
+         |        "${mailboxId.serialize()}": {}
          |      }
          |    }, "c2"],
          |    ["Mailbox/get", {
          |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
          |      "state": "000001",
          |      "list": [{
-         |        "id": "1",
+         |        "id": "${mailboxId.serialize()}",
          |        "name": "newName"
          |      }],
          |      "notFound": []
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryMailboxSetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryMailboxSetMethodTest.java
index 519b119..ee6e1c6 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryMailboxSetMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/memory-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/memory/MemoryMailboxSetMethodTest.java
@@ -44,4 +44,9 @@ public class MemoryMailboxSetMethodTest implements MailboxSetMethodContract {
     public MailboxId randomMailboxId() {
         return InMemoryId.of(ThreadLocalRandom.current().nextInt(100000) + 100);
     }
+
+    @Override
+    public String errorInvalidMailboxIdMessage(String value) {
+        return String.format("%s is not a mailboxId: For input string: \\\"%s\\\"", value, value);
+    }
 }


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


[james-project] 04/07: JAMES-3359 add tests for applyRightCommand by MailboxId

Posted by bt...@apache.org.
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 5f567461bc05bd02cfcfec9fd34e1e87b16b3859
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Wed Aug 19 11:09:06 2020 +0700

    JAMES-3359 add tests for applyRightCommand by MailboxId
---
 .../apache/james/mailbox/MailboxManagerTest.java   | 30 ++++++++++++++++++++++
 .../contract/MailboxSetMethodContract.scala        |  2 --
 2 files changed, 30 insertions(+), 2 deletions(-)

diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
index 04a4a68..95a220a 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java
@@ -2854,6 +2854,23 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
         }
 
         @Test
+        void applyRightsCommandByIdShouldThrowWhenNotOwner() throws Exception {
+            MailboxPath mailboxPath = MailboxPath.forUser(USER_2, "mailbox");
+            MailboxId mailboxId = mailboxManager.createMailbox(mailboxPath, session2).get();
+            mailboxManager.setRights(mailboxPath,  MailboxACL.EMPTY.apply(MailboxACL.command()
+                .key(MailboxACL.EntryKey.createUserEntryKey(USER_1))
+                .rights(new MailboxACL.Rfc4314Rights(MailboxACL.Right.Lookup))
+                .asAddition()), session2);
+
+            assertThatThrownBy(() -> mailboxManager.applyRightsCommand(mailboxId,
+                MailboxACL.command()
+                    .key(MailboxACL.EntryKey.createUserEntryKey(USER_1))
+                    .rights(MailboxACL.FULL_RIGHTS)
+                    .asAddition(), session))
+                .isInstanceOf(InsufficientRightsException.class);
+        }
+
+        @Test
         void setRightsByIdShouldThrowWhenNoRights() throws Exception {
             MailboxPath mailboxPath = MailboxPath.forUser(USER_2, "mailbox");
             MailboxId mailboxId = mailboxManager.createMailbox(mailboxPath, session2).get();
@@ -2891,5 +2908,18 @@ public abstract class MailboxManagerTest<T extends MailboxManager> {
                     .asAddition(), session))
                 .isInstanceOf(MailboxNotFoundException.class);
         }
+
+        @Test
+        void applyRightsCommandByIdShouldThrowWhenNoRights() throws Exception {
+            MailboxPath mailboxPath = MailboxPath.forUser(USER_2, "mailbox");
+            MailboxId mailboxId = mailboxManager.createMailbox(mailboxPath, session2).get();
+
+            assertThatThrownBy(() -> mailboxManager.applyRightsCommand(mailboxId,
+                MailboxACL.command()
+                    .key(MailboxACL.EntryKey.createUserEntryKey(USER_1))
+                    .rights(MailboxACL.FULL_RIGHTS)
+                    .asAddition(), session))
+                .isInstanceOf(MailboxNotFoundException.class);
+        }
     }
 }
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 b624f3b..0398c0e 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
@@ -5168,8 +5168,6 @@ trait MailboxSetMethodContract {
          |}""".stripMargin)
   }
 
-  // TODO write test for rights store manager applyCommandById in MailboxManagerTest
-
   @Test
   def partialRightsUpdateShouldFailWhenInvalidUsername(server: GuiceJamesServer): Unit = {
     val path = MailboxPath.forUser(BOB, "mailbox")


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


[james-project] 02/07: JAMES-3359 Mailbox/set partial rights update validation

Posted by bt...@apache.org.
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 83e047842eaf52c3d83983ccb985b43778691b79
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Wed Aug 19 10:44:41 2020 +0700

    JAMES-3359 Mailbox/set partial rights update validation
---
 .../contract/MailboxSetMethodContract.scala        | 188 +++++++++++++++++++++
 .../org/apache/james/jmap/mail/MailboxSet.scala    |  57 ++++++-
 .../james/jmap/method/MailboxSetMethod.scala       |  87 ++++------
 3 files changed, 272 insertions(+), 60 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 b18f8db..90828ee 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
@@ -5167,4 +5167,192 @@ trait MailboxSetMethodContract {
          |  ]
          |}""".stripMargin)
   }
+
+  // TODO testing delegation & partial updates
+  // TODO write test for rights store manager applyCommandById in MailboxManagerTest
+
+  @Test
+  def partialRightsUpdateShouldFailWhenInvalidUsername(server: GuiceJamesServer): Unit = {
+    val path = MailboxPath.forUser(BOB, "mailbox")
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+
+    val request =
+      s"""
+         |{
+         |   "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+         |   "methodCalls": [
+         |       [
+         |           "Mailbox/set",
+         |           {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "update": {
+         |                    "${mailboxId.serialize()}": {
+         |                      "/sharedWith/invalid@invalid@${DOMAIN.asString()}": ["r", "l"]
+         |                    }
+         |                }
+         |           },
+         |    "c1"
+         |       ]
+         |   ]
+         |}
+         |""".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
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |  "sessionState": "75128aab4b1b",
+         |  "methodResponses": [
+         |    ["Mailbox/set", {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "newState": "000001",
+         |      "notUpdated": {
+         |        "${mailboxId.serialize()}": {
+         |          "type": "invalidPatch",
+         |          "description": "The username should not contain multiple domain delimiter. Value: invalid@invalid@domain.tld"
+         |        }
+         |      }
+         |    }, "c1"]
+         |  ]
+         |}""".stripMargin)
+  }
+
+  @Test
+  def partialRightsUpdateShouldFailWhenInvalidRights(server: GuiceJamesServer): Unit = {
+    val path = MailboxPath.forUser(BOB, "mailbox")
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+
+    val request =
+      s"""
+         |{
+         |   "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+         |   "methodCalls": [
+         |       [
+         |           "Mailbox/set",
+         |           {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "update": {
+         |                    "${mailboxId.serialize()}": {
+         |                      "/sharedWith/${ANDRE.asString()}": ["p"]
+         |                    }
+         |                }
+         |           },
+         |    "c1"
+         |       ]
+         |   ]
+         |}
+         |""".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
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |    "sessionState": "75128aab4b1b",
+         |    "methodResponses": [
+         |        [
+         |            "Mailbox/set",
+         |            {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "newState": "000001",
+         |                "notUpdated": {
+         |                    "${mailboxId.serialize()}": {
+         |                        "type": "invalidArguments",
+         |                        "description": "Specified value do not match the expected JSON format: List(((0),List(JsonValidationError(List(Unknown right 'p'),ArraySeq()))))",
+         |                        "properties": [
+         |                            "/sharedWith/andre@domain.tld"
+         |                        ]
+         |                    }
+         |                }
+         |            },
+         |            "c1"
+         |        ]
+         |    ]
+         |}""".stripMargin)
+  }
+
+  @Test
+  def rightsUpdateShouldFailWhenBothPartialAndReset(server: GuiceJamesServer): Unit = {
+    val path = MailboxPath.forUser(BOB, "mailbox")
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path)
+
+    val request =
+      s"""
+         |{
+         |   "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:apache:james:params:jmap:mail:shares" ],
+         |   "methodCalls": [
+         |       [
+         |           "Mailbox/set",
+         |           {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "update": {
+         |                    "${mailboxId.serialize()}": {
+         |                      "/sharedWith": {
+         |                        "${DAVID.asString()}":["r", "l", "w"]
+         |                      },
+         |                      "/sharedWith/${ANDRE.asString()}": ["r"]
+         |                    }
+         |                }
+         |           },
+         |    "c1"
+         |       ]
+         |   ]
+         |}
+         |""".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
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |    "sessionState": "75128aab4b1b",
+         |    "methodResponses": [
+         |        [
+         |            "Mailbox/set",
+         |            {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "newState": "000001",
+         |                "notUpdated": {
+         |                    "${mailboxId.serialize()}": {
+         |                        "type": "invalidPatch",
+         |                        "description": "Resetting rights and partial updates cannot be done in the same method call"
+         |                    }
+         |                }
+         |            },
+         |            "c1"
+         |        ]
+         |    ]
+         |}""".stripMargin)
+  }
 }
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 e5e5525..267fb4d 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
@@ -79,7 +79,54 @@ object MailboxPatchObject {
 }
 
 case class MailboxPatchObject(value: Map[String, JsValue]) {
-  def updates(serializer: Serializer): Iterable[Either[PatchUpdateValidationException, Update]] = value.map({
+  def validate(serializer: Serializer): Either[PatchUpdateValidationException, ValidatedMailboxPathObject] = {
+    val asUpdatedIterable = updates(serializer)
+
+    val maybeParseException: Option[PatchUpdateValidationException] = asUpdatedIterable
+      .flatMap(x => x match {
+        case Left(e) => Some(e)
+        case _ => None
+      }).headOption
+
+    val nameUpdate: Option[NameUpdate] = asUpdatedIterable
+      .flatMap(x => x match {
+        case scala.Right(NameUpdate(newName)) => Some(NameUpdate(newName))
+        case _ => None
+      }).headOption
+
+    val isSubscribedUpdate: Option[IsSubscribedUpdate] = asUpdatedIterable
+      .flatMap(x => x match {
+        case scala.Right(IsSubscribedUpdate(isSubscribed)) => Some(IsSubscribedUpdate(isSubscribed))
+        case _ => None
+      }).headOption
+
+    val rightsReset: Option[SharedWithResetUpdate] = asUpdatedIterable
+      .flatMap(x => x match {
+        case scala.Right(SharedWithResetUpdate(rights)) => Some(SharedWithResetUpdate(rights))
+        case _ => None
+      }).headOption
+
+    val partialRightsUpdates: Seq[SharedWithPartialUpdate] = asUpdatedIterable.flatMap(x => x match {
+      case scala.Right(SharedWithPartialUpdate(username, rights)) => Some(SharedWithPartialUpdate(username, rights))
+      case _ => None
+    }).toSeq
+
+    val bothPartialAndResetRights: Option[PatchUpdateValidationException] = if (rightsReset.isDefined && partialRightsUpdates.nonEmpty) {
+      Some(InvalidPatchException("Resetting rights and partial updates cannot be done in the same method call"))
+    } else {
+       None
+    }
+    maybeParseException
+      .orElse(bothPartialAndResetRights)
+      .map(e => Left(e))
+      .getOrElse(scala.Right(ValidatedMailboxPathObject(
+        nameUpdate = nameUpdate,
+        isSubscribedUpdate = isSubscribedUpdate,
+        rightsReset = rightsReset,
+        rightsPartialUpdates = partialRightsUpdates)))
+  }
+
+  private def updates(serializer: Serializer): Iterable[Either[PatchUpdateValidationException, Update]] = value.map({
     case (property, newValue) => property match {
       case "/name" => NameUpdate.parse(newValue)
       case "/sharedWith" => SharedWithResetUpdate.parse(newValue, serializer)
@@ -103,6 +150,11 @@ case class MailboxPatchObject(value: Map[String, JsValue]) {
   })
 }
 
+case class ValidatedMailboxPathObject(nameUpdate: Option[NameUpdate],
+                                      isSubscribedUpdate: Option[IsSubscribedUpdate],
+                                      rightsReset: Option[SharedWithResetUpdate],
+                                      rightsPartialUpdates: Seq[SharedWithPartialUpdate])
+
 case class MailboxSetResponse(accountId: AccountId,
                               oldState: Option[State],
                               newState: State,
@@ -224,4 +276,5 @@ case class UnsupportedPropertyUpdatedException(property: MailboxPatchObjectKey)
 case class InvalidPropertyUpdatedException(property: MailboxPatchObjectKey) extends PatchUpdateValidationException
 case class InvalidPropertyException(property: String, cause: String) extends PatchUpdateValidationException
 case class InvalidUpdateException(property: MailboxPatchObjectKey, cause: String) extends PatchUpdateValidationException
-case class ServerSetPropertyException(property: MailboxPatchObjectKey) extends PatchUpdateValidationException
\ No newline at end of file
+case class ServerSetPropertyException(property: MailboxPatchObjectKey) extends PatchUpdateValidationException
+case class InvalidPatchException(cause: String) extends PatchUpdateValidationException
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 2f523aa..46ad2d7 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
@@ -23,7 +23,7 @@ import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.json.Serializer
 import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, UnparsedMailboxId}
-import org.apache.james.jmap.mail.{InvalidPropertyException, InvalidUpdateException, IsSubscribed, IsSubscribedUpdate, MailboxCreationRequest, MailboxCreationResponse, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, PatchUpdateValidationException, Properties, RemoveEmailsOnDestroy, ServerSetPropertyException, SetErrorDescription, SharedWithPartialUpdate, SharedWithResetUpdate, SortOrder, TotalEmails, TotalThrea [...]
+import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, IsSubscribed, IsSubscribedUpdate, MailboxCreationRequest, MailboxCreationResponse, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, PatchUpdateValidationException, Properties, RemoveEmailsOnDestroy, ServerSetPropertyException, SetErrorDescription, SharedWithPartialUpdate, SharedWithResetUpdate, SortOrder,  [...]
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.{ClientId, Id, Invocation, ServerId, State}
@@ -101,7 +101,7 @@ case class DeletionResults(results: Seq[DeletionResult]) {
       case failure: DeletionFailure => Some(failure.mailboxId, failure.asMailboxSetError)
       case _ => None
     })
-    .toMap
+      .toMap
 }
 
 sealed trait UpdateResult
@@ -115,6 +115,7 @@ case class UpdateFailure(mailboxId: UnparsedMailboxId, exception: Throwable) ext
     case e: InvalidUpdateException => MailboxSetError.invalidArgument(Some(SetErrorDescription(s"${e.cause}")), Some(Properties(List(e.property))))
     case e: ServerSetPropertyException => MailboxSetError.invalidArgument(Some(SetErrorDescription("Can not modify server-set properties")), Some(Properties(List(e.property))))
     case e: InvalidPropertyException => MailboxSetError.invalidPatch(Some(SetErrorDescription(s"${e.cause}")))
+    case e: InvalidPatchException => MailboxSetError.invalidPatch(Some(SetErrorDescription(s"${e.cause}")))
     case e: SystemMailboxChangeException => MailboxSetError.invalidArgument(Some(SetErrorDescription("Invalid change to a system mailbox")), Some(Properties(List("/name"))))
     case e: InsufficientRightsException => MailboxSetError.invalidArgument(Some(SetErrorDescription("Invalid change to a delegated mailbox")), None)
     case _ => MailboxSetError.serverFail(Some(SetErrorDescription(exception.getMessage)), None)
@@ -159,8 +160,8 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
       .flatMap({
         case (unparsedMailboxId: UnparsedMailboxId, patch: MailboxPatchObject) =>
           processingContext.resolveMailboxId(unparsedMailboxId, mailboxIdFactory).fold(
-              e => SMono.just(UpdateFailure(unparsedMailboxId, e)),
-              mailboxId => updateMailbox(mailboxSession, mailboxId, patch))
+            e => SMono.just(UpdateFailure(unparsedMailboxId, e)),
+            mailboxId => updateMailbox(mailboxSession, mailboxId, patch))
             .onErrorResume(e => SMono.just(UpdateFailure(unparsedMailboxId, e)))
       })
       .collectSeq()
@@ -168,46 +169,17 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
   }
 
   private def updateMailbox(mailboxSession: MailboxSession,
-                            maiboxId: MailboxId,
+                            mailboxId: MailboxId,
                             patch: MailboxPatchObject): SMono[UpdateResult] = {
-    val updates = patch.updates(serializer)
-    val maybeParseException: Option[PatchUpdateValidationException] = updates
-      .flatMap(x => x match {
-        case Left(e) => Some(e)
-        case _ => None
-      }).headOption
-
-    val maybeNameUpdate: Option[NameUpdate] = updates
-      .flatMap(x => x match {
-        case Right(NameUpdate(newName)) => Some(NameUpdate(newName))
-        case _ => None
-      }).headOption
-
-    val maybeSharedWithResetUpdate: Option[SharedWithResetUpdate] = updates
-      .flatMap(x => x match {
-        case Right(SharedWithResetUpdate(rights)) => Some(SharedWithResetUpdate(rights))
-        case _ => None
-      }).headOption
-
-    val maybeIsSubscribedUpdate: Option[IsSubscribedUpdate] = updates
-      .flatMap(x => x match {
-        case Right(IsSubscribedUpdate(isSubscribed)) => Some(IsSubscribedUpdate(isSubscribed))
-        case _ => None
-      }).headOption
-
-    val partialRightsUpdates: Seq[SharedWithPartialUpdate] = updates.flatMap(x => x match {
-      case Right(SharedWithPartialUpdate(username, rights)) => Some(SharedWithPartialUpdate(username, rights))
-      case _ => None
-    }).toSeq
-
-    maybeParseException.map(e => SMono.raiseError[UpdateResult](e))
-      .getOrElse(updateMailboxPath(maiboxId, maybeNameUpdate, mailboxSession)
-        .`then`(updateMailboxRights(maiboxId, maybeSharedWithResetUpdate, partialRightsUpdates, mailboxSession))
-        .`then`(updateSubscription(maiboxId, maybeIsSubscribedUpdate, mailboxSession)))
+    patch.validate(serializer)
+      .fold(e => SMono.raiseError(e), validatedPatch =>
+        updateMailboxPath(mailboxId, validatedPatch, mailboxSession)
+          .`then`(updateMailboxRights(mailboxId, validatedPatch, mailboxSession))
+          .`then`(updateSubscription(mailboxId, validatedPatch, mailboxSession)))
   }
 
-  private def updateSubscription(mailboxId: MailboxId, maybeIsSubscribedUpdate: Option[IsSubscribedUpdate], mailboxSession: MailboxSession): SMono[UpdateResult] = {
-    maybeIsSubscribedUpdate.map(isSubscribedUpdate => {
+  private def updateSubscription(mailboxId: MailboxId, validatedPatch: ValidatedMailboxPathObject, mailboxSession: MailboxSession): SMono[UpdateResult] = {
+    validatedPatch.isSubscribedUpdate.map(isSubscribedUpdate => {
       SMono.fromCallable(() => {
         val mailbox = mailboxManager.getMailbox(mailboxId, mailboxSession)
         val isOwner = mailbox.getMailboxPath.belongsTo(mailboxSession)
@@ -224,8 +196,8 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
       .getOrElse(SMono.just[UpdateResult](UpdateSuccess(mailboxId)))
   }
 
-  private def updateMailboxPath(mailboxId: MailboxId, maybeNameUpdate: Option[NameUpdate], mailboxSession: MailboxSession): SMono[UpdateResult] = {
-    maybeNameUpdate.map(nameUpdate => {
+  private def updateMailboxPath(mailboxId: MailboxId, validatedPatch: ValidatedMailboxPathObject, mailboxSession: MailboxSession): SMono[UpdateResult] = {
+    validatedPatch.nameUpdate.map(nameUpdate => {
       SMono.fromCallable(() => {
         val mailbox = mailboxManager.getMailbox(mailboxId, mailboxSession)
         if (isASystemMailbox(mailbox)) {
@@ -243,17 +215,16 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
   }
 
   private def updateMailboxRights(mailboxId: MailboxId,
-                                  maybeSharedWithResetUpdate: Option[SharedWithResetUpdate],
-                                  partialUpdates: Seq[SharedWithPartialUpdate],
+                                  validatedPatch: ValidatedMailboxPathObject,
                                   mailboxSession: MailboxSession): SMono[UpdateResult] = {
 
-    val resetOperation: SMono[Unit] = maybeSharedWithResetUpdate.map(sharedWithResetUpdate => {
+    val resetOperation: SMono[Unit] = validatedPatch.rightsReset.map(sharedWithResetUpdate => {
       SMono.fromCallable(() => {
         mailboxManager.setRights(mailboxId, sharedWithResetUpdate.rights.toMailboxAcl.asJava, mailboxSession)
       }).`then`()
     }).getOrElse(SMono.empty)
 
-    val partialUpdatesOperation: SMono[Unit] = SFlux.fromIterable(partialUpdates)
+    val partialUpdatesOperation: SMono[Unit] = SFlux.fromIterable(validatedPatch.rightsPartialUpdates)
       .flatMap(partialUpdate => SMono.fromCallable(() => {
         mailboxManager.applyRightsCommand(mailboxId, partialUpdate.asACLCommand(), mailboxSession)
       }))
@@ -327,11 +298,11 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
       .getOrElse(Map.empty)
       .view)
       .flatMap {
-      case (mailboxCreationId: MailboxCreationId, jsObject: JsObject) =>
-        SMono.fromCallable(() => {
-          createMailbox(mailboxSession, mailboxCreationId, jsObject, processingContext)
-        }).subscribeOn(Schedulers.elastic())
-    }
+        case (mailboxCreationId: MailboxCreationId, jsObject: JsObject) =>
+          SMono.fromCallable(() => {
+            createMailbox(mailboxSession, mailboxCreationId, jsObject, processingContext)
+          }).subscribeOn(Schedulers.elastic())
+      }
       .collectSeq()
       .map(CreationResults)
   }
@@ -379,7 +350,7 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
       }
 
       mailboxCreationRequest.rights
-          .foreach(rights => mailboxManager.setRights(mailboxId, rights.toMailboxAcl.asJava, mailboxSession))
+        .foreach(rights => mailboxManager.setRights(mailboxId, rights.toMailboxAcl.asJava, mailboxSession))
 
       val quotas = quotaFactory.loadFor(mailboxSession)
         .flatMap(quotaLoader => quotaLoader.getQuotas(path))
@@ -433,10 +404,10 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
   }
 
   private def retrievePath(mailboxId: MailboxId, mailboxSession: MailboxSession): Either[Exception, MailboxPath] = try {
-      Right(mailboxManager.getMailbox(mailboxId, mailboxSession).getMailboxPath)
-    } catch {
-      case e: Exception => Left(e)
-    }
+    Right(mailboxManager.getMailbox(mailboxId, mailboxSession).getMailboxPath)
+  } catch {
+    case e: Exception => Left(e)
+  }
 
   private def createResponse(capabilities: Set[CapabilityIdentifier],
                              invocation: Invocation,
@@ -454,7 +425,7 @@ class MailboxSetMethod @Inject()(serializer: Serializer,
       updated = Some(updateResults.updated).filter(_.nonEmpty),
       notUpdated = Some(updateResults.notUpdated).filter(_.nonEmpty),
       notDestroyed = Some(deletionResults.retrieveErrors).filter(_.nonEmpty))
-    
+
     Invocation(methodName,
       Arguments(serializer.serialize(response, capabilities).as[JsObject]),
       invocation.methodCallId)


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