You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/04/13 02:53:32 UTC

[james-project] 05/13: JAMES-2891 AccountId should have its own type

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 3a53fafad2b6d6a7bf7039759f0c13611fa1ca1a
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Tue Mar 31 21:13:18 2020 +0700

    JAMES-2891 AccountId should have its own type
---
 .../org/apache/james/jmap/http/SessionRoutes.scala |  4 +-
 .../apache/james/jmap/http/SessionSupplier.scala   | 74 +++++++---------------
 .../org/apache/james/jmap/json/Serializer.scala    | 17 +++--
 .../org/apache/james/jmap/model/Capability.scala   |  4 +-
 .../org/apache/james/jmap/model/Invocation.scala   |  6 +-
 .../org/apache/james/jmap/model/Session.scala      | 54 ++++++++++++----
 .../apache/james/jmap/http/SessionRoutesTest.scala | 38 +----------
 .../james/jmap/http/SessionSupplierTest.scala      | 10 ++-
 .../james/jmap/json/SessionSerializationTest.scala | 36 ++++++-----
 9 files changed, 111 insertions(+), 132 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala
index 79bfb45..75a280b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionRoutes.scala
@@ -46,10 +46,10 @@ object SessionRoutes {
 @Inject
 class SessionRoutes(val authFilter: Authenticator,
                     val sessionSupplier: SessionSupplier = new SessionSupplier(),
-                    val serializer: Serializer = new Serializer) extends JMAPRoutes {
+                    val serializer: Serializer = new Serializer()) extends JMAPRoutes {
 
   val logger: Logger = SessionRoutes.LOGGER
-  val generateSession: BiFunction[HttpServerRequest, HttpServerResponse, Publisher[Void]] =
+  private val generateSession: BiFunction[HttpServerRequest, HttpServerResponse, Publisher[Void]] =
     (request, response) => SMono.fromPublisher(authFilter.authenticate(request))
       .map(_.getUser)
       .flatMap(sessionSupplier.generate)
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala
index c7fb076..231617a 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/http/SessionSupplier.scala
@@ -21,16 +21,12 @@ package org.apache.james.jmap.http
 
 import java.net.URL
 
-import com.google.common.annotations.VisibleForTesting
 import eu.timepit.refined.auto._
-import eu.timepit.refined.refineV
 import org.apache.james.core.Username
-import org.apache.james.jmap.http.SessionSupplier.{CORE_CAPABILITY, MAIL_CAPABILITY}
+import org.apache.james.jmap.http.SessionSupplier.{CORE_CAPABILITY, HARD_CODED_URL_PREFIX, MAIL_CAPABILITY}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.Id.Id
 import org.apache.james.jmap.model._
-import reactor.core.publisher.Mono
-import reactor.core.scala.publisher.{SFlux, SMono}
+import reactor.core.scala.publisher.SMono
 
 object SessionSupplier {
   private val CORE_CAPABILITY = CoreCapability(
@@ -53,56 +49,30 @@ object SessionSupplier {
       emailQuerySortOptions = List("receivedAt", "cc", "from", "to", "subject", "size", "sentAt", "hasKeyword", "uid", "Id"),
       MayCreateTopLevelMailbox(true)
     ))
+
+  private val HARD_CODED_URL_PREFIX = "http://this-url-is-hardcoded.org"
 }
 
 class SessionSupplier {
-  def generate(username: Username): SMono[Session] =
-    SMono.fromPublisher(
-      Mono.zip(
-        accounts(username).asJava(),
-        primaryAccounts(username).asJava()))
-      .map(tuple => generate(username, tuple.getT1, tuple.getT2))
-
-  private def accounts(username: Username): SMono[Map[Id, Account]] =
-    getId(username)
-      .map(id => Map(
-        id -> Account(
-          username,
-          IsPersonal(true),
-          IsReadOnly(false),
-          accountCapabilities = Set(CORE_CAPABILITY, MAIL_CAPABILITY))))
-
-  private def primaryAccounts(username: Username): SMono[Map[CapabilityIdentifier, Id]] =
-    SFlux.just(CORE_CAPABILITY, MAIL_CAPABILITY)
-      .flatMap(capability => getId(username)
-        .map(id => (capability.identifier, id)))
-      .collectMap(getIdentifier, getId)
-  private def getIdentifier(tuple : (CapabilityIdentifier, Id)): CapabilityIdentifier = tuple._1
-  private def getId(tuple : (CapabilityIdentifier, Id)): Id = tuple._2
-
-  private def getId(username: Username): SMono[Id] = {
-    SMono.fromCallable(() => refineId(username))
-      .flatMap {
-        case Left(errorMessage: String) => SMono.raiseError(new IllegalStateException(errorMessage))
-        case Right(id) => SMono.just(id)
-      }
+  def generate(username: Username): SMono[Session] = {
+    accounts(username)
+      .map(account => Session(
+        Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY),
+        List(account),
+        primaryAccounts(account.accountId),
+        username,
+        apiUrl = new URL(s"$HARD_CODED_URL_PREFIX/jmap"),
+        downloadUrl = new URL(s"$HARD_CODED_URL_PREFIX/download"),
+        uploadUrl = new URL(s"$HARD_CODED_URL_PREFIX/upload"),
+        eventSourceUrl = new URL(s"$HARD_CODED_URL_PREFIX/eventSource")))
   }
 
-  private def refineId(username: Username): Either[String, Id] = refineV(usernameHashCode(username))
-  @VisibleForTesting def usernameHashCode(username: Username) = username.asString().hashCode.toOctalString
+  private def accounts(username: Username): SMono[Account] = SMono.defer(() =>
+    Account.from(username, IsPersonal(true), IsReadOnly(false), Set(CORE_CAPABILITY, MAIL_CAPABILITY)) match {
+      case Left(ex: IllegalArgumentException) => SMono.raiseError(ex)
+      case Right(account: Account) => SMono.just(account)
+    })
 
-  private def generate(username: Username,
-                       accounts: Map[Id, Account],
-                       primaryAccounts: Map[CapabilityIdentifier, Id]): Session = {
-    Session(
-      Capabilities(CORE_CAPABILITY, MAIL_CAPABILITY),
-      accounts,
-      primaryAccounts,
-      username,
-      apiUrl = new URL("http://this-url-is-hardcoded.org/jmap"),
-      downloadUrl = new URL("http://this-url-is-hardcoded.org/download"),
-      uploadUrl = new URL("http://this-url-is-hardcoded.org/upload"),
-      eventSourceUrl = new URL("http://this-url-is-hardcoded.org/eventSource"),
-      state = "000001")
-  }
+  private def primaryAccounts(accountId: AccountId): Map[CapabilityIdentifier, AccountId] =
+    Map(CORE_CAPABILITY.identifier -> accountId, MAIL_CAPABILITY.identifier -> accountId)
 }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
index ebf9985..2189af9 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/Serializer.scala
@@ -24,7 +24,6 @@ import java.net.URL
 import org.apache.james.core.Username
 import org.apache.james.jmap.model
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
-import org.apache.james.jmap.model.Id.Id
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, MethodName}
 import org.apache.james.jmap.model.{Account, Invocation, Session, _}
 import play.api.libs.functional.syntax._
@@ -103,17 +102,27 @@ class Serializer {
 
   private implicit val capabilitiesWrites: Writes[Capabilities] = capabilities => setCapabilityWrites.writes(Set(capabilities.coreCapability, capabilities.mailCapability))
 
-  private implicit def identifierMapWrite[Any](implicit idWriter: Writes[Id]): Writes[Map[CapabilityIdentifier, Any]] =
+  private implicit val accountIdWrites: Format[AccountId] = Json.valueFormat[AccountId]
+  private implicit def identifierMapWrite[Any](implicit idWriter: Writes[AccountId]): Writes[Map[CapabilityIdentifier, Any]] =
     (m: Map[CapabilityIdentifier, Any]) => {
       m.foldLeft(JsObject.empty)((jsObject, kv) => {
-        val (identifier: CapabilityIdentifier, id: Id) = kv
+        val (identifier: CapabilityIdentifier, id: AccountId) = kv
         jsObject.+(identifier.value, idWriter.writes(id))
       })
     }
 
   private implicit val isPersonalFormat: Format[IsPersonal] = Json.valueFormat[IsPersonal]
   private implicit val isReadOnlyFormat: Format[IsReadOnly] = Json.valueFormat[IsReadOnly]
-  private implicit val accountWrites: Writes[Account] = Json.writes[Account]
+  private implicit val accountWrites: Writes[Account] = (
+      (JsPath \ Account.NAME).write[Username] and
+      (JsPath \ Account.IS_PERSONAL).write[IsPersonal] and
+      (JsPath \ Account.IS_READ_ONLY).write[IsReadOnly] and
+      (JsPath \ Account.ACCOUNT_CAPABILITIES).write[Set[_ <: Capability]]
+    ) (unlift(Account.unapplyIgnoreAccountId))
+
+  private implicit def accountListWrites(implicit accountWrites: Writes[Account]): Writes[List[Account]] =
+    (list: List[Account]) => JsObject(list.map(account => (account.accountId.id.value, accountWrites.writes(account))))
+
   private implicit val sessionWrites: Writes[Session] = Json.writes[Session]
 
   def serialize(session: Session): JsValue = {
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala
index 8e35c7d..17517fe 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Capability.scala
@@ -30,8 +30,8 @@ import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
 
 object CapabilityIdentifier {
   type CapabilityIdentifier = String Refined Uri
-  val JMAP_CORE: CapabilityIdentifier = "urn:ietf:params:jmap:core"
-  val JMAP_MAIL: CapabilityIdentifier = "urn:ietf:params:jmap:mail"
+  private[model] val JMAP_CORE: CapabilityIdentifier = "urn:ietf:params:jmap:core"
+  private[model] val JMAP_MAIL: CapabilityIdentifier = "urn:ietf:params:jmap:mail"
 }
 
 sealed trait CapabilityProperties
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala
index 7ad8e5c..c46d95b 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Invocation.scala
@@ -25,9 +25,9 @@ import play.api.libs.json._
 case class Invocation(methodName: MethodName, arguments: Arguments, methodCallId: MethodCallId)
 
 object Invocation {
-  val METHOD_NAME: Int = 0
-  val ARGUMENTS: Int = 1
-  val METHOD_CALL: Int = 2
+  private[jmap] val METHOD_NAME: Int = 0
+  private[jmap] val ARGUMENTS: Int = 1
+  private[jmap] val METHOD_CALL: Int = 2
 
   case class MethodName(value: NonEmptyString)
   case class Arguments(value: JsObject) extends AnyVal
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Session.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Session.scala
index 5d85fca..0bb733d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Session.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/model/Session.scala
@@ -20,44 +20,76 @@
 package org.apache.james.jmap.model
 
 import java.net.URL
+import java.nio.charset.StandardCharsets
 
+import com.google.common.hash.Hashing
 import eu.timepit.refined.api.Refined
+import eu.timepit.refined.auto._
 import eu.timepit.refined.collection.NonEmpty
+import eu.timepit.refined.refineV
 import org.apache.james.core.Username
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.Id.Id
-import org.apache.james.jmap.model.State.State
+import org.apache.james.jmap.model.State.{INSTANCE, State}
 
 case class IsPersonal(value: Boolean)
 case class IsReadOnly(value: Boolean)
 
-object Account {
-  def apply(name: Username,
-            isPersonal: IsPersonal,
-            isReadOnly: IsReadOnly,
-            accountCapabilities: Set[_ <: Capability]): Account = {
+object AccountId {
+  def from(username: Username): Either[IllegalArgumentException, AccountId] = {
+    val sha256String = Hashing.sha256()
+      .hashString(username.asString(), StandardCharsets.UTF_8)
+      .toString
+    val refinedId: Either[String, Id] = refineV(sha256String)
 
-    new Account(name, isPersonal, isReadOnly, accountCapabilities)
+    refinedId match {
+      case Left(errorMessage: String) => Left(new IllegalArgumentException(errorMessage))
+      case Right(id) => Right(AccountId(id))
+    }
   }
 }
 
-final case class Account private(name: Username,
+final case class AccountId(id: Id)
+
+object Account {
+  private[jmap] val NAME = "name";
+  private[jmap] val IS_PERSONAL = "isPersonal"
+  private[jmap] val IS_READ_ONLY = "isReadOnly"
+  private[jmap] val ACCOUNT_CAPABILITIES = "accountCapabilities"
+
+  def from(name: Username,
+           isPersonal: IsPersonal,
+           isReadOnly: IsReadOnly,
+           accountCapabilities: Set[_ <: Capability]): Either[IllegalArgumentException, Account] =
+    AccountId.from(name) match {
+      case Left(ex: IllegalArgumentException) => Left(ex)
+      case Right(accountId) => Right(new Account(accountId, name, isPersonal, isReadOnly, accountCapabilities))
+    }
+
+  def unapplyIgnoreAccountId(account: Account): Some[(Username, IsPersonal, IsReadOnly, Set[_ <: Capability])] =
+    Some(account.name, account.isPersonal, account.isReadOnly, account.accountCapabilities)
+}
+
+final case class Account private(accountId: AccountId,
+                                 name: Username,
                                  isPersonal: IsPersonal,
                                  isReadOnly: IsReadOnly,
                                  accountCapabilities: Set[_ <: Capability])
 
 object State {
+  private[model] val INSTANCE: State = "000001"
+
   type State = String Refined NonEmpty
 }
 
 case class Capabilities(coreCapability: CoreCapability, mailCapability: MailCapability)
 
 final case class Session(capabilities: Capabilities,
-                         accounts: Map[Id, Account],
-                         primaryAccounts: Map[CapabilityIdentifier, Id],
+                         accounts: List[Account],
+                         primaryAccounts: Map[CapabilityIdentifier, AccountId],
                          username: Username,
                          apiUrl: URL,
                          downloadUrl: URL,
                          uploadUrl: URL,
                          eventSourceUrl: URL,
-                         state: State)
+                         state: State = INSTANCE)
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala
index 6ff561d..a7b3e9c 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionRoutesTest.scala
@@ -31,7 +31,6 @@ import org.apache.james.core.Username
 import org.apache.james.jmap.http.SessionRoutesTest.{BOB, TEST_CONFIGURATION}
 import org.apache.james.jmap.{JMAPConfiguration, JMAPRoutes, JMAPServer}
 import org.apache.james.mailbox.MailboxSession
-import org.hamcrest.CoreMatchers.is
 import org.mockito.ArgumentMatchers.any
 import org.mockito.Mockito._
 import org.scalatest.BeforeAndAfter
@@ -120,7 +119,7 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
                          |    }
                          |  },
                          |  "accounts" : {
-                         |    "25742733157" : {
+                         |    "0fe275bf13ff761407c17f64b1dfae2f4b3186feea223d7267b79f873a105401" : {
                          |      "name" : "bob@james.org",
                          |      "isPersonal" : true,
                          |      "isReadOnly" : false,
@@ -147,8 +146,8 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
                          |    }
                          |  },
                          |  "primaryAccounts" : {
-                         |    "urn:ietf:params:jmap:core" : "25742733157",
-                         |    "urn:ietf:params:jmap:mail" : "25742733157"
+                         |    "urn:ietf:params:jmap:core" : "0fe275bf13ff761407c17f64b1dfae2f4b3186feea223d7267b79f873a105401",
+                         |    "urn:ietf:params:jmap:mail" : "0fe275bf13ff761407c17f64b1dfae2f4b3186feea223d7267b79f873a105401"
                          |  },
                          |  "username" : "bob@james.org",
                          |  "apiUrl" : "http://this-url-is-hardcoded.org/jmap",
@@ -160,35 +159,4 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
 
     Json.parse(sessionJson) should equal(Json.parse(expectedJson))
   }
-
-  "get" should "return 500 when unexpected Id serialization" in {
-    when(sessionSupplier.usernameHashCode(BOB))
-      .thenReturn("INVALID_JMAP_ID_()*&*$(#*")
-
-    RestAssured.when()
-        .get
-      .then
-        .statusCode(HttpStatus.SC_INTERNAL_SERVER_ERROR)
-  }
-
-  "get" should "return empty content type when unexpected Id serialization" in {
-    when(sessionSupplier.usernameHashCode(BOB))
-      .thenReturn("INVALID_JMAP_ID_()*&*$(#*")
-
-    RestAssured.when()
-        .get
-      .then
-        .contentType(is(""))
-  }
-
-  "get" should "return empty body when unexpected Id serialization" in {
-    when(sessionSupplier.usernameHashCode(BOB))
-      .thenReturn("INVALID_JMAP_ID_()*&*$(#*")
-
-    RestAssured.`with`()
-        .get
-      .thenReturn()
-        .getBody
-        .asString() should equal("")
-  }
 }
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala
index 1b2bb0b..11bd415 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/http/SessionSupplierTest.scala
@@ -19,10 +19,8 @@
 
 package org.apache.james.jmap.http
 
-import eu.timepit.refined.auto._
 import org.apache.james.core.Username
 import org.apache.james.jmap.http.SessionSupplierTest.USERNAME
-import org.apache.james.jmap.model.Id.Id
 import org.scalatest.matchers.should.Matchers
 import org.scalatest.wordspec.AnyWordSpec
 
@@ -45,12 +43,12 @@ class SessionSupplierTest extends AnyWordSpec with Matchers {
       }
 
       "has name" in {
-        accounts.view.mapValues(_.name).values.toList should equal(List(USERNAME))
+        accounts.map(_.name) should equal(List(USERNAME))
       }
 
-      "has id" in {
-        val usernameHashCode: Id = "22267206120"
-        accounts.keys.toList should equal(List(usernameHashCode))
+      "has accountId being hash of username in string" in {
+        accounts.map(_.accountId)
+          .map(_.id.value) should equal(List("0cb33e029628ea603d1b988f0f81b069d89b6c5a093e12b275ecdc626bd7458c"))
       }
     }
   }
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala
index 8f6f228..98e3f81 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/SessionSerializationTest.scala
@@ -50,9 +50,7 @@ object SessionSerializationTest {
   private val MAX_OBJECTS_IN_SET : MaxObjectsInSet = MaxObjectsInSet(128L)
   private val COLLATION_ALGORITHMS : List[CollationAlgorithm] = List(ALGO_1, ALGO_2, ALGO_3)
   private val USER_1 = Username.of("user1@james.org")
-  private val USER_1_ID: Id = "user1Id"
   private val USER_2 = Username.of("user2@james.org")
-  private val USER_2_ID: Id = "user2Id"
   private val URL = new URL("http://james.org")
   private val STATE : State = "fda9342jcm"
 
@@ -87,28 +85,32 @@ object SessionSerializationTest {
   private val IS_NOT_PERSONAL : IsPersonal = IsPersonal(false)
   private val IS_NOT_READ_ONLY : IsReadOnly = IsReadOnly(false)
 
-  private val ACCOUNT_1 = Account(
+  private val ACCOUNT_1: Account = Account.from(
     name = USER_1,
     isPersonal = IS_PERSONAL,
     isReadOnly = IS_NOT_READ_ONLY,
-    accountCapabilities = Set(CORE_CAPABILITY))
-  private val ACCOUNT_2 = Account(
+    accountCapabilities = Set(CORE_CAPABILITY)) match {
+      case Left(ex: IllegalArgumentException) => throw ex
+      case Right(account: Account) => account
+    }
+
+  private val ACCOUNT_2: Account = Account.from(
     name = USER_2,
     isPersonal = IS_NOT_PERSONAL,
     isReadOnly = IS_NOT_READ_ONLY,
-    accountCapabilities = Set(CORE_CAPABILITY))
-  private val ACCOUNTS = Map(
-    USER_1_ID -> ACCOUNT_1,
-    USER_2_ID -> ACCOUNT_2,
-  )
+    accountCapabilities = Set(CORE_CAPABILITY))  match {
+      case Left(ex: IllegalArgumentException) => throw ex
+      case Right(account: Account) => account
+    }
+
   private val PRIMARY_ACCOUNTS = Map(
-    MAIL_IDENTIFIER -> USER_1_ID,
-    CONTACT_IDENTIFIER -> USER_2_ID
+    MAIL_IDENTIFIER -> ACCOUNT_1.accountId,
+    CONTACT_IDENTIFIER -> ACCOUNT_1.accountId
   )
 
   private val SESSION = Session(
     capabilities = CAPABILITIES,
-    accounts = ACCOUNTS,
+    accounts = List(ACCOUNT_1, ACCOUNT_2),
     primaryAccounts = PRIMARY_ACCOUNTS,
     username = USER_1,
     apiUrl = URL,
@@ -155,7 +157,7 @@ class SessionSerializationTest extends AnyWordSpec with Matchers {
           |    }
           |  },
           |  "accounts": {
-          |    "user1Id": {
+          |    "807a5306ccb4527af7790a0f9b48a776514bdbfba064e355461a76bcffbf2c90": {
           |      "name": "user1@james.org",
           |      "isPersonal": true,
           |      "isReadOnly": false,
@@ -176,7 +178,7 @@ class SessionSerializationTest extends AnyWordSpec with Matchers {
           |        }
           |      }
           |    },
-          |    "user2Id": {
+          |    "a9b46834e106ff73268a40a34ffba9fcfeee8bdb601939d1a96ef9199dc2695c": {
           |      "name": "user2@james.org",
           |      "isPersonal": false,
           |      "isReadOnly": false,
@@ -199,8 +201,8 @@ class SessionSerializationTest extends AnyWordSpec with Matchers {
           |    }
           |  },
           |  "primaryAccounts": {
-          |    "urn:ietf:params:jmap:mail": "user1Id",
-          |    "urn:ietf:params:jmap:contact": "user2Id"
+          |    "urn:ietf:params:jmap:mail": "807a5306ccb4527af7790a0f9b48a776514bdbfba064e355461a76bcffbf2c90",
+          |    "urn:ietf:params:jmap:contact": "807a5306ccb4527af7790a0f9b48a776514bdbfba064e355461a76bcffbf2c90"
           |  },
           |  "username": "user1@james.org",
           |  "apiUrl": "http://james.org",


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