You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2020/11/23 08:20:13 UTC

[james-project] 14/19: JAMES-3434 Advertise urn:ietf:params:jmap:submission capability

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 30e3ed5cb86e4788ebd0da22c462c337e4136982
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Sun Nov 22 14:16:50 2020 +0700

    JAMES-3434 Advertise urn:ietf:params:jmap:submission capability
---
 .../rfc8621/contract/SessionRoutesContract.scala   |  9 ++++++
 .../org/apache/james/jmap/core/Capabilities.scala  | 35 ++++++++++++----------
 .../org/apache/james/jmap/core/Capability.scala    | 16 ++++++++--
 .../james/jmap/json/ResponseSerializer.scala       |  7 +++++
 .../james/jmap/routes/SessionRoutesTest.scala      |  9 ++++++
 5 files changed, 57 insertions(+), 19 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/SessionRoutesContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/SessionRoutesContract.scala
index 7fc769e..b33c01e 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/SessionRoutesContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/SessionRoutesContract.scala
@@ -41,6 +41,10 @@ import org.junit.jupiter.api.{BeforeEach, Tag, Test}
 object SessionRoutesContract {
   private val expected_session_object: String = """{
                          |  "capabilities" : {
+                         |    "urn:ietf:params:jmap:submission": {
+                         |      "maxDelayedSend": 0,
+                         |      "submissionExtensions": []
+                         |    },
                          |    "urn:ietf:params:jmap:core" : {
                          |      "maxSizeUpload" : 31457280,
                          |      "maxConcurrentUpload" : 4,
@@ -69,6 +73,10 @@ object SessionRoutesContract {
                          |      "isPersonal" : true,
                          |      "isReadOnly" : false,
                          |      "accountCapabilities" : {
+                         |        "urn:ietf:params:jmap:submission": {
+                         |          "maxDelayedSend": 0,
+                         |          "submissionExtensions": []
+                         |        },
                          |        "urn:ietf:params:jmap:core" : {
                          |          "maxSizeUpload" : 31457280,
                          |          "maxConcurrentUpload" : 4,
@@ -94,6 +102,7 @@ object SessionRoutesContract {
                          |    }
                          |  },
                          |  "primaryAccounts" : {
+                         |    "urn:ietf:params:jmap:submission": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
                          |    "urn:ietf:params:jmap:core" : "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
                          |    "urn:ietf:params:jmap:mail" : "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
                          |    "urn:apache:james:params:jmap:mail:quota": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
index 8b74337..40fd95d 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capabilities.scala
@@ -19,10 +19,10 @@
 package org.apache.james.jmap.core
 
 import eu.timepit.refined.auto._
-import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JAMES_QUOTA, JAMES_SHARES, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, EMAIL_SUBMISSION, JAMES_QUOTA, JAMES_SHARES, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
 
 object DefaultCapabilities {
-  def coreCapability(maxUploadSize: MaxSizeUpload) = CoreCapability(
+  private def coreCapability(maxUploadSize: MaxSizeUpload) = CoreCapability(
     properties = CoreCapabilityProperties(
       maxUploadSize,
       MaxConcurrentUpload(4L),
@@ -32,8 +32,7 @@ object DefaultCapabilities {
       MaxObjectsInGet(500L),
       MaxObjectsInSet(500L),
       collationAlgorithms = List("i;unicode-casemap")))
-
-  val MAIL_CAPABILITY = MailCapability(
+  private val MAIL_CAPABILITY = MailCapability(
     properties = MailCapabilityProperties(
       MaxMailboxesPerEmail(Some(10_000_000L)),
       MaxMailboxDepth(None),
@@ -41,20 +40,24 @@ object DefaultCapabilities {
       MaxSizeAttachmentsPerEmail(20_000_000L),
       emailQuerySortOptions = List("receivedAt", "sentAt"),
       MayCreateTopLevelMailbox(true)))
-
-  val QUOTA_CAPABILITY = QuotaCapability()
-
-  val SHARES_CAPABILITY = SharesCapability()
-
-  val VACATION_RESPONSE_CAPABILITY = VacationResponseCapability()
-
-  val SUPPORTED_CAPABILITY_IDENTIFIERS = Set(JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE, JAMES_SHARES, JAMES_QUOTA)
-
-  def supported(maxUploadSize: MaxSizeUpload) = Capabilities(coreCapability(maxUploadSize), MAIL_CAPABILITY, QUOTA_CAPABILITY, SHARES_CAPABILITY, VACATION_RESPONSE_CAPABILITY)
+  private val QUOTA_CAPABILITY = QuotaCapability()
+  private val SHARES_CAPABILITY = SharesCapability()
+  private val VACATION_RESPONSE_CAPABILITY = VacationResponseCapability()
+  private val SUBMISSION_CAPABILITY = SubmissionCapability()
+
+  val SUPPORTED_CAPABILITY_IDENTIFIERS: Set[CapabilityIdentifier] =
+    Set(JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE, JAMES_SHARES, JAMES_QUOTA, EMAIL_SUBMISSION)
+
+  def supported(maxUploadSize: MaxSizeUpload): Capabilities = Capabilities(coreCapability(maxUploadSize),
+    MAIL_CAPABILITY,
+    QUOTA_CAPABILITY,
+    SHARES_CAPABILITY,
+    VACATION_RESPONSE_CAPABILITY,
+    SUBMISSION_CAPABILITY)
 }
 
 case class Capabilities(capabilities: Capability*) {
-  def toSet : Set[Capability] = capabilities.toSet
+  def toSet: Set[Capability] = capabilities.toSet
 
-  def ids : Set[CapabilityIdentifier] = toSet.map(_.identifier())
+  def ids: Set[CapabilityIdentifier] = toSet.map(_.identifier())
 }
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
index 8ec9dfd..072dc24 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/core/Capability.scala
@@ -24,7 +24,7 @@ import eu.timepit.refined.api.Refined
 import eu.timepit.refined.auto._
 import eu.timepit.refined.collection.NonEmpty
 import eu.timepit.refined.string.Uri
-import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JAMES_QUOTA, JAMES_SHARES, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
+import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, EMAIL_SUBMISSION, JAMES_QUOTA, JAMES_SHARES, JMAP_CORE, JMAP_MAIL, JMAP_VACATION_RESPONSE}
 import org.apache.james.jmap.core.CoreCapabilityProperties.CollationAlgorithm
 import org.apache.james.jmap.core.MailCapability.EmailQuerySortOption
 import org.apache.james.jmap.core.UnsignedInt.{UnsignedInt, UnsignedIntConstraint}
@@ -40,6 +40,7 @@ object CapabilityIdentifier {
   val JMAP_CORE: CapabilityIdentifier = "urn:ietf:params:jmap:core"
   val JMAP_MAIL: CapabilityIdentifier = "urn:ietf:params:jmap:mail"
   val JMAP_VACATION_RESPONSE: CapabilityIdentifier = "urn:ietf:params:jmap:vacationresponse"
+  val EMAIL_SUBMISSION: CapabilityIdentifier = "urn:ietf:params:jmap:submission"
   val JAMES_QUOTA: CapabilityIdentifier = "urn:apache:james:params:jmap:mail:quota"
   val JAMES_SHARES: CapabilityIdentifier = "urn:apache:james:params:jmap:mail:shares"
 }
@@ -80,8 +81,17 @@ final case class CoreCapabilityProperties(maxSizeUpload: MaxSizeUpload,
                                           maxCallsInRequest: MaxCallsInRequest,
                                           maxObjectsInGet: MaxObjectsInGet,
                                           maxObjectsInSet: MaxObjectsInSet,
-                                          collationAlgorithms: List[CollationAlgorithm]) extends CapabilityProperties {
-}
+                                          collationAlgorithms: List[CollationAlgorithm]) extends CapabilityProperties
+
+final case class MaxDelayedSend(value: Int) extends AnyVal
+final case class EhloName(value: String) extends AnyVal
+final case class EhloArgs(value: String) extends AnyVal
+
+final case class SubmissionCapability(identifier: CapabilityIdentifier = EMAIL_SUBMISSION,
+                                      properties: SubmissionProperties = SubmissionProperties()) extends Capability
+
+final case class SubmissionProperties(maxDelayedSend: MaxDelayedSend = MaxDelayedSend(0),
+                                      submissionExtensions: Map[EhloName, List[EhloArgs]] = Map()) extends CapabilityProperties
 
 object MailCapability {
   type EmailQuerySortOption = String Refined NonEmpty
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala
index 2afc1bb..24eefc8 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/ResponseSerializer.scala
@@ -87,9 +87,14 @@ object ResponseSerializer {
   private implicit val urlWrites: Writes[URL] = url => JsString(url.toString)
   private implicit val coreCapabilityWrites: Writes[CoreCapabilityProperties] = Json.writes[CoreCapabilityProperties]
   private implicit val mailCapabilityWrites: Writes[MailCapabilityProperties] = Json.writes[MailCapabilityProperties]
+  private implicit val maxDelayedSendWrites: Writes[MaxDelayedSend] = Json.valueWrites[MaxDelayedSend]
+  private implicit val ehloNameWrites: Writes[EhloName] = Json.valueWrites[EhloName]
+  private implicit val ehloArgsWrites: Writes[EhloArgs] = Json.valueWrites[EhloArgs]
+  private implicit val submissionPropertiesWrites: Writes[SubmissionProperties] = Json.writes[SubmissionProperties]
   private implicit val quotaCapabilityWrites: Writes[QuotaCapabilityProperties] = OWrites[QuotaCapabilityProperties](_ => Json.obj())
   private implicit val sharesCapabilityWrites: Writes[SharesCapabilityProperties] = OWrites[SharesCapabilityProperties](_ => Json.obj())
   private implicit val vacationResponseCapabilityWrites: Writes[VacationResponseCapabilityProperties] = OWrites[VacationResponseCapabilityProperties](_ => Json.obj())
+  private implicit val submissionCapabilityWrites: Writes[SubmissionCapability] = OWrites[SubmissionCapability](_ => Json.obj())
 
   private implicit val setCapabilityWrites: Writes[Set[_ <: Capability]] =
     (set: Set[_ <: Capability]) => {
@@ -105,6 +110,8 @@ object ResponseSerializer {
             jsObject.+(capability.identifier.value, sharesCapabilityWrites.writes(capability.properties))
           case capability: VacationResponseCapability =>
             jsObject.+(capability.identifier.value, vacationResponseCapabilityWrites.writes(capability.properties))
+          case capability: SubmissionCapability =>
+            jsObject.+(capability.identifier.value, submissionPropertiesWrites.writes(capability.properties))
           case _ => jsObject
         }
       })
diff --git a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
index 7a27623..8a3bb45 100644
--- a/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
+++ b/server/protocols/jmap-rfc-8621/src/test/scala/org/apache/james/jmap/routes/SessionRoutesTest.scala
@@ -119,6 +119,10 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
     val downloadPath: String = "download/{accountId}/{blobId}/?type={type}&name={name}"
     val expectedJson = s"""{
                          |  "capabilities" : {
+                         |    "urn:ietf:params:jmap:submission": {
+                         |      "maxDelayedSend": 0,
+                         |      "submissionExtensions": []
+                         |    },
                          |    "urn:ietf:params:jmap:core" : {
                          |      "maxSizeUpload" : 31457280,
                          |      "maxConcurrentUpload" : 4,
@@ -147,6 +151,10 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
                          |      "isPersonal" : true,
                          |      "isReadOnly" : false,
                          |      "accountCapabilities" : {
+                         |        "urn:ietf:params:jmap:submission": {
+                         |          "maxDelayedSend": 0,
+                         |          "submissionExtensions": []
+                         |        },
                          |        "urn:ietf:params:jmap:core" : {
                          |          "maxSizeUpload" : 31457280,
                          |          "maxConcurrentUpload" : 4,
@@ -172,6 +180,7 @@ class SessionRoutesTest extends AnyFlatSpec with BeforeAndAfter with Matchers {
                          |    }
                          |  },
                          |  "primaryAccounts" : {
+                         |    "urn:ietf:params:jmap:submission": "0fe275bf13ff761407c17f64b1dfae2f4b3186feea223d7267b79f873a105401",
                          |    "urn:ietf:params:jmap:core" : "0fe275bf13ff761407c17f64b1dfae2f4b3186feea223d7267b79f873a105401",
                          |    "urn:ietf:params:jmap:mail" : "0fe275bf13ff761407c17f64b1dfae2f4b3186feea223d7267b79f873a105401",
                          |    "urn:apache:james:params:jmap:mail:quota": "0fe275bf13ff761407c17f64b1dfae2f4b3186feea223d7267b79f873a105401",


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