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

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

commit c01bf3a985407595fbfb2d50329718e47abf68c0
Author: Rene Cordier <[email protected]>
AuthorDate: Fri May 8 17:45:35 2020 +0700

    JAMES-3091 JSON serialization for Mailbox POJO
---
 server/protocols/jmap-rfc-8621/pom.xml             |   5 +
 .../org/apache/james/jmap/json/Serializer.scala    |  73 ++++++++++-
 .../scala/org/apache/james/jmap/mail/Mailbox.scala |   2 +-
 .../scala/org/apache/james/jmap/mail/Quotas.scala  |  19 ++-
 .../james/jmap/json/MailboxSerializationTest.scala | 135 +++++++++++++++++++++
 .../org/apache/james/jmap/mail/MailboxTest.scala   |  12 +-
 6 files changed, 233 insertions(+), 13 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621/pom.xml 
b/server/protocols/jmap-rfc-8621/pom.xml
index bb6a83b..9f2d220 100644
--- a/server/protocols/jmap-rfc-8621/pom.xml
+++ b/server/protocols/jmap-rfc-8621/pom.xml
@@ -78,6 +78,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>net.javacrumbs.json-unit</groupId>
+            <artifactId>json-unit-assertj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
             <scope>test</scope>
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 173a82d..87937a1 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
@@ -22,11 +22,14 @@ package org.apache.james.jmap.json
 import java.io.InputStream
 import java.net.URL
 
-import org.apache.james.core.Username
+import org.apache.james.core.{Domain, Username}
+import org.apache.james.jmap.mail.{DelegatedNamespace, IsSubscribed, Mailbox, 
MailboxNamespace, MailboxRights, MayAddItems, MayCreateChild, MayDelete, 
MayReadItems, MayRemoveItems, MayRename, MaySetKeywords, MaySetSeen, MaySubmit, 
PersonalNamespace, Quota, QuotaId, QuotaRoot, Quotas, Right, Rights, SortOrder, 
TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, Value}
 import org.apache.james.jmap.model
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodCallId, 
MethodName}
 import org.apache.james.jmap.model.{Account, Invocation, Session, _}
+import org.apache.james.mailbox.Role
+import org.apache.james.mailbox.model.{MailboxACL, MailboxId}
 import play.api.libs.functional.syntax._
 import play.api.libs.json._
 
