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 2021/04/12 03:20:43 UTC

[james-project] 02/02: [REFACTORING] JMAP: Wrap Id in case classes

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 37da764783e727505c71fef911d87201c60aa06d
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Apr 6 11:25:35 2021 +0700

    [REFACTORING] JMAP: Wrap Id in case classes
    
     - Brings types safety as different kinds of ids can no longer be
     mixed.
     - Allow to put more context upon JSON deserialization in addition to
     refined constraints
---
 .../rfc8621/contract/EmailSetMethodContract.scala  |  4 +--
 .../contract/MailboxSetMethodContract.scala        |  2 +-
 .../james/jmap/json/EmailGetSerializer.scala       | 12 +++++++-
 .../james/jmap/json/EmailSetSerializer.scala       | 35 +++++++++++++++++-----
 .../jmap/json/EmailSubmissionSetSerializer.scala   | 28 ++++++++++++-----
 .../apache/james/jmap/json/MailboxSerializer.scala | 28 ++++++++++++-----
 .../james/jmap/json/VacationSerializer.scala       | 15 ++++++++--
 .../scala/org/apache/james/jmap/mail/Email.scala   |  8 ++---
 .../org/apache/james/jmap/mail/EmailGet.scala      |  1 -
 .../org/apache/james/jmap/mail/EmailSet.scala      | 18 +++++------
 .../james/jmap/mail/EmailSubmissionSet.scala       | 22 ++++++--------
 .../org/apache/james/jmap/mail/MailboxGet.scala    | 12 ++++----
 .../org/apache/james/jmap/mail/MailboxSet.scala    |  8 ++---
 .../apache/james/jmap/method/EmailGetMethod.scala  |  5 ++--
 .../jmap/method/EmailSetCreatePerformer.scala      |  3 +-
 .../jmap/method/EmailSetDeletePerformer.scala      |  3 +-
 .../apache/james/jmap/method/EmailSetMethod.scala  |  2 +-
 .../jmap/method/EmailSetUpdatePerformer.scala      |  3 +-
 .../jmap/method/EmailSubmissionSetMethod.scala     | 29 +++++++-----------
 .../james/jmap/method/MailboxGetMethod.scala       |  5 ++--
 .../jmap/method/MailboxSetCreatePerformer.scala    | 12 ++++----
 .../jmap/method/MailboxSetDeletePerformer.scala    |  5 ++--
 .../jmap/method/MailboxSetUpdatePerformer.scala    |  3 +-
 .../jmap/method/VacationResponseGetMethod.scala    |  4 +--
 .../james/jmap/vacation/VacationResponse.scala     |  9 +++---
 .../james/jmap/vacation/VacationResponseGet.scala  |  1 -
 .../jmap/json/MailboxGetSerializationTest.scala    |  7 ++---
 .../VacationResponseGetSerializationTest.scala     | 11 ++++---
 28 files changed, 163 insertions(+), 132 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/EmailSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
index 4c33a7b..e55d8ff 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSetMethodContract.scala
@@ -5076,7 +5076,7 @@ trait EmailSetMethodContract {
            |        "notDestroyed": {
            |          "invalid": {
            |            "type": "invalidArguments",
-           |            "description": "invalid is not a messageId: ${invalidMessageIdMessage("invalid")}"
+           |            "description": "UnparsedMessageId(invalid) is not a messageId: ${invalidMessageIdMessage("invalid")}"
            |          }
            |        }
            |      }, "c1"]]
@@ -5704,7 +5704,7 @@ trait EmailSetMethodContract {
            |        "notDestroyed": {
            |          "invalid": {
            |            "type": "invalidArguments",
-           |            "description": "invalid is not a messageId: ${invalidMessageIdMessage("invalid")}"
+           |            "description": "UnparsedMessageId(invalid) is not a messageId: ${invalidMessageIdMessage("invalid")}"
            |          }
            |        }
            |      }, "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 171cc3c..934214d 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
@@ -1059,7 +1059,7 @@ trait MailboxSetMethodContract {
          |      "notCreated": {
          |        "C42": {
          |          "type": "invalidArguments",
-         |          "description": "'/parentId' property in mailbox object is not valid: 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."
+         |          "description": "'/parentId' property in mailbox object is not valid: mailboxId does not match Id constraints: 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."
          |        }
          |      }
          |    },
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
index edc3392..d5d890b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailGetSerializer.scala
@@ -19,10 +19,12 @@
 
 package org.apache.james.jmap.json
 
+import eu.timepit.refined
 import org.apache.james.jmap.api.model.Preview
+import org.apache.james.jmap.core.Id.IdConstraint
 import org.apache.james.jmap.core.{Properties, State}
 import org.apache.james.jmap.mail.Email.Size
