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 rc...@apache.org on 2020/07/17 10:24:15 UTC

[james-project] 06/09: JAMES-3098 filter properties from response

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 9b1c3888d5d6765b2aa8547c00a3e1c42b03aa5e
Author: RĂ©mi Kowalski <rk...@linagora.com>
AuthorDate: Wed Jul 15 16:34:24 2020 +0200

    JAMES-3098 filter properties from response
---
 .../contract/MailboxGetMethodContract.scala        | 128 +++++++++++++++++++--
 .../org/apache/james/jmap/json/Serializer.scala    |  19 +--
 .../scala/org/apache/james/jmap/mail/Mailbox.scala |  25 +++-
 .../james/jmap/method/MailboxGetMethod.scala       |   2 +-
 .../jmap/json/MailboxGetSerializationTest.scala    |   4 +-
 .../james/jmap/json/MailboxSerializationTest.scala |   3 +-
 6 files changed, 149 insertions(+), 32 deletions(-)

diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
index 3f51a4b..40471f3 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxGetMethodContract.scala
@@ -81,6 +81,21 @@ object MailboxGetMethodContract {
     |      "c1"]]
     |}""".stripMargin
 
+  private val GET_ALL_MAILBOXES_REQUEST_EMPTY_PROPERTIES: String =
+    """{
+    |  "using": [
+    |    "urn:ietf:params:jmap:core",
+    |    "urn:ietf:params:jmap:mail"],
+    |  "methodCalls": [[
+    |      "Mailbox/get",
+    |      {
+    |        "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+    |        "properties": [],
+    |        "ids": null
+    |      },
+    |      "c1"]]
+    |}""".stripMargin
+
   private val GET_ALL_MAILBOXES_REQUEST_NAME_AND_ID_PROPERTIES: String =
     """{
     |  "using": [
@@ -156,6 +171,22 @@ object MailboxGetMethodContract {
     |      "c1"]]
     |}""".stripMargin
 
+  private val GET_ALL_MAILBOXES_REQUEST_WITH_SHARES_WITH_ONLY_ID_NAME_AND_RIGHTS: String =
+    """{
+      |  "using": [
+      |    "urn:ietf:params:jmap:core",
+      |    "urn:ietf:params:jmap:mail",
+      |    "urn:apache:james:params:jmap:mail:shares"],
+      |  "methodCalls": [[
+      |      "Mailbox/get",
+      |      {
+      |        "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+      |        "properties": ["id", "name", "rights"],
+      |        "ids": null
+      |      },
+      |      "c1"]]
+      |}""".stripMargin
+
   private val GET_ALL_MAILBOXES_REQUEST_WITH_BOTH: String =
     """{
     |  "using": [
@@ -436,9 +467,9 @@ trait MailboxGetMethodContract {
     val response: String = `given`
       .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
       .body(GET_ALL_MAILBOXES_REQUEST)
-      .when
+    .when
       .post
-      .`then`
+    .`then`
       .statusCode(SC_OK)
       .contentType(JSON)
       .extract
@@ -491,9 +522,9 @@ trait MailboxGetMethodContract {
     val response: String = `given`
       .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
       .body(GET_ALL_MAILBOXES_REQUEST_NULL_PROPERTIES)
-      .when
+    .when
       .post
-      .`then`
+    .`then`
       .statusCode(SC_OK)
       .contentType(JSON)
       .extract
@@ -537,7 +568,42 @@ trait MailboxGetMethodContract {
          |}""".stripMargin)
   }
 
-  @Disabled("TODO")
+  @Test
+  def getMailboxesShouldReturnIdWhenNoPropertiesRequested(server: GuiceJamesServer): Unit = {
+    val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, "custom"))
+      .serialize
+
+    val response: String = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST_EMPTY_PROPERTIES)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |  "sessionState": "75128aab4b1b",
+         |  "methodResponses": [[
+         |    "Mailbox/get",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "state": "000001",
+         |      "list": [
+         |        {
+         |          "id": "${mailboxId}"
+         |        }
+         |      ],
+         |      "notFound": []
+         |    },
+         |    "c1"]]
+         |}""".stripMargin)
+  }
   @Test
   def getMailboxesShouldReturnOnlyNameAndIdWhenPropertiesRequested(server: GuiceJamesServer): Unit = {
     val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
@@ -547,9 +613,9 @@ trait MailboxGetMethodContract {
     val response: String = `given`
       .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
       .body(GET_ALL_MAILBOXES_REQUEST_NAME_AND_ID_PROPERTIES)
-      .when
+    .when
       .post
-      .`then`
+    .`then`
       .statusCode(SC_OK)
       .contentType(JSON)
       .extract
@@ -576,7 +642,6 @@ trait MailboxGetMethodContract {
          |}""".stripMargin)
   }
 
-  @Disabled("TODO")
   @Test
   def getMailboxesShouldAlwaysReturnIdEvenIfNotRequested(server: GuiceJamesServer): Unit = {
     val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
@@ -586,9 +651,9 @@ trait MailboxGetMethodContract {
     val response: String = `given`
       .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
       .body(GET_ALL_MAILBOXES_REQUEST_NAME_PROPERTIES)
-      .when
+    .when
       .post
-      .`then`
+    .`then`
       .statusCode(SC_OK)
       .contentType(JSON)
       .extract
@@ -617,6 +682,45 @@ trait MailboxGetMethodContract {
 
   @Disabled("TODO")
   @Test
+  def getMailboxesShouldNotIncludeNamespaceIfSharesCapabilityIsUsedAndNamespaceIsNotRequested(server: GuiceJamesServer): Unit = {
+    val mailboxId: String = server.getProbe(classOf[MailboxProbeImpl])
+      .createMailbox(MailboxPath.forUser(BOB, "custom"))
+      .serialize
+
+    val response: String = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(GET_ALL_MAILBOXES_REQUEST_WITH_SHARES_WITH_ONLY_ID_NAME_AND_RIGHTS)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response).isEqualTo(
+      s"""{
+         |  "sessionState": "75128aab4b1b",
+         |  "methodResponses": [[
+         |    "Mailbox/get",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "state": "000001",
+         |      "list": [
+         |        {
+         |          "id": "${mailboxId}",
+         |          "name": "custom",
+         |          "rights": {}
+         |        }
+         |      ],
+         |      "notFound": []
+         |    },
+         |    "c1"]]
+         |}""".stripMargin)
+  }
+
+  @Test
   def getMailboxesShouldReturnInvalidArgumentsErrorWhenInvalidProperty(server: GuiceJamesServer): Unit = {
     server.getProbe(classOf[MailboxProbeImpl])
       .createMailbox(MailboxPath.forUser(BOB, "custom"))
@@ -624,9 +728,9 @@ trait MailboxGetMethodContract {
     val response: String = `given`
       .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
       .body(GET_ALL_MAILBOXES_REQUEST_INVALID_PROPERTIES)
-      .when
+    .when
       .post
-      .`then`
+    .`then`
       .statusCode(SC_OK)
       .contentType(JSON)
       .extract
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 ad6b6b5..b31e0b5 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
@@ -201,8 +201,8 @@ class Serializer @Inject() (mailboxIdFactory: MailboxId.Factory) {
       })
     }
 
-  implicit def mailboxWrites(propertiesToHide: Set[String]): Writes[Mailbox] = Json.writes[Mailbox]
-    .transform((o: JsObject) => JsObject(o.fields.filterNot(entry => propertiesToHide.contains(entry._1))))
+  implicit def mailboxWrites(properties: Set[String]): Writes[Mailbox] = Json.writes[Mailbox]
+    .transform((o: JsObject) => JsObject(o.fields.filter(entry => properties.contains(entry._1))))
 
   private implicit val idsRead: Reads[Ids] = Json.valueReads[Ids]
   private implicit val propertiesRead: Reads[Properties] = Json.valueReads[Properties]
@@ -230,15 +230,8 @@ class Serializer @Inject() (mailboxIdFactory: MailboxId.Factory) {
       })
     }
 
-  private def mailboxWritesWithFilteredProperties(capabilities: Set[CapabilityIdentifier]): Writes[Mailbox] = {
-    val propertiesForCapabitilites: Map[CapabilityIdentifier, Set[String]] = Map(
-      CapabilityIdentifier.JAMES_QUOTA -> Set("quotas"),
-      CapabilityIdentifier.JAMES_SHARES -> Set("namespace", "rights")
-    )
-    val propertiesToHide = propertiesForCapabitilites.filterNot(entry => capabilities.contains(entry._1))
-      .flatMap(_._2)
-      .toSet
-    mailboxWrites(propertiesToHide)
+  private def mailboxWritesWithFilteredProperties(properties: Option[Properties], capabilities: Set[CapabilityIdentifier]): Writes[Mailbox] = {
+    mailboxWrites(Mailbox.propertiesFiltered(properties, capabilities))
   }
 
   private implicit def jsErrorWrites: Writes[JsError] = Json.writes[JsError]
@@ -253,8 +246,8 @@ class Serializer @Inject() (mailboxIdFactory: MailboxId.Factory) {
 
   def serialize(mailboxGetResponse: MailboxGetResponse)(implicit mailboxWrites: Writes[Mailbox]): JsValue = Json.toJson(mailboxGetResponse)
 
-  def serialize(mailboxGetResponse: MailboxGetResponse, capabilities: Set[CapabilityIdentifier]): JsValue = {
-    serialize(mailboxGetResponse)(mailboxWritesWithFilteredProperties(capabilities))
+  def serialize(mailboxGetResponse: MailboxGetResponse, properties: Option[Properties], capabilities: Set[CapabilityIdentifier]): JsValue = {
+    serialize(mailboxGetResponse)(mailboxWritesWithFilteredProperties(properties, capabilities))
   }
 
   def serialize(errors: JsError): JsValue = Json.toJson(errors)
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 9f3cd6a..23782bb 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
@@ -25,6 +25,8 @@ import eu.timepit.refined.auto._
 import eu.timepit.refined.collection.NonEmpty
 import org.apache.james.core.Username
 import org.apache.james.jmap.mail.MailboxName.MailboxName
+import org.apache.james.jmap.model.CapabilityIdentifier
+import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.UnsignedInt.UnsignedInt
 import org.apache.james.mailbox.Role
 import org.apache.james.mailbox.model.MailboxId
@@ -142,4 +144,25 @@ case class Mailbox(id: MailboxId,
   def hasRole(role: Role): Boolean = this.role.contains(role)
 
   val hasSystemRole: Boolean = role.exists(_.isSystemRole)
-}
\ No newline at end of file
+}
+
+object Mailbox {
+  def allProperties: Set[String] = Set("id", "name", "parentId", "role", "sortOrder", "totalEmails", "unreadEmails",
+    "totalThreads", "unreadThreads", "myRights", "isSubscribed", "namespace", "rights", "quotas")
+
+  def propertiesFiltered(requestedProperties: Option[Properties], allowedCapabilities : Set[CapabilityIdentifier]) : Set[String] = {
+    val propertiesForCapabilities: Map[CapabilityIdentifier, Set[String]] = Map(
+      CapabilityIdentifier.JAMES_QUOTA -> Set("quotas"),
+      CapabilityIdentifier.JAMES_SHARES -> Set("namespace", "rights")
+    )
+
+    val propertiesToHide = propertiesForCapabilities.filterNot(entry => allowedCapabilities.contains(entry._1))
+      .flatMap(_._2)
+      .toSet
+
+    requestedProperties match {
+      case None => allProperties -- propertiesToHide
+      case Some(requested) => (Set("id") ++ requested.value.map(_.toString()).toSet) -- propertiesToHide
+    }
+  }
+}
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 168b095..b48e7a6 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
@@ -66,7 +66,7 @@ class MailboxGetMethod @Inject() (serializer: Serializer,
             notFound = mailboxes.notFound))
           .map(mailboxGetResponse => Invocation(
             methodName = methodName,
-            arguments = Arguments(serializer.serialize(mailboxGetResponse, capabilities).as[JsObject]),
+            arguments = Arguments(serializer.serialize(mailboxGetResponse, mailboxGetRequest.properties, capabilities).as[JsObject]),
             methodCallId = invocation.methodCallId))))
   }
 
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 c2da125..aa22699 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
@@ -42,8 +42,6 @@ object MailboxGetSerializationTest {
   private val MAILBOX_ID_2: MailboxId = FACTORY.fromString("2")
 
   private val PROPERTIES: Properties = Properties(List("name", "role"))
-
-  private val NO_PROPERTY_FILTERING: Set[String] = Set.empty
 }
 
 class MailboxGetSerializationTest extends AnyWordSpec with Matchers {
@@ -207,7 +205,7 @@ class MailboxGetSerializationTest extends AnyWordSpec with Matchers {
           |}
           |""".stripMargin
 
-      assertThatJson(Json.stringify(SERIALIZER.serialize(actualValue)(SERIALIZER.mailboxWrites(NO_PROPERTY_FILTERING)))).isEqualTo(expectedJson)
+      assertThatJson(Json.stringify(SERIALIZER.serialize(actualValue)(SERIALIZER.mailboxWrites(Mailbox.allProperties)))).isEqualTo(expectedJson)
     }
   }
 }
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
index 99455a6..c3a6a1a 100644
--- 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
@@ -130,8 +130,7 @@ class MailboxSerializationTest extends AnyWordSpec with Matchers {
           |}""".stripMargin
 
       val serializer = new Serializer(new TestId.Factory)
-      val noPropertyFiltering: Set[String] = Set.empty
-      assertThatJson(Json.stringify(serializer.serialize(MAILBOX)(serializer.mailboxWrites(noPropertyFiltering)))).isEqualTo(expectedJson)
+      assertThatJson(Json.stringify(serializer.serialize(MAILBOX)(serializer.mailboxWrites(Mailbox.allProperties)))).isEqualTo(expectedJson)
     }
   }
 }


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