@@ -126,6 +129,70 @@ class Serializer {
 
   private implicit val sessionWrites: Writes[Session] = Json.writes[Session]
 
+  private implicit val mailboxIdWrites: Writes[MailboxId] = mailboxId => 
JsString(mailboxId.serialize)
+  private implicit val roleWrites: Writes[Role] = role => 
JsString(role.serialize)
+  private implicit val sortOrderWrites: Writes[SortOrder] = 
Json.valueWrites[SortOrder]
+  private implicit val totalEmailsWrites: Writes[TotalEmails] = 
Json.valueWrites[TotalEmails]
+  private implicit val unreadEmailsWrites: Writes[UnreadEmails] = 
Json.valueWrites[UnreadEmails]
+  private implicit val totalThreadsWrites: Writes[TotalThreads] = 
Json.valueWrites[TotalThreads]
+  private implicit val unreadThreadsWrites: Writes[UnreadThreads] = 
Json.valueWrites[UnreadThreads]
+  private implicit val isSubscribedWrites: Writes[IsSubscribed] = 
Json.valueWrites[IsSubscribed]
+
+  private implicit val mayReadItemsWrites: Writes[MayReadItems] = 
Json.valueWrites[MayReadItems]
+  private implicit val mayAddItemsWrites: Writes[MayAddItems] = 
Json.valueWrites[MayAddItems]
+  private implicit val mayRemoveItemsWrites: Writes[MayRemoveItems] = 
Json.valueWrites[MayRemoveItems]
+  private implicit val maySetSeenWrites: Writes[MaySetSeen] = 
Json.valueWrites[MaySetSeen]
+  private implicit val maySetKeywordsWrites: Writes[MaySetKeywords] = 
Json.valueWrites[MaySetKeywords]
+  private implicit val mayCreateChildWrites: Writes[MayCreateChild] = 
Json.valueWrites[MayCreateChild]
+  private implicit val mayRenameWrites: Writes[MayRename] = 
Json.valueWrites[MayRename]
+  private implicit val mayDeleteWrites: Writes[MayDelete] = 
Json.valueWrites[MayDelete]
+  private implicit val maySubmitWrites: Writes[MaySubmit] = 
Json.valueWrites[MaySubmit]
+  private implicit val mailboxRightsWrites: Writes[MailboxRights] = 
Json.writes[MailboxRights]
+
+  private implicit val personalNamespaceWrites: Writes[PersonalNamespace] = 
namespace => JsString("Personal")
+  private implicit val delegatedNamespaceWrites: Writes[DelegatedNamespace] = 
namespace => JsString(s"Delegated[${namespace.owner.asString}]")
+  private implicit val mailboxNamespaceWrites: Writes[MailboxNamespace] = 
Json.writes[MailboxNamespace]
+
+  private implicit val mailboxACLWrites: Writes[MailboxACL.Right] = right => 
JsString(right.asCharacter.toString)
+
+  private implicit val rightWrites: Writes[Right] = Json.valueWrites[Right]
+  private implicit val rightsWrites: Writes[Rights] = Json.valueWrites[Rights]
+
+  private implicit def rightsMapWrites(implicit rightWriter: 
Writes[Seq[Right]]): Writes[Map[Username, Seq[Right]]] =
+    (m: Map[Username, Seq[Right]]) => {
+      m.foldLeft(JsObject.empty)((jsObject, kv) => {
+        val (username: Username, rights: Seq[Right]) = kv
+        jsObject.+(username.asString, rightWriter.writes(rights))
+      })
+    }
+
+  private implicit val domainWrites: Writes[Domain] = domain => 
JsString(domain.asString)
+  private implicit val quotaRootWrites: Writes[QuotaRoot] = 
Json.writes[QuotaRoot]
+  private implicit val quotaIdWrites: Writes[QuotaId] = 
Json.valueWrites[QuotaId]
+
+  private implicit val quotaValueWrites: Writes[Value] = Json.writes[Value]
+  private implicit val quotaWrites: Writes[Quota] = Json.valueWrites[Quota]
+
+  private implicit def quotaMapWrites(implicit valueWriter: Writes[Value]): 
Writes[Map[Quotas.Type, Value]] =
+    (m: Map[Quotas.Type, Value]) => {
+      m.foldLeft(JsObject.empty)((jsObject, kv) => {
+        val (quotaType: Quotas.Type, value: Value) = kv
+        jsObject.+(quotaType.toString, valueWriter.writes(value))
+      })
+    }
+
+  private implicit val quotasWrites: Writes[Quotas] = Json.valueWrites[Quotas]
+
+  private implicit def quotasMapWrites(implicit quotaWriter: Writes[Quota]): 
Writes[Map[QuotaId, Quota]] =
+    (m: Map[QuotaId, Quota]) => {
+      m.foldLeft(JsObject.empty)((jsObject, kv) => {
+        val (quotaId: QuotaId, quota: Quota) = kv
+        jsObject.+(quotaId.getName, quotaWriter.writes(quota))
+      })
+    }
+
+  private implicit val mailboxWrites: Writes[Mailbox] = Json.writes[Mailbox]
+
   def serialize(session: Session): JsValue = {
     Json.toJson(session)
   }
@@ -138,6 +205,10 @@ class Serializer {
     Json.toJson(responseObject)
   }
 
+  def serialize(mailbox: Mailbox): JsValue = {
+    Json.toJson(mailbox)
+  }
+
   def deserializeRequestObject(input: String): JsResult[RequestObject] = {
     Json.parse(input).validate[RequestObject]
   }
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
index 09f881f..aaff40e 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Mailbox.scala
@@ -56,7 +56,7 @@ object MailboxNamespace {
 
 sealed trait MailboxNamespace
 
-case object PersonalNamespace extends MailboxNamespace
+case class PersonalNamespace() extends MailboxNamespace
 
 case class DelegatedNamespace(owner: Username) extends MailboxNamespace
 
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Quotas.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Quotas.scala
index be64e75..c9d4212 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Quotas.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Quotas.scala
@@ -19,10 +19,19 @@
 
 package org.apache.james.jmap.mail
 
-import java.util.Optional
-
+import org.apache.james.core.Domain
 import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
-import org.apache.james.mailbox.model.QuotaRoot
+import org.apache.james.mailbox.model.{QuotaRoot => JavaQuotaRoot}
+
+import scala.compat.java8.OptionConverters._
+
+object QuotaRoot{
+  def fromJava(quotaRoot: JavaQuotaRoot) = QuotaRoot(quotaRoot.getValue, 
quotaRoot.getDomain.asScala)
+}
+
+case class QuotaRoot(value: String, domain: Option[Domain]) {
+  def asJava: JavaQuotaRoot = JavaQuotaRoot.quotaRoot(value, domain.asJava)
+}
 
 object Quotas {
   sealed trait Type
@@ -39,11 +48,11 @@ object QuotaId {
 }
 
 case class QuotaId(quotaRoot: QuotaRoot) extends AnyVal {
-  def getName: String = quotaRoot.getValue
+  def getName: String = quotaRoot.value
 }
 
 case class Quota(quota: Map[Quotas.Type, Value]) extends AnyVal
 
-case class Value(used: UnsignedInt, max: Optional[UnsignedInt])
+case class Value(used: UnsignedInt, max: Option[UnsignedInt])
 
 case class Quotas(quotas: Map[QuotaId, Quota]) extends AnyVal
\ No newline at end of file
diff --git 
a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxSerializationTest.scala
 
b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxSerializationTest.scala
new file mode 100644
index 0000000..5ab9690
--- /dev/null
+++ 
b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/json/MailboxSerializationTest.scala
@@ -0,0 +1,135 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.json
+
+import eu.timepit.refined.auto._
+import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
+import org.apache.james.core.{Domain, Username}
+import org.apache.james.jmap.json.MailboxSerializationTest.MAILBOX
+import org.apache.james.jmap.mail.Mailbox.MailboxName
+import org.apache.james.jmap.mail.{IsSubscribed, Mailbox, MailboxNamespace, 
MailboxRights, MayAddItems, MayCreateChild, MayDelete, MayReadItems, 
MayRemoveItems, MayRename, MaySetKeywords, MaySetSeen, MaySubmit, 
PersonalNamespace, Quota, QuotaId, QuotaRoot, Quotas, Right, Rights, SortOrder, 
TotalEmails, TotalThreads, UnreadEmails, UnreadThreads, Value}
+import org.apache.james.mailbox.Role
+import org.apache.james.mailbox.model.{MailboxId, TestId}
+import org.scalatest.matchers.should.Matchers
+import org.scalatest.wordspec.AnyWordSpec
+import play.api.libs.json.Json
+
+object MailboxSerializationTest {
+  private val MAILBOX_ID: MailboxId = TestId.of(2)
+  private val MAILBOX_NAME: MailboxName = "inbox"
+  private val PARENT_ID: Option[MailboxId] = Option.apply(TestId.of(1))
+  private val ROLE: Option[Role] = Option.apply(Role.INBOX)
+  private val SORT_ORDER: SortOrder = SortOrder(10L)
+  private val TOTAL_EMAILS: TotalEmails = TotalEmails(1234L)
+  private val UNREAD_EMAILS: UnreadEmails = UnreadEmails(123L)
+  private val TOTAL_THREADS: TotalThreads = TotalThreads(58L)
+  private val UNREAD_THREADS: UnreadThreads = UnreadThreads(22L)
+  private val IS_SUBSCRIBED: IsSubscribed = IsSubscribed(true)
+  private val NAMESPACE: MailboxNamespace = PersonalNamespace()
+
+  private val MY_RIGHTS: MailboxRights = MailboxRights(
+    mayAddItems = MayAddItems(true),
+    mayReadItems = MayReadItems(false),
+    mayRemoveItems = MayRemoveItems(false),
+    maySetSeen = MaySetSeen(true),
+    maySetKeywords = MaySetKeywords(false),
+    mayCreateChild = MayCreateChild(true),
+    mayRename = MayRename(true),
+    mayDelete = MayDelete(false),
+    maySubmit = MaySubmit(false)
+  )
+
+  private val RIGHTS: Rights = Rights.of(Username.of("bob"), 
Seq(Right.Expunge, Right.Lookup))
+    .append(Username.of("alice"), Seq(Right.Read, Right.Write))
+
+  private val QUOTAS = Quotas(Map(
+    QuotaId(QuotaRoot("quotaRoot", None)) -> Quota(Map(
+      Quotas.Message -> Value(18L, Some(42L)),
+      Quotas.Storage -> Value(12L, None))),
+    QuotaId(QuotaRoot("quotaRoot2@localhost", Some(Domain.LOCALHOST))) -> 
Quota(Map(
+      Quotas.Message -> Value(14L, Some(43L)),
+      Quotas.Storage -> Value(19L, None)))))
+
+  private val MAILBOX: Mailbox = Mailbox(
+    id = MAILBOX_ID,
+    name = MAILBOX_NAME,
+    parentId = PARENT_ID,
+    role = ROLE,
+    sortOrder = SORT_ORDER,
+    totalEmails = TOTAL_EMAILS,
+    unreadEmails = UNREAD_EMAILS,
+    totalThreads = TOTAL_THREADS,
+    unreadThreads = UNREAD_THREADS,
+    myRights = MY_RIGHTS,
+    isSubscribed = IS_SUBSCRIBED,
+    namespace = NAMESPACE,
+    rights = RIGHTS,
+    quotas = QUOTAS
+  )
+}
+
+class MailboxSerializationTest extends AnyWordSpec with Matchers {
+  "Serialize Mailbox" should {
+    "succeed " in {
+
+      val expectedJson = Json.parse(
+        """{
+          |  "id":"2",
+          |  "name":"inbox",
+          |  "parentId":"1",
+          |  "role":"inbox",
+          |  "sortOrder":10,
+          |  "totalEmails":1234,
+          |  "unreadEmails":123,
+          |  "totalThreads":58,
+          |  "unreadThreads":22,
+          |  "myRights":{
+          |    "mayReadItems":false,
+          |    "mayAddItems":true,
+          |    "mayRemoveItems":false,
+          |    "maySetSeen":true,
+          |    "maySetKeywords":false,
+          |    "mayCreateChild":true,
+          |    "mayRename":true,
+          |    "mayDelete":false,
+          |    "maySubmit":false
+          |  },
+          |  "isSubscribed":true,
+          |  "namespace":"Personal",
+          |  "rights":{
+          |    "bob":["e","l"],
+          |    "alice":["r","w"]
+          |  },
+          |  "quotas":{
+          |    "quotaRoot":{
+          |      "Message":{"used":18,"max":42},
+          |      "Storage":{"used":12}
+          |    },
+          |    "quotaRoot2@localhost":{
+          |      "Message":{"used":14,"max":43},
+          |      "Storage":{"used":19}
+          |    }
+          |  }
+          |}""".stripMargin)
+
+      assertThatJson(new 
Serializer().serialize(MAILBOX)).isEqualTo(expectedJson)
+    }
+  }
+}
diff --git 
a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/mail/MailboxTest.scala
 
b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/mail/MailboxTest.scala
index 018c2bb..47e8c95 100644
--- 
a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/mail/MailboxTest.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/mail/MailboxTest.scala
@@ -58,7 +58,7 @@ class MailboxTest extends AnyWordSpec with Matchers {
           MayDelete(true),
           MaySubmit(true)),
         IsSubscribed(true),
-        MailboxNamespace.personal,
+        MailboxNamespace.personal(),
         Rights.EMPTY,
         Quotas(Map()))
         .hasRole(Role.INBOX) must be(false)
@@ -84,7 +84,7 @@ class MailboxTest extends AnyWordSpec with Matchers {
           MayDelete(true),
           MaySubmit(true)),
         IsSubscribed(true),
-        MailboxNamespace.personal,
+        MailboxNamespace.personal(),
         Rights.EMPTY,
         Quotas(Map()))
         .hasRole(Role.INBOX) must be(false)
@@ -110,7 +110,7 @@ class MailboxTest extends AnyWordSpec with Matchers {
           MayDelete(true),
           MaySubmit(true)),
         IsSubscribed(true),
-        MailboxNamespace.personal,
+        MailboxNamespace.personal(),
         Rights.EMPTY,
         Quotas(Map())).hasRole(Role.INBOX) must be(true)
     }
@@ -138,7 +138,7 @@ class MailboxTest extends AnyWordSpec with Matchers {
           MayDelete(true),
           MaySubmit(true)),
         IsSubscribed(true),
-        MailboxNamespace.personal,
+        MailboxNamespace.personal(),
         Rights.EMPTY,
         Quotas(Map()))
         .hasSystemRole must be(false)
@@ -164,7 +164,7 @@ class MailboxTest extends AnyWordSpec with Matchers {
           MayDelete(true),
           MaySubmit(true)),
         IsSubscribed(true),
-        MailboxNamespace.personal,
+        MailboxNamespace.personal(),
         Rights.EMPTY,
         Quotas(Map())).hasSystemRole must be(false)
     }
@@ -189,7 +189,7 @@ class MailboxTest extends AnyWordSpec with Matchers {
           MayDelete(true),
           MaySubmit(true)),
         IsSubscribed(true),
-        MailboxNamespace.personal,
+        MailboxNamespace.personal(),
         Rights.EMPTY,
         Quotas(Map())).hasSystemRole must be(true)
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to