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 26784d5868513db3dd13fae221c369896e141932
Author: Benoit Tellier <[email protected]>
AuthorDate: Mon Oct 26 12:44:38 2020 +0100

    JAMES-3436 Email/set create: Support convenience address headers
---
 .../rfc8621/contract/EmailGetMethodContract.scala  |  2 +-
 .../rfc8621/contract/EmailSetMethodContract.scala  | 62 ++++++++++++++++++++++
 .../james/jmap/json/EmailGetSerializer.scala       |  3 +-
 .../james/jmap/json/EmailSetSerializer.scala       |  5 +-
 .../scala/org/apache/james/jmap/json/package.scala |  8 +++
 .../scala/org/apache/james/jmap/mail/Email.scala   |  2 +-
 .../org/apache/james/jmap/mail/EmailAddress.scala  | 23 ++++----
 .../org/apache/james/jmap/mail/EmailHeader.scala   |  6 ++-
 .../org/apache/james/jmap/mail/EmailSet.scala      | 15 ++++++
 9 files changed, 110 insertions(+), 16 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/EmailGetMethodContract.scala
 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
index a6ca953..47a5c3e 100644
--- 
a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
+++ 
b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailGetMethodContract.scala
@@ -6223,7 +6223,7 @@ trait EmailGetMethodContract {
            |     "c1"]]
            |}""".stripMargin)
     .when
-      .post
+      .post.prettyPeek
     .`then`
       .statusCode(SC_OK)
       .contentType(JSON)
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 64bbdf6..b04ca96 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
@@ -237,6 +237,68 @@ trait EmailSetMethodContract {
   }
 
   @Test
+  def createShouldHandleAddressHeaders(server: GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    val mailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+
+    val request =
+      s"""{
+         |  "using": ["urn:ietf:params:jmap:core", 
"urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [
+         |    ["Email/set", {
+         |      "accountId": "$ACCOUNT_ID",
+         |      "create": {
+         |        "aaaaaa":{
+         |          "mailboxIds": {"${mailboxId.serialize}": true},
+         |          "cc": [{"name": "MODALİF", "email": "[email protected]"}],
+         |          "bcc": [{"email": "[email protected]"}],
+         |          "to": [{"email": "[email protected]"}, {"email": 
"[email protected]"}],
+         |          "from": [{"email": "[email protected]"}, {"email": 
"[email protected]"}],
+         |          "sender": [{"email": "[email protected]"}],
+         |          "replyTo": [{"email": "[email protected]"}, {"email": 
"[email protected]"}]
+         |        }
+         |      }
+         |    }, "c1"],
+         |    ["Email/get",
+         |     {
+         |       "accountId": "$ACCOUNT_ID",
+         |       "ids": ["#aaaaaa"],
+         |       "properties": ["cc", "bcc", "sender", "from", "to", "replyTo"]
+         |     },
+         |     "c2"]]
+         |}""".stripMargin
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(request)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .whenIgnoringPaths("methodResponses[0][1].created.aaaaaa.id")
+      .inPath("methodResponses[0][1].created.aaaaaa")
+      .isEqualTo("{}".stripMargin)
+
+    assertThatJson(response)
+      .whenIgnoringPaths("methodResponses[1][1].list[0].id")
+      .inPath(s"methodResponses[1][1].list")
+      .isEqualTo(s"""[{
+          |          "cc": [{"name": "MODALİF", "email": 
"[email protected]"}],
+          |          "bcc": [{"email": "[email protected]"}],
+          |          "to": [{"email": "[email protected]"}, {"email": 
"[email protected]"}],
+          |          "from": [{"email": "[email protected]"}, {"email": 
"[email protected]"}],
+          |          "sender": [{"email": "[email protected]"}],
+          |          "replyTo": [{"email": "[email protected]"}, {"email": 
"[email protected]"}]
+          |}]""".stripMargin)
+  }
+
+  @Test
   def createShouldSupportKeywords(server: GuiceJamesServer): Unit = {
     val bobPath = MailboxPath.inbox(BOB)
     val mailboxId = 
server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
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 9ec87f6..e8f3c54 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
@@ -20,7 +20,7 @@
 package org.apache.james.jmap.json
 
 import org.apache.james.jmap.api.model.Preview
-import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, 
Charset, DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, 
EmailBody, EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, 
EmailFullView, EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, 
EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, 
EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, 
FetchHTMLBodyValues, FetchTextBodyValu [...]
+import org.apache.james.jmap.mail.{AddressesHeaderValue, BlobId, Charset, 
DateHeaderValue, Disposition, EmailAddress, EmailAddressGroup, EmailBody, 
EmailBodyMetadata, EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, 
EmailGetRequest, EmailGetResponse, EmailHeader, EmailHeaderName, 
EmailHeaderValue, EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, 
EmailMetadataView, EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, 
FetchHTMLBodyValues, FetchTextBodyValues, Group [...]
 import org.apache.james.jmap.model._
 import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
 import play.api.libs.functional.syntax._
@@ -45,7 +45,6 @@ object EmailGetSerializer {
   private implicit val languageWrites: Writes[Language] = 
Json.valueWrites[Language]
   private implicit val locationWrites: Writes[Location] = 
Json.valueWrites[Location]
   private implicit val emailerNameWrites: Writes[EmailerName] = 
Json.valueWrites[EmailerName]
-  private implicit val addressWrites: Writes[Address] = 
Json.valueWrites[Address]
   private implicit val emailAddressWrites: Writes[EmailAddress] = 
Json.writes[EmailAddress]
   private implicit val headerMessageIdWrites: Writes[HeaderMessageId] = 
Json.valueWrites[HeaderMessageId]
   private implicit val isEncodingProblemWrites: Writes[IsEncodingProblem] = 
Json.valueWrites[IsEncodingProblem]
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 e36e3ca..a5f0cd2 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
@@ -23,7 +23,7 @@ import cats.implicits._
 import eu.timepit.refined.refineV
 import javax.inject.Inject
 import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, 
UnparsedMessageId, UnparsedMessageIdConstraint}
-import org.apache.james.jmap.mail.{DestroyIds, EmailCreationRequest, 
EmailCreationResponse, EmailSetRequest, EmailSetResponse, EmailSetUpdate, 
MailboxIds, Subject}
+import org.apache.james.jmap.mail.{AddressesHeaderValue, DestroyIds, 
EmailAddress, EmailCreationRequest, EmailCreationResponse, EmailSetRequest, 
EmailSetResponse, EmailSetUpdate, EmailerName, MailboxIds, Subject}
 import org.apache.james.jmap.model.Id.IdConstraint
 import org.apache.james.jmap.model.KeywordsFactory.STRICT_KEYWORDS_FACTORY
 import org.apache.james.jmap.model.{Keyword, Keywords, SetError}
@@ -230,6 +230,9 @@ class EmailSetSerializer @Inject()(messageIdFactory: 
MessageId.Factory, mailboxI
   private implicit val emailResponseSetWrites: OWrites[EmailSetResponse] = 
Json.writes[EmailSetResponse]
 
   private implicit val subjectReads: Reads[Subject] = Json.valueReads[Subject]
+  private implicit val emailerNameReads: Reads[EmailerName] = 
Json.valueReads[EmailerName]
+  private implicit val emailAddressReads: Reads[EmailAddress] = 
Json.reads[EmailAddress]
+  private implicit val addressesHeaderValueReads: Reads[AddressesHeaderValue] 
= Json.valueReads[AddressesHeaderValue]
   private implicit val emailCreationRequestReads: Reads[EmailCreationRequest] 
= Json.reads[EmailCreationRequest]
 
   def deserialize(input: JsValue): JsResult[EmailSetRequest] = 
Json.fromJson[EmailSetRequest](input)
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/package.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/package.scala
index 05eaeab..2c4e5c2 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/package.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/json/package.scala
@@ -23,6 +23,7 @@ import java.time.ZonedDateTime
 import java.time.format.DateTimeFormatter
 
 import eu.timepit.refined.api.{RefType, Validate}
+import org.apache.james.core.MailAddress
 import org.apache.james.jmap.model.Id.Id
 import org.apache.james.jmap.model.SetError.SetErrorDescription
 import org.apache.james.jmap.model.{AccountId, Properties, SetError, UTCDate}
@@ -101,6 +102,13 @@ package object json {
   private[json] implicit val propertiesFormat: Format[Properties] = 
Json.valueFormat[Properties]
   private[json] implicit val setErrorDescriptionWrites: 
Writes[SetErrorDescription] = Json.valueWrites[SetErrorDescription]
   private[json] implicit val setErrorWrites: Writes[SetError] = 
Json.writes[SetError]
+  private[json] implicit val mailAddressWrites: Writes[MailAddress] = 
mailAddress => JsString(mailAddress.asString)
+  private[json] implicit val mailAddressReads: Reads[MailAddress] = {
+    case JsString(value) => Try(new MailAddress(value))
+      .fold(e => JsError(e.getMessage),
+        mailAddress => JsSuccess(mailAddress))
+    case _ => JsError("mail address needs to be represented with a JsString")
+  }
   private[json] implicit val utcDateWrites: Writes[UTCDate] =
     utcDate => 
JsString(utcDate.asUTC.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssX")))
 }
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 9174ef9..0804583 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
@@ -286,7 +286,7 @@ object EmailHeaders {
       .flatMap {
         case f: AddressListField => 
Some(AddressesHeaderValue(EmailAddress.from(f.getAddressList)))
         case f: MailboxListField => 
Some(AddressesHeaderValue(EmailAddress.from(f.getMailboxList)))
-        case f: MailboxField => 
Some(AddressesHeaderValue(List(EmailAddress.from(f.getMailbox))))
+        case f: MailboxField => 
Some(AddressesHeaderValue(List(EmailAddress.from(f.getMailbox).toOption).flatten))
         case _ => None
       }
       .filter(_.value.nonEmpty)
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddress.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddress.scala
index 59f3a6c..80548d6 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddress.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddress.scala
@@ -19,9 +19,11 @@
 
 package org.apache.james.jmap.mail
 
+import org.apache.james.core.MailAddress
 import org.apache.james.mime4j.dom.address.{AddressList, MailboxList, Mailbox 
=> Mime4jMailbox}
 
 import scala.jdk.CollectionConverters._
+import scala.util.Try
 
 object EmailerName {
   def from(value: String): EmailerName = EmailerName(value.strip())
@@ -29,8 +31,6 @@ object EmailerName {
 
 case class EmailerName(value: String) extends AnyVal
 
-case class Address(value: String) extends AnyVal
-
 object EmailAddress {
   def from(addressList: AddressList): List[EmailAddress] = Option(addressList)
     .map(addressList => from(addressList.flatten()))
@@ -39,13 +39,18 @@ object EmailAddress {
   def from(addressList: MailboxList): List[EmailAddress] =
     addressList.asScala
       .toList
-      .map(from)
+      .flatMap(mailbox => from(mailbox).toOption)
 
-  def from(mailbox: Mime4jMailbox): EmailAddress = {
-    EmailAddress(
-      name = Option(mailbox.getName).map(EmailerName.from),
-      email = Address(mailbox.getAddress))
-  }
+  def from(mailbox: Mime4jMailbox): Try[EmailAddress] =
+    Try(new MailAddress(mailbox.getAddress))
+        .map(email => EmailAddress(
+          name = Option(mailbox.getName).map(EmailerName.from),
+          email = email))
 }
 
-case class EmailAddress(name: Option[EmailerName], email: Address)
+case class EmailAddress(name: Option[EmailerName], email: MailAddress) {
+  val asMime4JMailbox: Mime4jMailbox = new Mime4jMailbox(
+    name.map(_.value).orNull,
+    email.getLocalPart,
+    email.getDomain.asString)
+}
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
index 8c8247c..02f6645 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailHeader.scala
@@ -71,7 +71,7 @@ object GroupedAddressesHeaderValue extends EmailHeaderValue {
           case mailbox: Mime4jMailbox => Some(mailbox)
           case _ => None
         })
-        .map(EmailAddress.from(_))
+        .flatMap(EmailAddress.from(_).toOption)
 
       GroupedAddressesHeaderValue(List(EmailAddressGroup(None, 
addressesWithoutGroup)) ++ groups)
     }
@@ -124,7 +124,9 @@ case class EmailHeaderName(value: String) extends AnyVal
 sealed trait EmailHeaderValue
 case class RawHeaderValue(value: String) extends EmailHeaderValue
 case class TextHeaderValue(value: String) extends EmailHeaderValue
-case class AddressesHeaderValue(value: List[EmailAddress]) extends 
EmailHeaderValue
+case class AddressesHeaderValue(value: List[EmailAddress]) extends 
EmailHeaderValue {
+  def asMime4JMailboxList: Option[List[Mime4jMailbox]] = 
Some(value.map(_.asMime4JMailbox)).filter(_.nonEmpty)
+}
 case class GroupedAddressesHeaderValue(value: List[EmailAddressGroup]) extends 
EmailHeaderValue
 case class MessageIdsHeaderValue(value: Option[List[HeaderMessageId]]) extends 
EmailHeaderValue
 case class DateHeaderValue(value: Option[UTCDate]) extends EmailHeaderValue
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 90c9c41..f64db7e 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
@@ -30,8 +30,11 @@ import org.apache.james.jmap.model.State.State
 import org.apache.james.jmap.model.{AccountId, Keywords, SetError, UTCDate}
 import org.apache.james.mailbox.model.MessageId
 import org.apache.james.mime4j.dom.Message
+import org.apache.james.mime4j.dom.field.FieldName
+import org.apache.james.mime4j.field.Fields
 import play.api.libs.json.JsObject
 
+import scala.jdk.CollectionConverters._
 import scala.util.{Right, Try}
 
 object EmailSet {
@@ -49,12 +52,24 @@ object EmailSet {
 }
 
 case class EmailCreationRequest(mailboxIds: MailboxIds,
+                                from: Option[AddressesHeaderValue],
+                                to: Option[AddressesHeaderValue],
+                                cc: Option[AddressesHeaderValue],
+                                bcc: Option[AddressesHeaderValue],
+                                sender: Option[AddressesHeaderValue],
+                                replyTo: Option[AddressesHeaderValue],
                                 subject: Option[Subject],
                                 keywords: Option[Keywords],
                                 receivedAt: Option[UTCDate]) {
   def toMime4JMessage: Message = {
     val builder = Message.Builder.of
     subject.foreach(value => builder.setSubject(value.value))
+    from.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setFrom)
+    to.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setTo)
+    cc.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setCc)
+    bcc.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setBcc)
+    
sender.flatMap(_.asMime4JMailboxList).map(_.asJava).map(Fields.addressList(FieldName.SENDER,
 _)).foreach(builder.setField)
+    
replyTo.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setReplyTo)
     builder.setBody("", StandardCharsets.UTF_8)
     builder.build()
   }


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

Reply via email to