-import org.apache.james.jmap.mail.{AddressesHeaderValue, BlobId, Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailChangesRequest, EmailChangesResponse, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, Fetc [...]
+import org.apache.james.jmap.mail.{AddressesHeaderValue, BlobId, Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailChangesRequest, EmailChangesResponse, EmailFastView, EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, Fetc [...]
 import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
 import play.api.libs.functional.syntax._
 import play.api.libs.json._
@@ -108,6 +110,14 @@ object EmailGetSerializer {
   }
   private implicit val headersWrites: Writes[EmailHeader] = Json.writes[EmailHeader]
   private implicit val bodyValueWrites: Writes[EmailBodyValue] = Json.writes[EmailBodyValue]
+  private implicit val unparsedMessageIdWrites: Writes[UnparsedEmailId] = Json.valueWrites[UnparsedEmailId]
+  private implicit val unparsedMessageIdReads: Reads[UnparsedEmailId] = {
+    case JsString(string) => refined.refineV[IdConstraint](string)
+      .fold(
+        e => JsError(s"emailId does not match Id constraints: $e"),
+        id => JsSuccess(UnparsedEmailId(id)))
+    case _ => JsError("emailId needs to be represented by a JsString")
+  }
   private implicit val emailIdsReads: Reads[EmailIds] = Json.valueReads[EmailIds]
   private implicit val emailGetRequestReads: Reads[EmailGetRequest] = Json.reads[EmailGetRequest]
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
index d75ce09..b5b99f9 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSetSerializer.scala
@@ -20,15 +20,15 @@
 package org.apache.james.jmap.json
 
 import cats.implicits._
+import eu.timepit.refined
 import eu.timepit.refined.collection.NonEmpty
 import eu.timepit.refined.refineV
 import eu.timepit.refined.types.string.NonEmptyString
 import javax.inject.Inject
 import org.apache.james.jmap.core.Id.IdConstraint
 import org.apache.james.jmap.core.{Id, SetError, State, UTCDate}
-import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId, UnparsedMessageIdConstraint}
 import org.apache.james.jmap.mail.KeywordsFactory.STRICT_KEYWORDS_FACTORY
-import org.apache.james.jmap.mail.{AddressesHeaderValue, AsAddresses, AsDate, AsGroupedAddresses, AsMessageIds, AsRaw, AsText, AsURLs, Attachment, BlobId, Charset, ClientBody, ClientCid, ClientEmailBodyValue, ClientPartId, DateHeaderValue, DestroyIds, Disposition, EmailAddress, EmailAddressGroup, EmailCreationRequest, EmailCreationResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailSetRequest, EmailSetResponse, EmailSetUpdate, EmailerName, GroupName, GroupedAddressesHeaderValu [...]
+import org.apache.james.jmap.mail.{AddressesHeaderValue, AsAddresses, AsDate, AsGroupedAddresses, AsMessageIds, AsRaw, AsText, AsURLs, Attachment, BlobId, Charset, ClientBody, ClientCid, ClientEmailBodyValue, ClientPartId, DateHeaderValue, DestroyIds, Disposition, EmailAddress, EmailAddressGroup, EmailCreationId, EmailCreationRequest, EmailCreationResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, EmailSetRequest, EmailSetResponse, EmailSetUpdate, EmailerName, GroupName, GroupedAd [...]
 import org.apache.james.mailbox.model.{MailboxId, MessageId}
 import play.api.libs.json.{Format, JsArray, JsBoolean, JsError, JsNull, JsObject, JsResult, JsString, JsSuccess, JsValue, Json, OWrites, Reads, Writes}
 
@@ -184,10 +184,14 @@ class EmailSetSerializer @Inject()(messageIdFactory: MessageId.Factory, mailboxI
   }
 
   private implicit val updatesMapReads: Reads[Map[UnparsedMessageId, JsObject]] =
-    Reads.mapReads[UnparsedMessageId, JsObject] {string => refineV[UnparsedMessageIdConstraint](string).fold(JsError(_), id => JsSuccess(id)) }
+    Reads.mapReads[UnparsedMessageId, JsObject] {string => refineV[IdConstraint](string)
+      .fold(e => JsError(s"messageId needs to match id constraints: $e"),
+        id => JsSuccess(UnparsedMessageId(id))) }
 
   private implicit val createsMapReads: Reads[Map[EmailCreationId, JsObject]] =
-    Reads.mapReads[EmailCreationId, JsObject] {s => refineV[IdConstraint](s).fold(JsError(_), JsSuccess(_)) }
+    Reads.mapReads[EmailCreationId, JsObject] {s => refineV[IdConstraint](s)
+      .fold(e => JsError(s"email creationId needs to match id constraints: $e"),
+        id => JsSuccess(EmailCreationId(id))) }
 
   private implicit val keywordReads: Reads[Keyword] = {
     case jsString: JsString => Keyword.parse(jsString.value)
@@ -202,18 +206,33 @@ class EmailSetSerializer @Inject()(messageIdFactory: MessageId.Factory, mailboxI
     keywordsMap => STRICT_KEYWORDS_FACTORY.fromSet(keywordsMap.keys.toSet)
       .fold(e => JsError(e.getMessage), keywords => JsSuccess(keywords)))
 
-  private implicit val blobIdFormat: Format[BlobId] = Json.valueFormat[BlobId]
+  private implicit val blobIdWrites: Writes[BlobId] = Json.valueWrites[BlobId]
+  private implicit val blobIdReads: Reads[BlobId] = {
+    case JsString(string) => BlobId.of(string)
+      .toEither.fold(
+        e => JsError(s"blobId does not match Id constraints: $e"),
+        blobId => JsSuccess(blobId))
+    case _ => JsError("blobId needs to be represented by a JsString")
+  }
+  private implicit val unparsedMessageIdWrites: Writes[UnparsedMessageId] = Json.valueWrites[UnparsedMessageId]
+  private implicit val unparsedMessageIdReads: Reads[UnparsedMessageId] = {
+    case JsString(string) => refined.refineV[IdConstraint](string)
+      .fold(
+        e => JsError(s"messageId does not match Id constraints: $e"),
+        id => JsSuccess(UnparsedMessageId(id)))
+    case _ => JsError("messageId needs to be represented by a JsString")
+  }
   private implicit val unitWrites: Writes[Unit] = _ => JsNull
   private implicit val updatedWrites: Writes[Map[MessageId, Unit]] = mapWrites[MessageId, Unit](_.serialize, unitWrites)
-  private implicit val notDestroyedWrites: Writes[Map[UnparsedMessageId, SetError]] = mapWrites[UnparsedMessageId, SetError](_.value, setErrorWrites)
+  private implicit val notDestroyedWrites: Writes[Map[UnparsedMessageId, SetError]] = mapWrites[UnparsedMessageId, SetError](_.id.value, setErrorWrites)
   private implicit val destroyIdsReads: Reads[DestroyIds] = Json.valueFormat[DestroyIds]
   private implicit val destroyIdsWrites: Writes[DestroyIds] = Json.valueWrites[DestroyIds]
   private implicit val emailRequestSetReads: Reads[EmailSetRequest] = Json.reads[EmailSetRequest]
   private implicit val emailCreationResponseWrites: Writes[EmailCreationResponse] = Json.writes[EmailCreationResponse]
   private implicit val createsMapWrites: Writes[Map[EmailCreationId, EmailCreationResponse]] =
-    mapWrites[EmailCreationId, EmailCreationResponse](_.value, emailCreationResponseWrites)
+    mapWrites[EmailCreationId, EmailCreationResponse](_.id.value, emailCreationResponseWrites)
   private implicit val notCreatedMapWrites: Writes[Map[EmailCreationId, SetError]] =
-    mapWrites[EmailCreationId, SetError](_.value, setErrorWrites)
+    mapWrites[EmailCreationId, SetError](_.id.value, setErrorWrites)
 
   private implicit val stateWrites: Writes[State] = Json.valueWrites[State]
   private implicit val emailResponseSetWrites: OWrites[EmailSetResponse] = Json.writes[EmailSetResponse]
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSubmissionSetSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSubmissionSetSerializer.scala
index 3f3c911..af944e4 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSubmissionSetSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/EmailSubmissionSetSerializer.scala
@@ -19,29 +19,31 @@
 
 package org.apache.james.jmap.json
 
+import eu.timepit.refined
 import eu.timepit.refined.refineV
 import javax.inject.Inject
 import org.apache.james.core.MailAddress
 import org.apache.james.jmap.core.Id.IdConstraint
 import org.apache.james.jmap.core.{SetError, State}
-import org.apache.james.jmap.mail.EmailSet.{UnparsedMessageId, UnparsedMessageIdConstraint}
-import org.apache.james.jmap.mail.EmailSubmissionSet.EmailSubmissionCreationId
-import org.apache.james.jmap.mail.{DestroyIds, EmailSubmissionAddress, EmailSubmissionCreationRequest, EmailSubmissionCreationResponse, EmailSubmissionId, EmailSubmissionSetRequest, EmailSubmissionSetResponse, Envelope}
+import org.apache.james.jmap.mail.{DestroyIds, EmailSubmissionAddress, EmailSubmissionCreationId, EmailSubmissionCreationRequest, EmailSubmissionCreationResponse, EmailSubmissionId, EmailSubmissionSetRequest, EmailSubmissionSetResponse, Envelope, UnparsedMessageId}
 import org.apache.james.mailbox.model.MessageId
 import play.api.libs.json.{JsError, JsObject, JsResult, JsString, JsSuccess, JsValue, Json, Reads, Writes}
 
 import scala.util.Try
 
 class EmailSubmissionSetSerializer @Inject()(messageIdFactory: MessageId.Factory) {
+  private implicit val creationIdFormat: Reads[EmailSubmissionCreationId] = Json.valueFormat[EmailSubmissionCreationId]
   private implicit val mapCreationRequestByEmailSubmissionCreationId: Reads[Map[EmailSubmissionCreationId, JsObject]] =
-    Reads.mapReads[EmailSubmissionCreationId, JsObject] {string => refineV[IdConstraint](string).fold(JsError(_), id => JsSuccess(id)) }
+    Reads.mapReads[EmailSubmissionCreationId, JsObject] {string => refineV[IdConstraint](string)
+      .fold(e => JsError(s"email submission creationId needs to match id contraints: $e"),
+        id => JsSuccess(EmailSubmissionCreationId(id))) }
 
   private implicit val messageIdReads: Reads[MessageId] = {
     case JsString(serializedMessageId) => Try(JsSuccess(messageIdFactory.fromString(serializedMessageId)))
-      .fold(_ => JsError("Invalid messageId"), messageId => messageId)
+      .fold(e => JsError(s"Invalid messageId: ${e.getMessage}"), messageId => messageId)
     case _ => JsError("Expecting messageId to be represented by a JsString")
   }
-  private implicit val notCreatedWrites: Writes[Map[EmailSubmissionCreationId, SetError]] = mapWrites[EmailSubmissionCreationId, SetError](_.value, setErrorWrites)
+  private implicit val notCreatedWrites: Writes[Map[EmailSubmissionCreationId, SetError]] = mapWrites[EmailSubmissionCreationId, SetError](_.id.value, setErrorWrites)
 
   private implicit val mailAddressReads: Reads[MailAddress] = {
     case JsString(value) => Try(JsSuccess(new MailAddress(value)))
@@ -50,7 +52,17 @@ class EmailSubmissionSetSerializer @Inject()(messageIdFactory: MessageId.Factory
   }
 
   private implicit val emailUpdatesMapReads: Reads[Map[UnparsedMessageId, JsObject]] =
-    Reads.mapReads[UnparsedMessageId, JsObject] {string => refineV[UnparsedMessageIdConstraint](string).fold(JsError(_), id => JsSuccess(id)) }
+    Reads.mapReads[UnparsedMessageId, JsObject] {string => refineV[IdConstraint](string)
+      .fold(e => JsError(s"messageId needs to match id contraints: $e"),
+        id => JsSuccess(UnparsedMessageId(id))) }
+  private implicit val unparsedMessageIdReads: Reads[UnparsedMessageId] = {
+    case JsString(string) => refined.refineV[IdConstraint](string)
+      .fold(
+        e => JsError(s"messageId does not match Id constraints: $e"),
+        id => JsSuccess(UnparsedMessageId(id)))
+    case _ => JsError("messageId needs to be represented by a JsString")
+  }
+  private implicit val unparsedMessageIdWrites: Writes[UnparsedMessageId] = Json.valueWrites[UnparsedMessageId]
   private implicit val destroyIdsReads: Reads[DestroyIds] = Json.valueFormat[DestroyIds]
 
   private implicit val emailSubmissionSetRequestReads: Reads[EmailSubmissionSetRequest] = Json.reads[EmailSubmissionSetRequest]
@@ -68,7 +80,7 @@ class EmailSubmissionSetSerializer @Inject()(messageIdFactory: MessageId.Factory
   private implicit def emailSubmissionSetResponseWrites(implicit emailSubmissionCreationResponseWrites: Writes[EmailSubmissionCreationResponse]): Writes[EmailSubmissionSetResponse] = Json.writes[EmailSubmissionSetResponse]
 
   private implicit def emailSubmissionMapCreationResponseWrites(implicit emailSubmissionSetCreationResponseWrites: Writes[EmailSubmissionCreationResponse]): Writes[Map[EmailSubmissionCreationId, EmailSubmissionCreationResponse]] =
-    mapWrites[EmailSubmissionCreationId, EmailSubmissionCreationResponse](_.value, emailSubmissionSetCreationResponseWrites)
+    mapWrites[EmailSubmissionCreationId, EmailSubmissionCreationResponse](_.id.value, emailSubmissionSetCreationResponseWrites)
 
   def deserializeEmailSubmissionSetRequest(input: JsValue): JsResult[EmailSubmissionSetRequest] = Json.fromJson[EmailSubmissionSetRequest](input)
 
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 c227c97..e2690e7 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
@@ -19,15 +19,14 @@
 
 package org.apache.james.jmap.json
 
+import eu.timepit.refined
 import eu.timepit.refined._
 import javax.inject.Inject
 import org.apache.james.core.{Domain, Username}
 import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.core.Id.IdConstraint
 import org.apache.james.jmap.core.{ClientId, Properties, SetError, State}
-import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
-import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
-import org.apache.james.jmap.mail.{DelegatedNamespace, Ids, IsSubscribed, Mailbox, MailboxChangesRequest, MailboxChangesResponse, MailboxCreationRequest, MailboxCreationResponse, MailboxGetRequest, MailboxGetResponse, MailboxNamespace, MailboxPatchObject, MailboxRights, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, MayAddItems, MayCreateChild, MayDelete, MayReadItems, MayRemoveItems, MayRename, MaySetKeywords, MaySetSeen, MaySubmit, NotFound, PersonalNamespace, Quota, Quo [...]
+import org.apache.james.jmap.mail.{DelegatedNamespace, Ids, IsSubscribed, Mailbox, MailboxChangesRequest, MailboxChangesResponse, MailboxCreationId, MailboxCreationRequest, MailboxCreationResponse, MailboxGetRequest, MailboxGetResponse, MailboxNamespace, MailboxPatchObject, MailboxRights, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, MayAddItems, MayCreateChild, MayDelete, MayReadItems, MayRemoveItems, MayRename, MaySetKeywords, MaySetSeen, MaySubmit, NotFound, PersonalNa [...]
 import org.apache.james.mailbox.Role
 import org.apache.james.mailbox.model.MailboxACL.{Right => JavaRight}
 import org.apache.james.mailbox.model.{MailboxACL, MailboxId}
@@ -42,6 +41,15 @@ class MailboxSerializer @Inject()(mailboxIdFactory: MailboxId.Factory) {
     case JsString(serializedMailboxId) => Try(JsSuccess(mailboxIdFactory.fromString(serializedMailboxId))).getOrElse(JsError())
     case _ => JsError()
   }
+  private implicit val unparsedMailboxIdWrites: Writes[UnparsedMailboxId] = Json.valueWrites[UnparsedMailboxId]
+  private implicit val unparsedMailboxIdReads: Reads[UnparsedMailboxId] = {
+    case JsString(string) =>
+      refined.refineV[IdConstraint](string)
+        .fold(
+          e => JsError(s"mailboxId does not match Id constraints: $e"),
+          id => JsSuccess(UnparsedMailboxId(id)))
+    case _ => JsError("mailboxId needs to be represented by a JsString")
+  }
 
   private implicit val roleWrites: Writes[Role] = Writes(role => JsString(role.serialize))
   private implicit val sortOrderWrites: Writes[SortOrder] = Json.valueWrites[SortOrder]
@@ -120,10 +128,14 @@ class MailboxSerializer @Inject()(mailboxIdFactory: MailboxId.Factory) {
   private implicit val mailboxPatchObject: Reads[MailboxPatchObject] = Json.valueReads[MailboxPatchObject]
 
   private implicit val mapPatchObjectByMailboxIdReads: Reads[Map[UnparsedMailboxId, MailboxPatchObject]] =
-    Reads.mapReads[UnparsedMailboxId, MailboxPatchObject] {string => refineV[IdConstraint](string).fold(JsError(_), id => JsSuccess(id)) }
+    Reads.mapReads[UnparsedMailboxId, MailboxPatchObject] {string =>refineV[IdConstraint](string)
+      .fold(e => JsError(s"mailboxId needs to match id contraints: $e"),
+        id => JsSuccess(UnparsedMailboxId(id))) }
 
   private implicit val mapCreationRequestByMailBoxCreationId: Reads[Map[MailboxCreationId, JsObject]] =
-    Reads.mapReads[MailboxCreationId, JsObject] {string => refineV[IdConstraint](string).fold(JsError(_), id => JsSuccess(id)) }
+    Reads.mapReads[MailboxCreationId, JsObject] {string => refineV[IdConstraint](string)
+      .fold(e => JsError(s"mailbox creationId needs to match id contraints: $e"),
+        id => JsSuccess(MailboxCreationId(id))) }
 
   private implicit val mailboxSetRequestReads: Reads[MailboxSetRequest] = Json.reads[MailboxSetRequest]
 
@@ -136,15 +148,17 @@ class MailboxSerializer @Inject()(mailboxIdFactory: MailboxId.Factory) {
   private implicit val mailboxSetUpdateResponseWrites: Writes[MailboxUpdateResponse] = Json.valueWrites[MailboxUpdateResponse]
 
   private implicit val mailboxMapSetErrorForCreationWrites: Writes[Map[MailboxCreationId, SetError]] =
-    mapWrites[MailboxCreationId, SetError](_.value, setErrorWrites)
+    mapWrites[MailboxCreationId, SetError](_.id.value, setErrorWrites)
   private implicit val mailboxMapSetErrorWrites: Writes[Map[MailboxId, SetError]] =
     mapWrites[MailboxId, SetError](_.serialize(), setErrorWrites)
   private implicit val mailboxMapSetErrorWritesByClientId: Writes[Map[ClientId, SetError]] =
     mapWrites[ClientId, SetError](_.value.value, setErrorWrites)
   private implicit val mailboxMapCreationResponseWrites: Writes[Map[MailboxCreationId, MailboxCreationResponse]] =
-    mapWrites[MailboxCreationId, MailboxCreationResponse](_.value, mailboxCreationResponseWrites)
+    mapWrites[MailboxCreationId, MailboxCreationResponse](_.id.value, mailboxCreationResponseWrites)
   private implicit val mailboxMapUpdateResponseWrites: Writes[Map[MailboxId, MailboxUpdateResponse]] =
     mapWrites[MailboxId, MailboxUpdateResponse](_.serialize(), mailboxSetUpdateResponseWrites)
+  private implicit val mailboxMapUpdateErrorWrites: Writes[Map[UnparsedMailboxId, SetError]] =
+    mapWrites[UnparsedMailboxId, SetError](_.id.value, setErrorWrites)
 
   private implicit val mailboxSetResponseWrites: Writes[MailboxSetResponse] = Json.writes[MailboxSetResponse]
   private implicit val changesResponseWrites: Writes[MailboxChangesResponse] = response =>
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 4219bc2..d58d1b7 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
@@ -19,15 +19,26 @@
 
 package org.apache.james.jmap.json
 
+import eu.timepit.refined
+import org.apache.james.jmap.core.Id.IdConstraint
 import org.apache.james.jmap.core.{Properties, State}
 import org.apache.james.jmap.mail.Subject
 import org.apache.james.jmap.vacation.VacationResponse.VACATION_RESPONSE_ID
-import org.apache.james.jmap.vacation.{FromDate, HtmlBody, IsEnabled, TextBody, ToDate, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseId, VacationResponseIds, VacationResponseNotFound, VacationResponsePatchObject, VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse}
+import org.apache.james.jmap.vacation.{FromDate, HtmlBody, IsEnabled, TextBody, ToDate, UnparsedVacationResponseId, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseId, VacationResponseIds, VacationResponseNotFound, VacationResponsePatchObject, VacationResponseSetError, VacationResponseSetRequest, VacationResponseSetResponse, VacationResponseUpdateResponse}
 import play.api.libs.json._
 
 import scala.language.implicitConversions
 
 object VacationSerializer {
+
+  private implicit val unparsedMessageIdWrites: Writes[UnparsedVacationResponseId] = Json.valueWrites[UnparsedVacationResponseId]
+  private implicit val unparsedMessageIdReads: Reads[UnparsedVacationResponseId] = {
+    case JsString(string) => refined.refineV[IdConstraint](string)
+      .fold(
+        e => JsError(s"vacation response id does not match Id constraints: $e"),
+        id => JsSuccess(UnparsedVacationResponseId(id)))
+    case _ => JsError("vacation response id needs to be represented by a JsString")
+  }
   private implicit val isEnabledReads: Reads[IsEnabled] = Json.valueReads[IsEnabled]
   private implicit val vacationResponsePatchObjectReads: Reads[VacationResponsePatchObject] = {
     case jsObject: JsObject => JsSuccess(VacationResponsePatchObject(jsObject))
@@ -62,7 +73,7 @@ object VacationSerializer {
   private implicit val vacationResponseGetRequest: Reads[VacationResponseGetRequest] = Json.reads[VacationResponseGetRequest]
 
   private implicit val vacationResponseNotFoundWrites: Writes[VacationResponseNotFound] =
-    notFound => JsArray(notFound.value.toList.map(id => JsString(id.value)))
+    notFound => JsArray(notFound.value.toList.map(id => JsString(id.id.value)))
 
   private implicit val vacationResponseGetResponseWrites: Writes[VacationResponseGetResponse] = Json.writes[VacationResponseGetResponse]
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index b9c6048..d3b037c 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
@@ -33,7 +33,7 @@ import eu.timepit.refined.types.string.NonEmptyString
 import javax.inject.Inject
 import org.apache.james.jmap.api.model.Preview
 import org.apache.james.jmap.api.projections.{MessageFastViewPrecomputedProperties, MessageFastViewProjection}
-import org.apache.james.jmap.core.Id.IdConstraint
+import org.apache.james.jmap.core.Id.{Id, IdConstraint}
 import org.apache.james.jmap.core.{Properties, UTCDate}
 import org.apache.james.jmap.mail.BracketHeader.sanitize
 import org.apache.james.jmap.mail.Email.{Size, sanitizeSize}
@@ -61,8 +61,6 @@ import scala.util.{Failure, Success, Try}
 object Email {
   private val logger: Logger = LoggerFactory.getLogger(classOf[EmailView])
 
-  type UnparsedEmailId = String Refined IdConstraint
-
   val defaultProperties: Properties = Properties("id", "blobId", "threadId", "mailboxIds", "keywords", "size",
     "receivedAt", "messageId", "inReplyTo", "references", "sender", "from",
     "to", "cc", "bcc", "replyTo", "subject", "sentAt", "hasAttachment",
@@ -76,7 +74,7 @@ object Email {
   def asUnparsed(messageId: MessageId): Try[UnparsedEmailId] =
     refined.refineV[IdConstraint](messageId.serialize()) match {
       case Left(e) => Failure(new IllegalArgumentException(e))
-      case scala.Right(value) => Success(value)
+      case scala.Right(value) => Success(UnparsedEmailId(value))
     }
 
   type Size = Long Refined NonNegative
@@ -115,6 +113,8 @@ object Email {
   }
 }
 
+case class UnparsedEmailId(id: Id)
+
 object ReadLevel {
   private val metadataProperty: Seq[NonEmptyString] = Seq("id", "size", "mailboxIds",
     "mailboxIds", "blobId", "threadId", "receivedAt")
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
index 27f7ef2..248ed6d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
@@ -27,7 +27,6 @@ import eu.timepit.refined.numeric.NonNegative
 import eu.timepit.refined.types.string.NonEmptyString
 import org.apache.james.jmap.api.change.Limit
 import org.apache.james.jmap.core.{AccountId, Properties, State}
-import org.apache.james.jmap.mail.Email.UnparsedEmailId
 import org.apache.james.jmap.mail.EmailGetRequest.MaxBodyValueBytes
 import org.apache.james.jmap.mail.EmailHeaders.SPECIFIC_HEADER_PREFIX
 import org.apache.james.jmap.method.WithAccountId
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
index 7505f72..ca28163 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSet.scala
@@ -25,13 +25,10 @@ import cats.implicits._
 import com.google.common.net.MediaType
 import com.google.common.net.MediaType.{HTML_UTF_8, PLAIN_TEXT_UTF_8}
 import eu.timepit.refined
-import eu.timepit.refined.api.Refined
-import eu.timepit.refined.collection.NonEmpty
-import org.apache.james.jmap.core.Id.Id
+import org.apache.james.jmap.core.Id.{Id, IdConstraint}
 import org.apache.james.jmap.core.{AccountId, SetError, State, UTCDate}
 import org.apache.james.jmap.mail.Disposition.INLINE
 import org.apache.james.jmap.mail.Email.Size
-import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId}
 import org.apache.james.jmap.method.WithAccountId
 import org.apache.james.jmap.routes.{Blob, BlobResolvers}
 import org.apache.james.mailbox.MailboxSession
@@ -52,19 +49,18 @@ import scala.jdk.OptionConverters._
 import scala.util.{Right, Try, Using}
 
 object EmailSet {
-  type EmailCreationId = Id
-  type UnparsedMessageIdConstraint = NonEmpty
-  type UnparsedMessageId = String Refined UnparsedMessageIdConstraint
-
-  def asUnparsed(messageId: MessageId): UnparsedMessageId = refined.refineV[UnparsedMessageIdConstraint](messageId.serialize()) match {
+  def asUnparsed(messageId: MessageId): UnparsedMessageId = refined.refineV[IdConstraint](messageId.serialize()) match {
     case Left(e) => throw new IllegalArgumentException(e)
-    case scala.Right(value) => value
+    case scala.Right(value) => UnparsedMessageId(value)
   }
 
   def parse(messageIdFactory: MessageId.Factory)(unparsed: UnparsedMessageId): Try[MessageId] =
-    Try(messageIdFactory.fromString(unparsed.value))
+    Try(messageIdFactory.fromString(unparsed.id.value))
 }
 
+case class EmailCreationId(id: Id)
+case class UnparsedMessageId(id: Id)
+
 object SubType {
   val HTML_SUBTYPE = "html"
   val MIXED_SUBTYPE = "mixed"
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSubmissionSet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSubmissionSet.scala
index 0e1da02..d155580 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSubmissionSet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailSubmissionSet.scala
@@ -30,20 +30,16 @@ import org.apache.james.core.MailAddress
 import org.apache.james.jmap.core.Id.{Id, IdConstraint}
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{AccountId, Id, Properties, SetError, State}
-import org.apache.james.jmap.mail.EmailSet.UnparsedMessageId
-import org.apache.james.jmap.mail.EmailSubmissionSet.EmailSubmissionCreationId
 import org.apache.james.jmap.method.{EmailSubmissionCreationParseException, WithAccountId}
 import org.apache.james.mailbox.model.MessageId
 import play.api.libs.json.JsObject
 
-object EmailSubmissionSet {
-  type EmailSubmissionCreationId = Id
-}
-
 object EmailSubmissionId {
   def generate: EmailSubmissionId = EmailSubmissionId(Id.validate(UUID.randomUUID().toString).toOption.get)
 }
 
+case class EmailSubmissionCreationId(id: Id)
+
 case class EmailSubmissionSetRequest(accountId: AccountId,
                                      create: Option[Map[EmailSubmissionCreationId, JsObject]],
                                      onSuccessUpdateEmail: Option[Map[EmailSubmissionCreationId, JsObject]],
@@ -98,18 +94,18 @@ case class EmailSubmissionSetRequest(accountId: AccountId,
       .map(_ => this)
 
   private def validate(creationId: EmailSubmissionCreationId, supportedCreationIds: List[EmailSubmissionCreationId]): Either[IllegalArgumentException, EmailSubmissionCreationId] = {
-    if (creationId.startsWith("#")) {
-      val realId = creationId.substring(1)
-      val validatedId: Either[String, EmailSubmissionCreationId] = refineV[IdConstraint](realId)
+    if (creationId.id.startsWith("#")) {
+      val realId = creationId.id.substring(1)
+      val validatedId: Either[String, Id] = refineV[IdConstraint](realId)
       validatedId
         .left.map(s => new IllegalArgumentException(s))
-        .flatMap(id => if (supportedCreationIds.contains(id)) {
-          scala.Right(id)
+        .flatMap(id => if (supportedCreationIds.contains(EmailSubmissionCreationId(id))) {
+          scala.Right(EmailSubmissionCreationId(id))
         } else {
-          Left(new IllegalArgumentException(s"$creationId cannot be referenced in current method call"))
+          Left(new IllegalArgumentException(s"${creationId.id} cannot be referenced in current method call"))
         })
     } else {
-      Left(new IllegalArgumentException(s"$creationId cannot be retrieved as storage for EmailSubmission is not yet implemented"))
+      Left(new IllegalArgumentException(s"${creationId.id} cannot be retrieved as storage for EmailSubmission is not yet implemented"))
     }
   }
 }
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 ac68630..2d55106 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/MailboxGet.scala
@@ -20,26 +20,22 @@
 package org.apache.james.jmap.mail
 
 import eu.timepit.refined
-import eu.timepit.refined.api.Refined
 import org.apache.james.jmap.api.change.{EmailChanges, Limit, MailboxChanges}
-import org.apache.james.jmap.core.Id.IdConstraint
+import org.apache.james.jmap.core.Id.{Id, IdConstraint}
 import org.apache.james.jmap.core.{AccountId, Properties, State}
-import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
 import org.apache.james.jmap.method.WithAccountId
 import org.apache.james.mailbox.model.MailboxId
 
 import scala.util.{Failure, Try}
 
 object MailboxGet {
-  type UnparsedMailboxId = String Refined IdConstraint
-
   def asUnparsed(mailboxId: MailboxId): UnparsedMailboxId = refined.refineV[IdConstraint](mailboxId.serialize()) match {
     case Left(e) => throw new IllegalArgumentException(e)
-    case scala.Right(value) => value
+    case scala.Right(value) => UnparsedMailboxId(value)
   }
 
   def parse(mailboxIdFactory: MailboxId.Factory)(unparsed: UnparsedMailboxId): Try[MailboxId] =
-    parseString(mailboxIdFactory)(unparsed.value)
+    parseString(mailboxIdFactory)(unparsed.id.value)
 
   def parseString(mailboxIdFactory: MailboxId.Factory)(unparsed: String): Try[MailboxId] =
     unparsed match {
@@ -49,6 +45,8 @@ object MailboxGet {
     }
 }
 
+case class UnparsedMailboxId(id: Id)
+
 case class Ids(value: List[UnparsedMailboxId])
 
 case class MailboxGetRequest(accountId: AccountId,
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 a8d1217..e276df3 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,14 +26,12 @@ import eu.timepit.refined.refineV
 import eu.timepit.refined.types.string.NonEmptyString
 import org.apache.james.core.Username
 import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.core.Id.IdConstraint
+import org.apache.james.jmap.core.Id.Id
 import org.apache.james.jmap.core.SetError.{SetErrorDescription, SetErrorType}
 import org.apache.james.jmap.core.{AccountId, CapabilityIdentifier, Properties, SetError, State}
 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
 import org.apache.james.jmap.method.{MailboxCreationParseException, WithAccountId}
 import org.apache.james.mailbox.model.{MailboxId, MailboxACL => JavaMailboxACL}
 import org.apache.james.mailbox.{MailboxSession, Role}
@@ -46,9 +44,7 @@ case class MailboxSetRequest(accountId: AccountId,
                              destroy: Option[Seq[UnparsedMailboxId]],
                              onDestroyRemoveEmails: Option[RemoveEmailsOnDestroy]) extends WithAccountId
 
-object MailboxSetRequest {
-  type MailboxCreationId = String Refined IdConstraint
-}
+case class MailboxCreationId(id: Id)
 
 case class RemoveEmailsOnDestroy(value: Boolean) extends AnyVal
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
index 736b656..784b95b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailGetMethod.scala
@@ -30,8 +30,7 @@ import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.State.INSTANCE
 import org.apache.james.jmap.core.{AccountId, ErrorCode, Invocation, Properties, State}
 import org.apache.james.jmap.json.{EmailGetSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.Email.UnparsedEmailId
-import org.apache.james.jmap.mail.{Email, EmailBodyPart, EmailGetRequest, EmailGetResponse, EmailIds, EmailNotFound, EmailView, EmailViewReaderFactory, SpecificHeaderRequest}
+import org.apache.james.jmap.mail.{Email, EmailBodyPart, EmailGetRequest, EmailGetResponse, EmailIds, EmailNotFound, EmailView, EmailViewReaderFactory, SpecificHeaderRequest, UnparsedEmailId}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.mailbox.model.MessageId
@@ -172,7 +171,7 @@ class EmailGetMethod @Inject() (readerFactory: EmailViewReaderFactory,
 
   private def asMessageId(id: UnparsedEmailId): Either[(UnparsedEmailId, IllegalArgumentException),  MessageId] =
     try {
-      Right(messageIdFactory.fromString(id))
+      Right(messageIdFactory.fromString(id.id))
     } catch {
       case e: Exception => Left((id, new IllegalArgumentException(e)))
     }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
index b05cdb5..2ec151e 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetCreatePerformer.scala
@@ -28,8 +28,7 @@ import javax.mail.Flags
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{Properties, SetError, UTCDate}
 import org.apache.james.jmap.json.EmailSetSerializer
-import org.apache.james.jmap.mail.EmailSet.EmailCreationId
-import org.apache.james.jmap.mail.{BlobId, Email, EmailCreationRequest, EmailCreationResponse, EmailSetRequest}
+import org.apache.james.jmap.mail.{BlobId, Email, EmailCreationId, EmailCreationRequest, EmailCreationResponse, EmailSetRequest}
 import org.apache.james.jmap.method.EmailSetCreatePerformer.{CreationFailure, CreationResult, CreationResults, CreationSuccess}
 import org.apache.james.jmap.routes.{BlobNotFoundException, BlobResolvers}
 import org.apache.james.mailbox.MessageManager.AppendCommand
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala
index 037e7dc..14f106a 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetDeletePerformer.scala
@@ -22,8 +22,7 @@ package org.apache.james.jmap.method
 import javax.inject.Inject
 import org.apache.james.jmap.core.SetError
 import org.apache.james.jmap.core.SetError.SetErrorDescription
-import org.apache.james.jmap.mail.EmailSet.UnparsedMessageId
-import org.apache.james.jmap.mail.{DestroyIds, EmailSet, EmailSetRequest}
+import org.apache.james.jmap.mail.{DestroyIds, EmailSet, EmailSetRequest, UnparsedMessageId}
 import org.apache.james.jmap.method.EmailSetDeletePerformer.{DestroyFailure, DestroyResult, DestroyResults}
 import org.apache.james.mailbox.model.{DeleteResult, MessageId}
 import org.apache.james.mailbox.{MailboxSession, MessageIdManager}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
index bd52c8b..a4ce860 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
@@ -72,7 +72,7 @@ class EmailSetMethod @Inject()(serializer: EmailSetSerializer,
           case (processingContext, (clientId, response)) =>
             Id.validate(response.id.serialize)
               .fold(_ => processingContext,
-                serverId => processingContext.recordCreatedId(ClientId(clientId), ServerId(serverId)))
+                serverId => processingContext.recordCreatedId(ClientId(clientId.id), ServerId(serverId)))
         }))
   }
 
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetUpdatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetUpdatePerformer.scala
index d40273b..059aed6 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetUpdatePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetUpdatePerformer.scala
@@ -27,9 +27,8 @@ import javax.mail.Flags
 import org.apache.james.jmap.core.SetError
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.json.EmailSetSerializer
-import org.apache.james.jmap.mail.EmailSet.UnparsedMessageId
 import org.apache.james.jmap.mail.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
-import org.apache.james.jmap.mail.{EmailSet, EmailSetRequest, MailboxIds, ValidatedEmailSetUpdate}
+import org.apache.james.jmap.mail.{EmailSet, EmailSetRequest, MailboxIds, UnparsedMessageId, ValidatedEmailSetUpdate}
 import org.apache.james.jmap.method.EmailSetUpdatePerformer.{EmailUpdateFailure, EmailUpdateResult, EmailUpdateResults, EmailUpdateSuccess}
 import org.apache.james.mailbox.MessageManager.FlagsUpdateMode
 import org.apache.james.mailbox.exception.MailboxNotFoundException
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
index 4c7f82e..acfb0dc 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
@@ -31,13 +31,12 @@ import javax.mail.Message.RecipientType
 import javax.mail.internet.{InternetAddress, MimeMessage}
 import org.apache.james.core.{MailAddress, Username}
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, EMAIL_SUBMISSION, JMAP_CORE}
-import org.apache.james.jmap.core.Id.IdConstraint
+import org.apache.james.jmap.core.Id.{Id, IdConstraint}
 import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.SetError.{SetErrorDescription, SetErrorType}
-import org.apache.james.jmap.core.{ClientId, Id, Invocation, Properties, ServerId, SetError, State}
+import org.apache.james.jmap.core.{ClientId, Invocation, Properties, ServerId, SetError, State}
 import org.apache.james.jmap.json.{EmailSubmissionSetSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.EmailSubmissionSet.EmailSubmissionCreationId
-import org.apache.james.jmap.mail.{EmailSubmissionAddress, EmailSubmissionCreationRequest, EmailSubmissionCreationResponse, EmailSubmissionId, EmailSubmissionSetRequest, EmailSubmissionSetResponse, Envelope}
+import org.apache.james.jmap.mail.{EmailSubmissionAddress, EmailSubmissionCreationId, EmailSubmissionCreationRequest, EmailSubmissionCreationResponse, EmailSubmissionId, EmailSubmissionSetRequest, EmailSubmissionSetResponse, Envelope}
 import org.apache.james.jmap.method.EmailSubmissionSetMethod.{LOGGER, MAIL_METADATA_USERNAME_ATTRIBUTE}
 import org.apache.james.jmap.routes.{ProcessingContext, SessionSupplier}
 import org.apache.james.lifecycle.api.{LifecycleUtil, Startable}
@@ -131,12 +130,12 @@ class EmailSubmissionSetMethod @Inject()(serializer: EmailSubmissionSetSerialize
       .toMap
 
     def resolveMessageId(creationId: EmailSubmissionCreationId): Either[IllegalArgumentException, MessageId] = {
-      if (creationId.startsWith("#")) {
-        val realId = creationId.substring(1)
-        val validatedId: Either[String, EmailSubmissionCreationId] = refineV[IdConstraint](realId)
+      if (creationId.id.startsWith("#")) {
+        val realId = creationId.id.substring(1)
+        val validatedId: Either[String, Id] = refineV[IdConstraint](realId)
         validatedId
           .left.map(s => new IllegalArgumentException(s))
-          .flatMap(id => retrieveMessageId(id)
+          .flatMap(id => retrieveMessageId(EmailSubmissionCreationId(id))
             .map(scala.Right(_))
             .getOrElse(Left(new IllegalArgumentException(s"$creationId cannot be referenced in current method call"))))
       } else {
@@ -217,10 +216,9 @@ class EmailSubmissionSetMethod @Inject()(serializer: EmailSubmissionSetSerialize
                             processingContext: ProcessingContext): (CreationResult, ProcessingContext) =
     parseCreate(jsObject)
       .flatMap(emailSubmissionCreationRequest => sendEmail(mailboxSession, emailSubmissionCreationRequest))
-      .flatMap {
+      .map {
         case (creationResponse, messageId) =>
-          recordCreationIdInProcessingContext(emailSubmissionCreationId, processingContext, creationResponse.id)
-            .map(context => (creationResponse, messageId, context))
+          (creationResponse, messageId, recordCreationIdInProcessingContext(emailSubmissionCreationId, processingContext, creationResponse.id))
       }
       .fold(e => (CreationFailure(emailSubmissionCreationId, e), processingContext),
         creation => CreationSuccess(emailSubmissionCreationId, creation._1, creation._2) -> creation._3)
@@ -325,11 +323,6 @@ class EmailSubmissionSetMethod @Inject()(serializer: EmailSubmissionSetSerialize
 
   private def recordCreationIdInProcessingContext(emailSubmissionCreationId: EmailSubmissionCreationId,
                                                   processingContext: ProcessingContext,
-                                                  emailSubmissionId: EmailSubmissionId): Either[IllegalArgumentException, ProcessingContext] =
-    for {
-      creationId <- Id.validate(emailSubmissionCreationId)
-      serverAssignedId <- Id.validate(emailSubmissionId.value)
-    } yield {
-      processingContext.recordCreatedId(ClientId(creationId), ServerId(serverAssignedId))
-    }
+                                                  emailSubmissionId: EmailSubmissionId): ProcessingContext =
+      processingContext.recordCreatedId(ClientId(emailSubmissionCreationId.id), ServerId(emailSubmissionId.value))
 }
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 04cd1f8..6417a17 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
@@ -28,8 +28,7 @@ import org.apache.james.jmap.core.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.core.{AccountId, CapabilityIdentifier, ErrorCode, Invocation, Properties, State}
 import org.apache.james.jmap.http.MailboxesProvisioner
 import org.apache.james.jmap.json.{MailboxSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
-import org.apache.james.jmap.mail.{Mailbox, MailboxFactory, MailboxGet, MailboxGetRequest, MailboxGetResponse, NotFound, PersonalNamespace, Subscriptions}
+import org.apache.james.jmap.mail.{Mailbox, MailboxFactory, MailboxGet, MailboxGetRequest, MailboxGetResponse, NotFound, PersonalNamespace, Subscriptions, UnparsedMailboxId}
 import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.utils.quotas.{QuotaLoaderWithPreloadedDefault, QuotaLoaderWithPreloadedDefaultFactory}
 import org.apache.james.mailbox.exception.MailboxNotFoundException
@@ -118,7 +117,7 @@ class MailboxGetMethod @Inject() (serializer: MailboxSerializer,
           case None => getAllMailboxes(capabilities, mailboxSession)
             .map(MailboxGetResults.found)
           case Some(ids) => SFlux.fromIterable(ids.value)
-            .flatMap(id => Try(mailboxIdFactory.fromString(id.value))
+            .flatMap(id => Try(mailboxIdFactory.fromString(id.id))
               .fold(e => SMono.just(MailboxGetResults.notFound(id)),
                 mailboxId => getMailboxResultById(capabilities, mailboxId, mailboxSession)),
               maxConcurrency = 5)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala
index e9db0ef..87a25f7 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetCreatePerformer.scala
@@ -24,8 +24,7 @@ import javax.inject.Inject
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{ClientId, Id, Properties, ServerId, SetError}
 import org.apache.james.jmap.json.MailboxSerializer
-import org.apache.james.jmap.mail.MailboxSetRequest.MailboxCreationId
-import org.apache.james.jmap.mail.{IsSubscribed, MailboxCreationRequest, MailboxCreationResponse, MailboxRights, MailboxSetRequest, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads}
+import org.apache.james.jmap.mail.{IsSubscribed, MailboxCreationId, MailboxCreationRequest, MailboxCreationResponse, MailboxRights, MailboxSetRequest, SortOrder, TotalEmails, TotalThreads, UnreadEmails, UnreadThreads}
 import org.apache.james.jmap.method.MailboxSetCreatePerformer.{MailboxCreationFailure, MailboxCreationResult, MailboxCreationResults, MailboxCreationSuccess}
 import org.apache.james.jmap.routes.{ProcessingContext, SessionSupplier}
 import org.apache.james.jmap.utils.quotas.QuotaLoaderWithPreloadedDefaultFactory
@@ -129,7 +128,7 @@ class MailboxSetCreatePerformer @Inject()(serializer: MailboxSerializer,
     }
     mailboxCreationRequest.parentId
       .map(maybeParentId => for {
-        parentId <- Try(mailboxIdFactory.fromString(maybeParentId.value))
+        parentId <- Try(mailboxIdFactory.fromString(maybeParentId.id))
           .toEither
           .left
           .map(e => new IllegalArgumentException(e.getMessage, e))
@@ -148,14 +147,13 @@ class MailboxSetCreatePerformer @Inject()(serializer: MailboxSerializer,
 
   private def recordCreationIdInProcessingContext(mailboxCreationId: MailboxCreationId,
                                                   processingContext: ProcessingContext,
-                                                  mailboxId: MailboxId): Either[IllegalArgumentException, ProcessingContext] = {
+                                                  mailboxId: MailboxId): Either[IllegalArgumentException, ProcessingContext] =
     for {
-      creationId <- Id.validate(mailboxCreationId)
       serverAssignedId <- Id.validate(mailboxId.serialize())
     } yield {
-      processingContext.recordCreatedId(ClientId(creationId), ServerId(serverAssignedId))
+      processingContext.recordCreatedId(ClientId(mailboxCreationId.id), ServerId(serverAssignedId))
     }
-  }
+
   private def mailboxSetError(errors: collection.Seq[(JsPath, collection.Seq[JsonValidationError])]): SetError =
     errors.head match {
       case (path, Seq()) => SetError.invalidArguments(SetErrorDescription(s"'$path' property in mailbox object is not valid"))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala
index de5d4ea..66704e1 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetDeletePerformer.scala
@@ -22,8 +22,7 @@ package org.apache.james.jmap.method
 import javax.inject.Inject
 import org.apache.james.jmap.core.SetError
 import org.apache.james.jmap.core.SetError.SetErrorDescription
-import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
-import org.apache.james.jmap.mail.{MailboxGet, MailboxSetError, MailboxSetRequest, RemoveEmailsOnDestroy}
+import org.apache.james.jmap.mail.{MailboxGet, MailboxSetError, MailboxSetRequest, RemoveEmailsOnDestroy, UnparsedMailboxId}
 import org.apache.james.jmap.method.MailboxSetDeletePerformer.{MailboxDeletionFailure, MailboxDeletionResult, MailboxDeletionResults, MailboxDeletionSuccess}
 import org.apache.james.mailbox.exception.MailboxNotFoundException
 import org.apache.james.mailbox.model.{FetchGroup, MailboxId, MessageRange}
@@ -40,7 +39,7 @@ object MailboxSetDeletePerformer {
       case e: MailboxHasMailException => MailboxSetError.mailboxHasEmail(SetErrorDescription(s"${e.mailboxId.serialize} is not empty"))
       case e: MailboxHasChildException => MailboxSetError.mailboxHasChild(SetErrorDescription(s"${e.mailboxId.serialize} has child mailboxes"))
       case e: SystemMailboxChangeException => SetError.invalidArguments(SetErrorDescription("System mailboxes cannot be destroyed"))
-      case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(s"${mailboxId} is not a mailboxId: ${e.getMessage}"))
+      case e: IllegalArgumentException => SetError.invalidArguments(SetErrorDescription(s"${mailboxId.id} is not a mailboxId: ${e.getMessage}"))
       case _ => SetError.serverFail(SetErrorDescription(exception.getMessage))
     }
   }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala
index c82e5c3..0b41a86 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/MailboxSetUpdatePerformer.scala
@@ -25,8 +25,7 @@ import org.apache.james.jmap.core.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.core.SetError.SetErrorDescription
 import org.apache.james.jmap.core.{Properties, SetError}
 import org.apache.james.jmap.json.MailboxSerializer
-import org.apache.james.jmap.mail.MailboxGet.UnparsedMailboxId
-import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, MailboxGet, MailboxPatchObject, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, ServerSetPropertyException, UnsupportedPropertyUpdatedException, ValidatedMailboxPatchObject}
+import org.apache.james.jmap.mail.{InvalidPatchException, InvalidPropertyException, InvalidUpdateException, MailboxGet, MailboxPatchObject, MailboxSetRequest, MailboxSetResponse, MailboxUpdateResponse, NameUpdate, ParentIdUpdate, ServerSetPropertyException, UnparsedMailboxId, UnsupportedPropertyUpdatedException, ValidatedMailboxPatchObject}
 import org.apache.james.jmap.method.MailboxSetUpdatePerformer.{MailboxUpdateFailure, MailboxUpdateResult, MailboxUpdateResults, MailboxUpdateSuccess}
 import org.apache.james.mailbox.MailboxManager.{MailboxSearchFetchType, RenameOption}
 import org.apache.james.mailbox.exception.{InsufficientRightsException, MailboxExistsException, MailboxNameException, MailboxNotFoundException}
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 6683f84..7cc57e1 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
@@ -29,8 +29,8 @@ import org.apache.james.jmap.core.State.INSTANCE
 import org.apache.james.jmap.core.{AccountId, ErrorCode, Invocation, MissingCapabilityException, Properties}
 import org.apache.james.jmap.json.{ResponseSerializer, VacationSerializer}
 import org.apache.james.jmap.routes.SessionSupplier
-import org.apache.james.jmap.vacation.VacationResponse.{UNPARSED_SINGLETON, UnparsedVacationResponseId}
-import org.apache.james.jmap.vacation.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseNotFound}
+import org.apache.james.jmap.vacation.VacationResponse.UNPARSED_SINGLETON
+import org.apache.james.jmap.vacation.{UnparsedVacationResponseId, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseNotFound}
 import org.apache.james.mailbox.MailboxSession
 import org.apache.james.metrics.api.MetricFactory
 import play.api.libs.json.{JsError, JsObject, JsSuccess}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponse.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponse.scala
index 6ce95a0..131253d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponse.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponse.scala
@@ -19,10 +19,9 @@
 
 package org.apache.james.jmap.vacation
 
-import eu.timepit.refined.api.Refined
 import eu.timepit.refined.auto._
 import org.apache.james.jmap.api.vacation.Vacation
-import org.apache.james.jmap.core.Id.{Id, IdConstraint}
+import org.apache.james.jmap.core.Id.Id
 import org.apache.james.jmap.core.{Properties, UTCDate}
 import org.apache.james.jmap.mail.Subject
 
@@ -38,9 +37,7 @@ case class HtmlBody(value: String) extends AnyVal
 
 object VacationResponse {
   val VACATION_RESPONSE_ID: Id = "singleton"
-  val UNPARSED_SINGLETON: UnparsedVacationResponseId = "singleton"
-
-  type UnparsedVacationResponseId = String Refined IdConstraint
+  val UNPARSED_SINGLETON: UnparsedVacationResponseId = UnparsedVacationResponseId("singleton")
 
   def asRfc8621(vacation: Vacation) = VacationResponse(
     id = VacationResponseId(),
@@ -58,6 +55,8 @@ object VacationResponse {
   def propertiesFiltered(requestedProperties: Properties) : Properties = idProperty ++ requestedProperties
 }
 
+case class UnparsedVacationResponseId(id: Id)
+
 case class VacationResponse(id: VacationResponseId,
                            isEnabled: IsEnabled,
                            fromDate: Option[FromDate],
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseGet.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseGet.scala
index 8d07bef..ff2bf58 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseGet.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/vacation/VacationResponseGet.scala
@@ -21,7 +21,6 @@ package org.apache.james.jmap.vacation
 
 import org.apache.james.jmap.core.{AccountId, Properties, State}
 import org.apache.james.jmap.method.WithAccountId
-import org.apache.james.jmap.vacation.VacationResponse.UnparsedVacationResponseId
 
 case class VacationResponseIds(value: List[UnparsedVacationResponseId])
 
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 62c4e92..e1a1b77 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
@@ -27,8 +27,7 @@ import org.apache.james.jmap.core.{AccountId, Properties}
 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.MailboxGet.UnparsedMailboxId
-import org.apache.james.jmap.mail.{Ids, Mailbox, MailboxGetRequest, MailboxGetResponse, NotFound}
+import org.apache.james.jmap.mail.{Ids, Mailbox, MailboxGetRequest, MailboxGetResponse, NotFound, UnparsedMailboxId}
 import org.apache.james.mailbox.model.{MailboxId, TestId}
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
@@ -41,8 +40,8 @@ object MailboxGetSerializationTest {
 
   private val ACCOUNT_ID: AccountId = AccountId(id)
 
-  private val MAILBOX_ID_1: UnparsedMailboxId = "1"
-  private val MAILBOX_ID_2: UnparsedMailboxId = "2"
+  private val MAILBOX_ID_1: UnparsedMailboxId = UnparsedMailboxId("1")
+  private val MAILBOX_ID_2: UnparsedMailboxId = UnparsedMailboxId("2")
 
   private val PROPERTIES: Properties = Properties("name", "role")
 }
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 b374d94..e168899 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
@@ -26,8 +26,7 @@ import org.apache.james.jmap.core.{AccountId, Properties, State}
 import org.apache.james.jmap.json.Fixture.id
 import org.apache.james.jmap.json.VacationResponseGetSerializationTest.{ACCOUNT_ID, PROPERTIES, SINGLETON_ID}
 import org.apache.james.jmap.json.VacationResponseSerializationTest.VACATION_RESPONSE
-import org.apache.james.jmap.vacation.VacationResponse.UnparsedVacationResponseId
-import org.apache.james.jmap.vacation.{VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseIds, VacationResponseNotFound}
+import org.apache.james.jmap.vacation.{UnparsedVacationResponseId, VacationResponse, VacationResponseGetRequest, VacationResponseGetResponse, VacationResponseIds, VacationResponseNotFound}
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 import play.api.libs.json.{JsSuccess, Json}
@@ -35,7 +34,7 @@ import play.api.libs.json.{JsSuccess, Json}
 object VacationResponseGetSerializationTest {
   private val ACCOUNT_ID: AccountId = AccountId(id)
 
-  private val SINGLETON_ID: UnparsedVacationResponseId = "singleton"
+  private val SINGLETON_ID: UnparsedVacationResponseId = UnparsedVacationResponseId("singleton")
   private val PROPERTIES: Properties = Properties("isEnabled", "fromDate")
 }
 
@@ -44,7 +43,7 @@ class VacationResponseGetSerializationTest extends AnyWordSpec with Matchers {
     "succeed on invalid VacationResponseId" in {
       val expectedRequestObject = VacationResponseGetRequest(
         accountId = ACCOUNT_ID,
-        ids = Some(VacationResponseIds(List("invalid"))),
+        ids = Some(VacationResponseIds(List(UnparsedVacationResponseId("invalid")))),
         properties = None)
 
       VacationSerializer.deserializeVacationResponseGetRequest(
@@ -136,7 +135,7 @@ class VacationResponseGetSerializationTest extends AnyWordSpec with Matchers {
     "succeed when multiple ids" in {
       val expectedRequestObject = VacationResponseGetRequest(
         accountId = ACCOUNT_ID,
-        ids = Some(VacationResponseIds(List(SINGLETON_ID, "randomId"))),
+        ids = Some(VacationResponseIds(List(SINGLETON_ID, UnparsedVacationResponseId("randomId")))),
         properties = Some(PROPERTIES))
 
       VacationSerializer.deserializeVacationResponseGetRequest(
@@ -156,7 +155,7 @@ class VacationResponseGetSerializationTest extends AnyWordSpec with Matchers {
         accountId = ACCOUNT_ID,
         state = State.INSTANCE,
         list = List(VACATION_RESPONSE),
-        notFound = VacationResponseNotFound(Set("randomId1", "randomId2")))
+        notFound = VacationResponseNotFound(Set(UnparsedVacationResponseId("randomId1"), UnparsedVacationResponseId("randomId2"))))
 
       val expectedJson: String =
         s"""

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