You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2020/10/07 04:52:31 UTC

[james-project] branch master updated (7b951c1 -> a7a6546)

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 7b951c1  JAMES-3401 Handle Deserialize for FilterCondition when passing unsupported filter option on EmailQuery/MailboxQuery
     new 08e3d1d  JAMES-3404 Implicitly handle client id resolution with JSON substitution
     new d3234f5  JAMES-3404 Demonstrate Email/query handle client id resolution for mailbox filters
     new a7a6546  JAMES-3382 Email/query limit & position integration test fix

The 3 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:
 .../contract/EmailQueryMethodContract.scala        | 230 +++++++++++++++++++--
 .../contract/MailboxSetMethodContract.scala        |  10 +-
 .../apache/james/jmap/json/MailboxSerializer.scala |   3 +-
 .../james/jmap/json/VacationSerializer.scala       |  14 +-
 .../org/apache/james/jmap/mail/MailboxGet.scala    |  28 ++-
 .../org/apache/james/jmap/mail/MailboxSet.scala    |  30 +--
 .../apache/james/jmap/mail/VacationResponse.scala  |   1 +
 .../james/jmap/method/MailboxGetMethod.scala       |  15 +-
 .../james/jmap/method/MailboxSetMethod.scala       |  46 +++--
 .../jmap/method/VacationResponseGetMethod.scala    |  13 +-
 .../james/jmap/routes/ProcessingContext.scala      |  45 ++--
 .../jmap/json/MailboxGetSerializationTest.scala    |  18 +-
 .../VacationResponseGetSerializationTest.scala     |   3 -
 13 files changed, 326 insertions(+), 130 deletions(-)


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


[james-project] 02/03: JAMES-3404 Demonstrate Email/query handle client id resolution for mailbox filters

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 d3234f5d26b272951c8f4ed74a2653d25fb4b422
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Oct 6 11:37:35 2020 +0700

    JAMES-3404 Demonstrate Email/query handle client id resolution for mailbox filters
---
 .../contract/EmailQueryMethodContract.scala        | 194 +++++++++++++++++++++
 1 file changed, 194 insertions(+)

diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index f781e5d..ea20688 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -205,6 +205,200 @@ trait EmailQueryMethodContract {
   }
 
   @Test
+  def emailQueryShouldAcceptMailboxCreationIdForInMailboxOtherThan(server: GuiceJamesServer): Unit = {
+    val request =
+      s"""{
+         |   "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+         |   "methodCalls": [
+         |       [
+         |           "Mailbox/set",
+         |           {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "create": {
+         |                    "C42": {
+         |                      "name": "myMailbox"
+         |                    }
+         |                }
+         |           },
+         |           "c1"
+         |       ], [
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter" : {
+         |        "inMailboxOtherThan": ["#C42"]
+         |      }
+         |    },
+         |    "c2"
+         |  ]]
+         |}""".stripMargin
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+      .getMailboxId("#private", BOB.asString(), "myMailbox")
+      .serialize
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |    "sessionState": "75128aab4b1b",
+         |    "methodResponses": [
+         |        [
+         |            "Mailbox/set",
+         |            {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "newState": "000001",
+         |                "created": {
+         |                    "C42": {
+         |                        "id": "${mailboxId}",
+         |                        "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
+         |                    }
+         |                }
+         |            },
+         |            "c1"
+         |        ],
+         |        [
+         |            "Email/query",
+         |            {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "queryState": "00000000",
+         |                "canCalculateChanges": false,
+         |                "ids": [
+         |
+         |                ],
+         |                "position": 0,
+         |                "limit": 256
+         |            },
+         |            "c2"
+         |        ]
+         |    ]
+         |}""".stripMargin)
+  }
+
+  @Test
+  def emailQueryShouldAcceptMailboxCreationIdForInMailbox(server: GuiceJamesServer): Unit = {
+    val request =
+      s"""{
+         |   "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ],
+         |   "methodCalls": [
+         |       [
+         |           "Mailbox/set",
+         |           {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "create": {
+         |                    "C42": {
+         |                      "name": "myMailbox"
+         |                    }
+         |                }
+         |           },
+         |           "c1"
+         |       ], [
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter" : {
+         |        "inMailbox": "#C42"
+         |      }
+         |    },
+         |    "c2"
+         |  ]]
+         |}""".stripMargin
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+      .getMailboxId("#private", BOB.asString(), "myMailbox")
+      .serialize
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |    "sessionState": "75128aab4b1b",
+         |    "methodResponses": [
+         |        [
+         |            "Mailbox/set",
+         |            {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "newState": "000001",
+         |                "created": {
+         |                    "C42": {
+         |                        "id": "${mailboxId}",
+         |                        "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
+         |                    }
+         |                }
+         |            },
+         |            "c1"
+         |        ],
+         |        [
+         |            "Email/query",
+         |            {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "queryState": "00000000",
+         |                "canCalculateChanges": false,
+         |                "ids": [
+         |
+         |                ],
+         |                "position": 0,
+         |                "limit": 256
+         |            },
+         |            "c2"
+         |        ]
+         |    ]
+         |}""".stripMargin)
+  }
+
+  @Test
   def emailInSharedMailboxesShouldNotBeDisplayedWhenNoExtension(server: GuiceJamesServer): Unit = {
     val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
     val andreInboxId = mailboxProbe.createMailbox(inbox(ANDRE))


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


[james-project] 03/03: JAMES-3382 Email/query limit & position integration test fix

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 a7a65461e41ca3d1feffe442916eda3a023e367d
Author: LanKhuat <dl...@linagora.com>
AuthorDate: Tue Oct 6 16:35:10 2020 +0700

    JAMES-3382 Email/query limit & position integration test fix
---
 .../contract/EmailQueryMethodContract.scala        | 36 ++++++++++++++--------
 1 file changed, 23 insertions(+), 13 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index ea20688..33c53b3 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -3176,33 +3176,43 @@ trait EmailQueryMethodContract {
 
   @Test
   def shouldLimitResultByLimitAndPosition(server: GuiceJamesServer): Unit = {
+    val mailboxProbe: MailboxProbeImpl = server.getProbe(classOf[MailboxProbeImpl])
+
     val message: Message = Message.Builder
       .of
       .setSubject("test")
       .setBody("testmail", StandardCharsets.UTF_8)
       .build
-    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(MailboxPath.inbox(BOB))
-    val otherMailboxPath = MailboxPath.forUser(BOB, "other")
-    val requestDate = Date.from(ZonedDateTime.now().minusDays(1).toInstant)
-    sendMessageToBobInbox(server, message, Date.from(requestDate.toInstant))
+    mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
 
-    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(otherMailboxPath)
-    val messageId2: MessageId = server.getProbe(classOf[MailboxProbeImpl])
-      .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.from(message))
+    val otherMailboxPath = MailboxPath.forUser(BOB, "other")
+    mailboxProbe.createMailbox(otherMailboxPath)
+    server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.builder()
+        .withInternalDate(Date.from(ZonedDateTime.now().minusDays(4).toInstant))
+        .build(message))
       .getMessageId
 
-    val messageId3: MessageId = server.getProbe(classOf[MailboxProbeImpl])
-      .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.from(message))
+    val messageId2: MessageId = mailboxProbe
+      .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.builder()
+        .withInternalDate(Date.from(ZonedDateTime.now().minusDays(3).toInstant))
+        .build(message))
       .getMessageId
 
-    server.getProbe(classOf[MailboxProbeImpl])
-      .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.from(message))
+    val messageId3: MessageId = mailboxProbe
+      .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.builder()
+        .withInternalDate(Date.from(ZonedDateTime.now().minusDays(2).toInstant))
+        .build(message))
       .getMessageId
 
-    server.getProbe(classOf[MailboxProbeImpl])
-      .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.from(message))
+    mailboxProbe
+      .appendMessage(BOB.asString, otherMailboxPath, AppendCommand.builder()
+        .withInternalDate(Date.from(ZonedDateTime.now().minusDays(1).toInstant))
+        .build(message))
       .getMessageId
 
+    sendMessageToBobInbox(server, message, Date.from(Date.from(ZonedDateTime.now().toInstant).toInstant))
+
     val request =
       s"""{
          |  "using": [


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


[james-project] 01/03: JAMES-3404 Implicitly handle client id resolution with JSON substitution

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 08e3d1dec0f1ae74b3621d3aea9b174db1661596
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Oct 6 11:28:34 2020 +0700

    JAMES-3404 Implicitly handle client id resolution with JSON substitution
    
    This empowers implicit handling, making it easier to implement client Id resolution
---
 .../contract/MailboxSetMethodContract.scala        | 10 ++---
 .../apache/james/jmap/json/MailboxSerializer.scala |  3 +-
 .../james/jmap/json/VacationSerializer.scala       | 14 ++++---
 .../org/apache/james/jmap/mail/MailboxGet.scala    | 28 ++++++++++++-
 .../org/apache/james/jmap/mail/MailboxSet.scala    | 30 +++++---------
 .../apache/james/jmap/mail/VacationResponse.scala  |  1 +
 .../james/jmap/method/MailboxGetMethod.scala       | 15 ++++---
 .../james/jmap/method/MailboxSetMethod.scala       | 46 ++++++++++++----------
 .../jmap/method/VacationResponseGetMethod.scala    | 13 +++---
 .../james/jmap/routes/ProcessingContext.scala      | 45 +++++++++------------
 .../jmap/json/MailboxGetSerializationTest.scala    | 18 +--------
 .../VacationResponseGetSerializationTest.scala     |  3 --
 12 files changed, 109 insertions(+), 117 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 413aadb..c81aef5 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
@@ -2807,7 +2807,7 @@ trait MailboxSetMethodContract {
          |      "notDestroyed": {
          |        "#C42": {
          |          "type": "invalidArguments",
-         |          "description": "#C42 is not a mailboxId: ClientId(#C42) was not used in previously defined creationIds"
+         |          "description": "#C42 is not a mailboxId: #C42 was not used in previously defined creationIds"
          |        }
          |      }
          |    }, "c2"],
@@ -2881,7 +2881,7 @@ trait MailboxSetMethodContract {
          |      "notDestroyed": {
          |        "#C42": {
          |          "type": "invalidArguments",
-         |          "description": "#C42 is not a mailboxId: ClientId(#C42) was not used in previously defined creationIds"
+         |          "description": "#C42 is not a mailboxId: #C42 was not used in previously defined creationIds"
          |        }
          |      }
          |    }, "c2"]
@@ -2919,7 +2919,7 @@ trait MailboxSetMethodContract {
       .body
       .asString
 
-    val message = "# is not a mailboxId: Left predicate of ((!(0 < 1) && !(0 > 255)) && \\\"\\\".matches(\\\"^[#a-zA-Z0-9-_]*$\\\")) failed: Predicate taking size() = 0 failed: Left predicate of (!(0 < 1) && !(0 > 255)) failed: Predicate (0 < 1) did not fail."
+    val message = "# is not a mailboxId: # was not used in previously defined creationIds"
     assertThatJson(response).isEqualTo(
       s"""{
          |  "sessionState": "75128aab4b1b",
@@ -6728,7 +6728,7 @@ trait MailboxSetMethodContract {
          |      "notUpdated": {
          |        "${mailboxId.serialize}": {
          |          "type": "invalidArguments",
-         |          "description": "ClientId(#C42) was not used in previously defined creationIds",
+         |          "description": "#C42 was not used in previously defined creationIds",
          |          "properties": ["parentId"]
          |        }
          |      }
@@ -7357,7 +7357,7 @@ trait MailboxSetMethodContract {
          |                "notUpdated": {
          |                    "#invalid": {
          |                        "type": "invalidArguments",
-         |                        "description": "ClientId(#invalid) was not used in previously defined creationIds"
+         |                        "description": "#invalid was not used in previously defined creationIds"
          |                    }
          |                }
          |            },
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
index d8e4467..edb6d11 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/MailboxSerializer.scala
@@ -23,7 +23,8 @@ import eu.timepit.refined._
 import eu.timepit.refined.collection.NonEmpty
 import javax.inject.Inject
 import org.apache.james.core.{Domain, Username}
-import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, UnparsedMailboxId, UnparsedMailboxIdConstraint}
+import org.apache.james.jmap.mail.MailboxGet.{UnparsedMailboxId, UnparsedMailboxIdConstraint}
+import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
 import org.apache.james.jmap.mail.{DelegatedNamespace, Ids, IsSubscribed, Mailbox, MailboxCreationRequest, MailboxCreationResponse, MailboxGetRequest, MailboxGetResponse, MailboxNamespace, MailboxPatchObject, MailboxRights, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, MayAddItems, MayCreateChild, MayDelete, MayReadItems, MayRemoveItems, MayRename, MaySetKeywords, MaySetSeen, MaySubmit, NotFound, PersonalNamespace, Quota, QuotaId, QuotaRoot, Quotas, RemoveEmailsOnDestroy, [...]
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model._
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
index 70f61b6..38c830c 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/VacationSerializer.scala
@@ -21,9 +21,8 @@ package org.apache.james.jmap.json
 
 import java.time.format.DateTimeFormatter
 
-import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
 import org.apache.james.jmap.mail.VacationResponse.{UnparsedVacationResponseId, VACATION_RESPONSE_ID}
-import org.apache.james.jmap.mail.{FromDate, HtmlBody, IsEnabled, NotFound, Subject, TextBody, ToDate, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseId, VacationResponseIds, VacationResponseNotFound}
+import org.apache.james.jmap.mail.{FromDate, HtmlBody, IsEnabled, Subject, TextBody, ToDate, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseId, VacationResponseIds, VacationResponseNotFound}
 import org.apache.james.jmap.model._
 import org.apache.james.jmap.vacation.{VacationResponsePatchObject, VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse}
 import play.api.libs.json._
@@ -38,9 +37,6 @@ object VacationSerializer {
   }
   private implicit val vacationResponseSetRequestReads: Reads[VacationResponseSetRequest] = Json.reads[VacationResponseSetRequest]
 
-  private implicit def notFoundWrites(implicit mailboxIdWrites: Writes[UnparsedMailboxId]): Writes[NotFound] =
-    notFound => JsArray(notFound.value.toList.map(mailboxIdWrites.writes))
-
   private implicit val vacationResponseSetUpdateResponseWrites: Writes[VacationResponseUpdateResponse] = Json.valueWrites[VacationResponseUpdateResponse]
 
   private implicit val vacationResponseSetErrorWrites: Writes[VacationResponseSetError] = Json.writes[VacationResponseSetError]
@@ -51,6 +47,11 @@ object VacationSerializer {
     utcDate => JsString(utcDate.asUTC.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssX")))
 
   private implicit val vacationResponseIdWrites: Writes[VacationResponseId] = _ => JsString(VACATION_RESPONSE_ID.value)
+  private implicit val vacationResponseIdReads: Reads[VacationResponseId] = {
+    case JsString("singleton") => JsSuccess(VacationResponseId())
+    case JsString(_) => JsError("Only singleton is supported as a VacationResponseId")
+    case _ => JsError("Expecting JsString(singleton) to represent a VacationResponseId")
+  }
   private implicit val isEnabledWrites: Writes[IsEnabled] = Json.valueWrites[IsEnabled]
   private implicit val fromDateWrites: Writes[FromDate] = Json.valueWrites[FromDate]
   private implicit val toDateWrites: Writes[ToDate] = Json.valueWrites[ToDate]
@@ -61,7 +62,8 @@ object VacationSerializer {
   implicit def vacationResponseWrites(properties: Properties): Writes[VacationResponse] = Json.writes[VacationResponse]
     .transform(properties.filter(_))
 
-  private implicit val vacationResponseIdReads: Reads[VacationResponseIds] = Json.valueReads[VacationResponseIds]
+  private implicit val vacationResponseIdsReads: Reads[VacationResponseIds] = Json.valueReads[VacationResponseIds]
+
   private implicit val vacationResponseGetRequest: Reads[VacationResponseGetRequest] = Json.reads[VacationResponseGetRequest]
 
   private implicit def vacationResponseNotFoundWrites(implicit idWrites: Writes[UnparsedVacationResponseId]): Writes[VacationResponseNotFound] =
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
index b230a77..bd7b8ba 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
@@ -19,10 +19,36 @@
 
 package org.apache.james.jmap.mail
 
-import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
+import eu.timepit.refined
+import eu.timepit.refined.api.Refined
+import eu.timepit.refined.collection.NonEmpty
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.method.WithAccountId
 import org.apache.james.jmap.model.State.State
 import org.apache.james.jmap.model.{AccountId, Properties}
+import org.apache.james.mailbox.model.MailboxId
+
+import scala.util.{Failure, Try}
+
+object MailboxGet {
+  type UnparsedMailboxIdConstraint = NonEmpty
+  type UnparsedMailboxId = String Refined UnparsedMailboxIdConstraint
+
+  def asUnparsed(mailboxId: MailboxId): UnparsedMailboxId = refined.refineV[UnparsedMailboxIdConstraint](mailboxId.serialize()) match {
+    case Left(e) => throw new IllegalArgumentException(e)
+    case scala.Right(value) => value
+  }
+
+  def parse(mailboxIdFactory: MailboxId.Factory)(unparsed: UnparsedMailboxId): Try[MailboxId] =
+    parseString(mailboxIdFactory)(unparsed.value)
+
+  def parseString(mailboxIdFactory: MailboxId.Factory)(unparsed: String): Try[MailboxId] =
+    unparsed match {
+      case a if a.startsWith("#") =>
+        Failure(new IllegalArgumentException(s"$unparsed was not used in previously defined creationIds"))
+      case _ => Try(mailboxIdFactory.fromString(unparsed))
+    }
+}
 
 case class Ids(value: List[UnparsedMailboxId])
 
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 cbd034e..20801bd 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxSet.scala
@@ -19,7 +19,6 @@
 
 package org.apache.james.jmap.mail
 
-import eu.timepit.refined
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.auto._
 import eu.timepit.refined.collection.NonEmpty
@@ -27,15 +26,15 @@ import eu.timepit.refined.refineV
 import eu.timepit.refined.types.string.NonEmptyString
 import org.apache.james.core.Username
 import org.apache.james.jmap.json.MailboxSerializer
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.mail.MailboxName.MailboxName
 import org.apache.james.jmap.mail.MailboxPatchObject.MailboxPatchObjectKey
-import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, UnparsedMailboxId, UnparsedMailboxIdConstraint}
+import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
 import org.apache.james.jmap.method.{MailboxCreationParseException, WithAccountId}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.SetError.{SetErrorDescription, SetErrorType}
 import org.apache.james.jmap.model.State.State
 import org.apache.james.jmap.model.{AccountId, CapabilityIdentifier, Properties, SetError}
-import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.mailbox.model.{MailboxId, MailboxACL => JavaMailboxACL}
 import org.apache.james.mailbox.{MailboxSession, Role}
 import play.api.libs.json.{JsBoolean, JsError, JsNull, JsObject, JsString, JsSuccess, JsValue}
@@ -48,14 +47,7 @@ case class MailboxSetRequest(accountId: AccountId,
                              onDestroyRemoveEmails: Option[RemoveEmailsOnDestroy]) extends WithAccountId
 
 object MailboxSetRequest {
-  type UnparsedMailboxIdConstraint = NonEmpty
   type MailboxCreationId = String Refined NonEmpty
-  type UnparsedMailboxId = String Refined UnparsedMailboxIdConstraint
-
-  def asUnparsed(mailboxId: MailboxId): UnparsedMailboxId = refined.refineV[UnparsedMailboxIdConstraint](mailboxId.serialize()) match {
-    case Left(e) => throw new IllegalArgumentException(e)
-    case scala.Right(value) => value
-  }
 }
 
 case class RemoveEmailsOnDestroy(value: Boolean) extends AnyVal
@@ -114,12 +106,11 @@ object MailboxPatchObject {
 }
 
 case class MailboxPatchObject(value: Map[String, JsValue]) {
-  def validate(processingContext: ProcessingContext,
-               mailboxIdFactory: MailboxId.Factory,
+  def validate(mailboxIdFactory: MailboxId.Factory,
                serializer: MailboxSerializer,
                capabilities: Set[CapabilityIdentifier],
                mailboxSession: MailboxSession): Either[PatchUpdateValidationException, ValidatedMailboxPatchObject] = {
-    val asUpdatedIterable = updates(serializer, capabilities, processingContext, mailboxIdFactory, mailboxSession)
+    val asUpdatedIterable = updates(serializer, capabilities, mailboxIdFactory, mailboxSession)
 
     val maybeParseException: Option[PatchUpdateValidationException] = asUpdatedIterable
       .flatMap(x => x match {
@@ -174,12 +165,11 @@ case class MailboxPatchObject(value: Map[String, JsValue]) {
 
   def updates(serializer: MailboxSerializer,
               capabilities: Set[CapabilityIdentifier],
-              processingContext: ProcessingContext,
               mailboxIdFactory: MailboxId.Factory,
               mailboxSession: MailboxSession): Iterable[Either[PatchUpdateValidationException, Update]] = value.map({
     case (property, newValue) => property match {
       case "name" => NameUpdate.parse(newValue, mailboxSession)
-      case "parentId" => ParentIdUpdate.parse(newValue, processingContext, mailboxIdFactory)
+      case "parentId" => ParentIdUpdate.parse(newValue, mailboxIdFactory)
       case "sharedWith" => SharedWithResetUpdate.parse(serializer, capabilities)(newValue)
       case "role" => Left(ServerSetPropertyException(MailboxPatchObject.roleProperty))
       case "sortOrder" => Left(ServerSetPropertyException(MailboxPatchObject.sortOrderProperty))
@@ -331,15 +321,13 @@ object SharedWithPartialUpdate {
 }
 
 object ParentIdUpdate {
-  def parse(newValue: JsValue, processingContext: ProcessingContext, mailboxIdFactory: MailboxId.Factory):
+  def parse(newValue: JsValue, mailboxIdFactory: MailboxId.Factory):
     Either[PatchUpdateValidationException, Update] =
       (newValue match {
         case JsString(id) =>
-          for {
-            unparsedMailboxId <- refineV[UnparsedMailboxIdConstraint](id)
-            mailboxId <- processingContext.resolveMailboxId(unparsedMailboxId, mailboxIdFactory).left.map(_.getMessage)
-          } yield changeParentTo(mailboxId)
-
+          MailboxGet.parseString(mailboxIdFactory)(id)
+            .fold(e => Left(e.getMessage),
+              mailboxId => scala.Right(changeParentTo(mailboxId)))
         case JsNull => scala.Right(removeParent())
 
         case _ => Left("Expecting a JSON string or null as an argument")
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala
index db02ec3..d25c603 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/VacationResponse.scala
@@ -38,6 +38,7 @@ case class HtmlBody(value: String)
 
 object VacationResponse {
   val VACATION_RESPONSE_ID: Id = "singleton"
+  val UNPARSED_SINGLETON: UnparsedVacationResponseId = "singleton"
 
   type UnparsedVacationResponseId = String Refined NonEmpty
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
index 15c01c9..154da66 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxGetMethod.scala
@@ -23,14 +23,13 @@ import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.mail._
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY}
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.State.INSTANCE
 import org.apache.james.jmap.model.{AccountId, Capabilities, CapabilityIdentifier, ErrorCode, Invocation, MailboxFactory, Properties, Subscriptions}
-import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.jmap.utils.quotas.{QuotaLoader, QuotaLoaderWithPreloadedDefaultFactory}
 import org.apache.james.mailbox.exception.MailboxNotFoundException
 import org.apache.james.mailbox.model.search.MailboxQuery
@@ -42,6 +41,7 @@ import reactor.core.scala.publisher.{SFlux, SMono}
 import reactor.core.scheduler.Schedulers
 
 import scala.jdk.CollectionConverters._
+import scala.util.Try
 
 class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
                                   mailboxManager: MailboxManager,
@@ -59,7 +59,7 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
     def empty(): MailboxGetResults = MailboxGetResults(Set.empty, NotFound(Set.empty))
     def found(mailbox: Mailbox): MailboxGetResults = MailboxGetResults(Set(mailbox), NotFound(Set.empty))
     def notFound(mailboxId: UnparsedMailboxId): MailboxGetResults = MailboxGetResults(Set.empty, NotFound(Set(mailboxId)))
-    def notFound(mailboxId: MailboxId): MailboxGetResults = MailboxGetResults(Set.empty, NotFound(Set(MailboxSetRequest.asUnparsed(mailboxId))))
+    def notFound(mailboxId: MailboxId): MailboxGetResults = MailboxGetResults(Set.empty, NotFound(Set(MailboxGet.asUnparsed(mailboxId))))
   }
 
   case class MailboxGetResults(mailboxes: Set[Mailbox], notFound: NotFound) {
@@ -75,7 +75,7 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
   override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxGetRequest): SMono[InvocationWithContext] = {
     val requestedProperties: Properties = request.properties.getOrElse(Mailbox.allProperties)
     (requestedProperties -- Mailbox.allProperties match {
-      case invalidProperties if invalidProperties.isEmpty() => getMailboxes(capabilities, request, invocation.processingContext, mailboxSession)
+      case invalidProperties if invalidProperties.isEmpty() => getMailboxes(capabilities, request, mailboxSession)
         .reduce(MailboxGetResults.empty(), MailboxGetResults.merge)
         .map(mailboxes => mailboxes.asResponse(request.accountId))
         .map(mailboxGetResponse => Invocation(
@@ -101,16 +101,15 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
 
   private def getMailboxes(capabilities: Set[CapabilityIdentifier],
                            mailboxGetRequest: MailboxGetRequest,
-                           processingContext: ProcessingContext,
                            mailboxSession: MailboxSession): SFlux[MailboxGetResults] =
 
     mailboxGetRequest.ids match {
       case None => getAllMailboxes(capabilities, mailboxSession)
         .map(MailboxGetResults.found)
       case Some(ids) => SFlux.fromIterable(ids.value)
-          .flatMap(id => processingContext.resolveMailboxId(id, mailboxIdFactory)
-            .fold(e => SMono.just(MailboxGetResults.notFound(id)),
-              mailboxId => getMailboxResultById(capabilities, mailboxId, mailboxSession)))
+        .flatMap(id => Try(mailboxIdFactory.fromString(id.value))
+          .fold(e => SMono.just(MailboxGetResults.notFound(id)),
+            mailboxId => getMailboxResultById(capabilities, mailboxId, mailboxSession)))
     }
 
   private def getMailboxResultById(capabilities: Set[CapabilityIdentifier],
diff --git a/server/protocols/jmap-rfc-8621/src/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 5c9a50a..6a40581 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,8 +23,9 @@ import eu.timepit.refined.auto._
 import javax.inject.Inject
 import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.MailboxSetRequest.{MailboxCreationId, UnparsedMailboxId}
-import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, IsSubscribed, MailboxCreationRequest, MailboxCreationResponse, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, RemoveEmailsOnDestroy, ServerSetPropertyException, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, UnsupportedPropertyUpdatedException, ValidatedMailboxPatchObject}
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
+import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
+import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, IsSubscribed, MailboxCreationRequest, MailboxCreationResponse, MailboxGet, MailboxPatchObject, MailboxRights, MailboxSetError, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, RemoveEmailsOnDestroy, ServerSetPropertyException, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, UnsupportedPropertyUpdatedException, ValidatedMai [...]
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY}
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
@@ -42,6 +43,7 @@ import reactor.core.scala.publisher.{SFlux, SMono}
 import reactor.core.scheduler.Schedulers
 
 import scala.jdk.CollectionConverters._
+import scala.util.Try
 
 case class MailboxHasMailException(mailboxId: MailboxId) extends Exception
 case class SystemMailboxChangeException(mailboxId: MailboxId) extends Exception
@@ -155,7 +157,7 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
 
   override def doProcess(capabilities: Set[CapabilityIdentifier], invocation: InvocationWithContext, mailboxSession: MailboxSession, request: MailboxSetRequest): SMono[InvocationWithContext] = for {
     creationResultsWithUpdatedProcessingContext <- createMailboxes(mailboxSession, request, invocation.processingContext)
-    deletionResults <- deleteMailboxes(mailboxSession, request, invocation.processingContext)
+    deletionResults <- deleteMailboxes(mailboxSession, request)
     updateResults <- updateMailboxes(mailboxSession, request, invocation.processingContext, capabilities)
   } yield InvocationWithContext(createResponse(capabilities, invocation.invocation, request, creationResultsWithUpdatedProcessingContext._1, deletionResults, updateResults), creationResultsWithUpdatedProcessingContext._2)
 
@@ -168,9 +170,10 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
     SFlux.fromIterable(mailboxSetRequest.update.getOrElse(Seq()))
       .flatMap({
         case (unparsedMailboxId: UnparsedMailboxId, patch: MailboxPatchObject) =>
-          processingContext.resolveMailboxId(unparsedMailboxId, mailboxIdFactory).fold(
+          MailboxGet.parse(mailboxIdFactory)(unparsedMailboxId)
+            .fold(
               e => SMono.just(UpdateFailure(unparsedMailboxId, e, None)),
-              mailboxId => updateMailbox(mailboxSession, processingContext, mailboxId, unparsedMailboxId, patch, capabilities))
+              mailboxId => updateMailbox(mailboxSession, mailboxId, unparsedMailboxId, patch, capabilities))
             .onErrorResume(e => SMono.just(UpdateFailure(unparsedMailboxId, e, None)))
       })
       .collectSeq()
@@ -178,12 +181,11 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
   }
 
   private def updateMailbox(mailboxSession: MailboxSession,
-                            processingContext: ProcessingContext,
                             mailboxId: MailboxId,
                             unparsedMailboxId: UnparsedMailboxId,
                             patch: MailboxPatchObject,
                             capabilities: Set[CapabilityIdentifier]): SMono[UpdateResult] = {
-    patch.validate(processingContext, mailboxIdFactory, serializer, capabilities, mailboxSession)
+    patch.validate(mailboxIdFactory, serializer, capabilities, mailboxSession)
       .fold(e => SMono.raiseError(e), validatedPatch =>
         updateMailboxRights(mailboxId, validatedPatch, mailboxSession)
           .`then`(updateSubscription(mailboxId, validatedPatch, mailboxSession))
@@ -299,24 +301,24 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
   }
 
 
-  private def deleteMailboxes(mailboxSession: MailboxSession, mailboxSetRequest: MailboxSetRequest, processingContext: ProcessingContext): SMono[DeletionResults] = {
+  private def deleteMailboxes(mailboxSession: MailboxSession, mailboxSetRequest: MailboxSetRequest): SMono[DeletionResults] = {
     SFlux.fromIterable(mailboxSetRequest.destroy.getOrElse(Seq()))
-      .flatMap(id => delete(mailboxSession, processingContext, id, mailboxSetRequest.onDestroyRemoveEmails.getOrElse(RemoveEmailsOnDestroy(false)))
+      .flatMap(id => delete(mailboxSession, id, mailboxSetRequest.onDestroyRemoveEmails.getOrElse(RemoveEmailsOnDestroy(false)))
         .onErrorRecover(e => DeletionFailure(id, e)))
       .collectSeq()
       .map(DeletionResults)
   }
 
-  private def delete(mailboxSession: MailboxSession, processingContext: ProcessingContext, id: UnparsedMailboxId, onDestroy: RemoveEmailsOnDestroy): SMono[DeletionResult] = {
-    processingContext.resolveMailboxId(id, mailboxIdFactory) match {
-      case Right(mailboxId) => SMono.fromCallable(() => delete(mailboxSession, mailboxId, onDestroy))
-        .subscribeOn(Schedulers.elastic())
-        .`then`(SMono.just[DeletionResult](DeletionSuccess(mailboxId)))
-      case Left(e) => SMono.raiseError(e)
-    }
+  private def delete(mailboxSession: MailboxSession, id: UnparsedMailboxId, onDestroy: RemoveEmailsOnDestroy): SMono[DeletionResult] = {
+    MailboxGet.parse(mailboxIdFactory)(id)
+        .fold(e => SMono.raiseError(e),
+          id => SMono.fromCallable(() => doDelete(mailboxSession, id, onDestroy))
+            .subscribeOn(Schedulers.elastic())
+            .`then`(SMono.just[DeletionResult](DeletionSuccess(id))))
+
   }
 
-  private def delete(mailboxSession: MailboxSession, id: MailboxId, onDestroy: RemoveEmailsOnDestroy): Unit = {
+  private def doDelete(mailboxSession: MailboxSession, id: MailboxId, onDestroy: RemoveEmailsOnDestroy): Unit = {
     val mailbox = mailboxManager.getMailbox(id, mailboxSession)
 
     if (isASystemMailbox(mailbox)) {
@@ -363,7 +365,7 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
                             jsObject: JsObject,
                             processingContext: ProcessingContext): (CreationResult, ProcessingContext) = {
     parseCreate(jsObject)
-      .flatMap(mailboxCreationRequest => resolvePath(mailboxSession, mailboxCreationRequest, processingContext)
+      .flatMap(mailboxCreationRequest => resolvePath(mailboxSession, mailboxCreationRequest)
         .flatMap(path => createMailbox(mailboxSession = mailboxSession,
           path = path,
           mailboxCreationRequest = mailboxCreationRequest)))
@@ -441,14 +443,16 @@ class MailboxSetMethod @Inject()(serializer: MailboxSerializer,
   }
 
   private def resolvePath(mailboxSession: MailboxSession,
-                          mailboxCreationRequest: MailboxCreationRequest,
-                          processingContext: ProcessingContext): Either[Exception, MailboxPath] = {
+                          mailboxCreationRequest: MailboxCreationRequest): Either[Exception, MailboxPath] = {
     if (mailboxCreationRequest.name.value.contains(mailboxSession.getPathDelimiter)) {
       return Left(new MailboxNameException(s"The mailbox '${mailboxCreationRequest.name.value}' contains an illegal character: '${mailboxSession.getPathDelimiter}'"))
     }
     mailboxCreationRequest.parentId
       .map(maybeParentId => for {
-        parentId <- processingContext.resolveMailboxId(maybeParentId, mailboxIdFactory)
+        parentId <- Try(mailboxIdFactory.fromString(maybeParentId.value))
+          .toEither
+          .left
+          .map(e => new IllegalArgumentException(e.getMessage, e))
         parentPath <- retrievePath(parentId, mailboxSession)
       } yield {
         parentPath.child(mailboxCreationRequest.name, mailboxSession.getPathDelimiter)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
index 2978afd..ebbc939 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/VacationResponseGetMethod.scala
@@ -24,14 +24,13 @@ import javax.inject.Inject
 import org.apache.james.jmap.api.vacation.{VacationRepository, AccountId => JavaAccountId}
 import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
-import org.apache.james.jmap.mail.VacationResponse.UnparsedVacationResponseId
+import org.apache.james.jmap.mail.VacationResponse.{UNPARSED_SINGLETON, UnparsedVacationResponseId}
 import org.apache.james.jmap.mail.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseNotFound}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY, VACATION_RESPONSE_CAPABILITY}
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
 import org.apache.james.jmap.model.State.INSTANCE
 import org.apache.james.jmap.model.{AccountId, Capabilities, ErrorCode, Invocation, MissingCapabilityException, Properties}
-import org.apache.james.jmap.routes.ProcessingContext
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import play.api.libs.json.{JsError, JsObject, JsSuccess}
@@ -67,7 +66,7 @@ class VacationResponseGetMethod @Inject()(vacationRepository: VacationRepository
     {
       val requestedProperties: Properties = request.properties.getOrElse(VacationResponse.allProperties)
       (requestedProperties -- VacationResponse.allProperties match {
-        case invalidProperties if invalidProperties.isEmpty() => getVacationResponse(request, invocation.processingContext, mailboxSession)
+        case invalidProperties if invalidProperties.isEmpty() => getVacationResponse(request, mailboxSession)
           .reduce(VacationResponseGetResult.empty, VacationResponseGetResult.merge)
           .map(vacationResult => vacationResult.asResponse(request.accountId))
           .map(vacationResponseGetResponse => Invocation(
@@ -97,16 +96,16 @@ class VacationResponseGetMethod @Inject()(vacationRepository: VacationRepository
   }
 
   private def getVacationResponse(vacationResponseGetRequest: VacationResponseGetRequest,
-                                  processingContext: ProcessingContext,
                                   mailboxSession: MailboxSession): SFlux[VacationResponseGetResult] =
     vacationResponseGetRequest.ids match {
       case None => getVacationSingleton(mailboxSession)
         .map(VacationResponseGetResult.found)
         .flux()
       case Some(ids) => SFlux.fromIterable(ids.value)
-        .flatMap(id => processingContext.resolveVacationResponseId(id)
-          .fold(_ => SMono.just(VacationResponseGetResult.notFound(id)),
-            _ => getVacationSingleton(mailboxSession).map(VacationResponseGetResult.found)))
+        .flatMap(id => id match {
+          case UNPARSED_SINGLETON => getVacationSingleton(mailboxSession).map(VacationResponseGetResult.found)
+          case _ => SMono.just(VacationResponseGetResult.notFound(id))
+        })
     }
 
   private def getVacationSingleton(mailboxSession: MailboxSession): SMono[VacationResponse] = {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala
index 8506f2f..a814df4 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/ProcessingContext.scala
@@ -23,13 +23,9 @@ import eu.timepit.refined.numeric.NonNegative
 import eu.timepit.refined.refineV
 import eu.timepit.refined.types.numeric.NonNegInt
 import org.apache.james.jmap.json.BackReferenceDeserializer
-import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
-import org.apache.james.jmap.mail.VacationResponse.{UnparsedVacationResponseId, VACATION_RESPONSE_ID}
-import org.apache.james.jmap.model.Id.Id
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
 import org.apache.james.jmap.model.{ClientId, Id, Invocation, ServerId}
-import org.apache.james.mailbox.model.MailboxId
-import play.api.libs.json.{JsArray, JsError, JsObject, JsResult, JsSuccess, JsValue, Reads}
+import play.api.libs.json.{JsArray, JsError, JsObject, JsResult, JsString, JsSuccess, JsValue, Reads}
 
 import scala.util.Try
 
@@ -115,7 +111,7 @@ case class JsonPath(parts: List[JsonPathPart]) {
       }
   }
 
-  private def readWildcard(jsValue: JsValue) = jsValue match {
+  private def readWildcard(jsValue: JsValue): JsResult[JsValue] = jsValue match {
     case JsArray(arrayItems) =>
       val evaluationResults: List[JsResult[JsValue]] = arrayItems.toList.map(evaluate)
 
@@ -146,8 +142,7 @@ case class InvalidResultReferenceException(message: String) extends IllegalArgum
 
 case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], private val invocations: Map[MethodCallId, Invocation]) {
 
- def recordCreatedId(clientId: ClientId, serverId: ServerId): ProcessingContext = ProcessingContext(creationIds + (clientId -> serverId), invocations)
- private def retrieveServerId(clientId: ClientId): Option[ServerId] = creationIds.get(clientId)
+  def recordCreatedId(clientId: ClientId, serverId: ServerId): ProcessingContext = ProcessingContext(creationIds + (clientId -> serverId), invocations)
 
   def recordInvocation(invocation: Invocation): ProcessingContext = ProcessingContext(creationIds, invocations + (invocation.methodCallId -> invocation))
 
@@ -163,6 +158,9 @@ case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], p
   private def backReferenceResolver(): Reads[JsValue] = {
     case JsArray(value) => resolveBackReferences(value)
     case JsObject(underlying) => resolveBackReference(underlying)
+    case JsString(value) if value.startsWith("#") => resolveCreationId(value)
+        .fold(_ => JsSuccess(JsString(value)),
+          serverId => JsSuccess(JsString(serverId.value.value)))
     case others: JsValue => JsSuccess(others)
   }
 
@@ -174,7 +172,7 @@ case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], p
   }
 
   private def resolveBackReference(underlying: collection.Map[String, JsValue]): JsResult[JsObject] = {
-    val resolutions = underlying.map(resolveBackReference)
+    val resolutions = underlying.map(resolveBackReference(_))
 
     val firstError = resolutions.flatMap({
       case Left(jsError) => Some(jsError)
@@ -196,7 +194,12 @@ case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], p
       BackReferenceDeserializer.deserializeBackReference(entry._2) match {
         case JsSuccess(backReference, _) => resolveBackReference(newEntry, backReference)
         // If the JSON object is not a back-reference continue parsing (it could be a creationId)
-        case JsError(_) => propagateBackReferenceResolution(entry)
+        case JsError(_) =>
+          backReferenceResolver().reads(entry._2)
+            .fold(e => Left(JsError(e)),
+              json => resolveCreationId(entry._1)
+                .fold(_ => Right((entry._1, json)),
+                  serverId => Right((serverId.value.value, json))))
       }
     } else {
       propagateBackReferenceResolution(entry)
@@ -225,17 +228,9 @@ case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], p
     .map(backReference.resolve)
     .getOrElse(JsError("Back reference could not be resolved"))
 
- def resolveMailboxId(unparsedMailboxId: UnparsedMailboxId, mailboxIdFactory: MailboxId.Factory): Either[IllegalArgumentException, MailboxId] =
-  Id.validate(unparsedMailboxId.value)
-   .flatMap(id => resolveServerId(ClientId(id)))
-   .flatMap(serverId => parseMailboxId(mailboxIdFactory, serverId))
-
- private def parseMailboxId(mailboxIdFactory: MailboxId.Factory, serverId: ServerId) =
-  try {
-   Right(mailboxIdFactory.fromString(serverId.value.value))
-  } catch {
-   case e: IllegalArgumentException => Left(e)
-  }
+  private def resolveCreationId(creationId: String): Either[IllegalArgumentException, ServerId] =
+    Id.validate(creationId)
+      .flatMap(id => resolveServerId(ClientId(id)))
 
  private def resolveServerId(id: ClientId): Either[IllegalArgumentException, ServerId] =
   id.retrieveOriginalClientId
@@ -244,10 +239,6 @@ case class ProcessingContext(private val creationIds: Map[ClientId, ServerId], p
       .getOrElse(Left[IllegalArgumentException, ServerId](new IllegalArgumentException(s"$id was not used in previously defined creationIds")))))
     .getOrElse(Right(ServerId(id.value)))
 
- def resolveVacationResponseId(unparsedVacationId: UnparsedVacationResponseId): Either[IllegalArgumentException, Id] =
-  if (unparsedVacationId.equals(VACATION_RESPONSE_ID)) {
-   Right(VACATION_RESPONSE_ID)
-  } else {
-   Left(new IllegalArgumentException(s"$unparsedVacationId is not a valid VacationResponse ID"))
-  }
+  private def retrieveServerId(clientId: ClientId): Option[ServerId] = creationIds.get(clientId)
+
 }
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
index de24595..c2841fc 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxGetSerializationTest.scala
@@ -24,7 +24,7 @@ import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
 import org.apache.james.jmap.json.Fixture._
 import org.apache.james.jmap.json.MailboxGetSerializationTest._
 import org.apache.james.jmap.json.MailboxSerializationTest.MAILBOX
-import org.apache.james.jmap.mail.MailboxSetRequest.UnparsedMailboxId
+import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.mail._
 import org.apache.james.jmap.model.{AccountId, Properties}
 import org.apache.james.mailbox.model.{MailboxId, TestId}
@@ -47,22 +47,6 @@ object MailboxGetSerializationTest {
 
 class MailboxGetSerializationTest extends AnyWordSpec with Matchers {
   "Deserialize MailboxGetRequest" should {
-    "succeed on invalid mailboxId" in {
-      // as they are unparsed
-      val expectedRequestObject = MailboxGetRequest(
-        accountId = ACCOUNT_ID,
-        ids = Some(Ids(List("ab#?"))),
-        properties = None)
-
-      SERIALIZER.deserializeMailboxGetRequest(
-        """
-          |{
-          |  "accountId": "aHR0cHM6Ly93d3cuYmFzZTY0ZW5jb2RlLm9yZy8",
-          |  "ids": ["ab#?"]
-          |}
-          |""".stripMargin) should equal(JsSuccess(expectedRequestObject))
-    }
-
     "succeed when properties are missing" in {
       val expectedRequestObject = MailboxGetRequest(
         accountId = ACCOUNT_ID,
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala
index e3028aa..35a0e4e 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/VacationResponseGetSerializationTest.scala
@@ -27,14 +27,11 @@ import org.apache.james.jmap.json.VacationResponseSerializationTest.VACATION_RES
 import org.apache.james.jmap.mail.VacationResponse.UnparsedVacationResponseId
 import org.apache.james.jmap.mail.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseIds, VacationResponseNotFound}
 import org.apache.james.jmap.model.{AccountId, Properties}
-import org.apache.james.mailbox.model.{MailboxId, TestId}
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 import play.api.libs.json.{JsSuccess, Json}
 
 object VacationResponseGetSerializationTest {
-  private val FACTORY: MailboxId.Factory = new TestId.Factory
-
   private val ACCOUNT_ID: AccountId = AccountId(id)
 
   private val SINGLETON_ID: UnparsedVacationResponseId = "singleton"


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