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 d5717e42cb12fd249eeaf5743b67fc5e3c5f798a
Author: LanKhuat <[email protected]>
AuthorDate: Tue Sep 22 15:30:31 2020 +0700

    JAMES-3379 Email/get specific parsed headers: asMessageIds
---
 .../rfc8621/contract/EmailGetMethodContract.scala  | 224 ++++++++++++++++++++-
 .../james/jmap/json/EmailGetSerializer.scala       |   4 +-
 .../scala/org/apache/james/jmap/mail/Email.scala   |  25 ++-
 .../org/apache/james/jmap/mail/EmailHeader.scala   |  22 +-
 4 files changed, 260 insertions(+), 15 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 601ba30..f266760 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
@@ -2105,7 +2105,8 @@ trait EmailGetMethodContract {
       .inPath("methodResponses[0][1].list[0]")
       .isEqualTo(
       s"""{
-         |    "id": "${messageId.serialize}"
+         |    "id": "${messageId.serialize}",
+         |    "messageId":null
          |}""".stripMargin)
   }
 
@@ -2148,7 +2149,8 @@ trait EmailGetMethodContract {
       .inPath("methodResponses[0][1].list[0]")
       .isEqualTo(
       s"""{
-         |    "id": "${messageId.serialize}"
+         |    "id": "${messageId.serialize}",
+         |    "inReplyTo":null
          |}""".stripMargin)
   }
 
@@ -2191,7 +2193,8 @@ trait EmailGetMethodContract {
       .inPath("methodResponses[0][1].list[0]")
       .isEqualTo(
       s"""{
-         |    "id": "${messageId.serialize}"
+         |    "id": "${messageId.serialize}",
+         |    "references":null
          |}""".stripMargin)
   }
 
@@ -6197,4 +6200,219 @@ trait EmailGetMethodContract {
            |    "header:To:asGroupedAddresses": []
            }""".stripMargin)
   }
+
+  @Test
+  def emailGetShouldReturnSpecificHeaderAsMessageIds(server: 
GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+    val alicePath = MailboxPath.inbox(ALICE)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+    val message: Message = Message.Builder
+      .of
+      .setSubject("test")
+      .setSender(ANDRE.asString())
+      .setFrom(ANDRE.asString())
+      .setMessageId("<[email protected]>")
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+    val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+      .getMessageId
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(
+        s"""{
+           |  "using": [
+           |    "urn:ietf:params:jmap:core",
+           |    "urn:ietf:params:jmap:mail"],
+           |  "methodCalls": [[
+           |     "Email/get",
+           |     {
+           |       "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |       "ids": ["${messageId.serialize}"],
+           |       "properties": ["header:Message-Id:asMessageIds"]
+           |     },
+           |     "c1"]]
+           |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].list[0]")
+      .isEqualTo(
+        s"""{
+           |    "id": "${messageId.serialize}",
+           |    "header:Message-Id:asMessageIds": [
+           |      "[email protected]"
+           |    ]
+           }""".stripMargin)
+  }
+
+  @Test
+  def emailGetSpecificHeaderAsMessageIdsShouldSupportMultipleIds(server: 
GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+    val alicePath = MailboxPath.inbox(ALICE)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+    val message: Message = Message.Builder
+      .of
+      .setSubject("test")
+      .setSender(ANDRE.asString())
+      .setFrom(ANDRE.asString())
+      .addField(new RawField("References", "<[email protected]> \r\n 
<[email protected]>"))
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+    val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+      .getMessageId
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(
+        s"""{
+           |  "using": [
+           |    "urn:ietf:params:jmap:core",
+           |    "urn:ietf:params:jmap:mail"],
+           |  "methodCalls": [[
+           |     "Email/get",
+           |     {
+           |       "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |       "ids": ["${messageId.serialize}"],
+           |       "properties": ["header:References:asMessageIds"]
+           |     },
+           |     "c1"]]
+           |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].list[0]")
+      .isEqualTo(
+        s"""{
+           |    "id": "${messageId.serialize}",
+           |    "header:References:asMessageIds": [
+           |      "[email protected]",
+           |      "[email protected]"
+           |    ]
+           }""".stripMargin)
+  }
+
+  @Test
+  def emailGetAsMessageIdsHeaderShouldReturnNullWhenInvalidMessageIds(server: 
GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+    val alicePath = MailboxPath.inbox(ALICE)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+    val message: Message = Message.Builder
+      .of
+      .setSubject("test")
+      .setSender(ANDRE.asString())
+      .setFrom(ANDRE.asString())
+      .setMessageId("invalid")
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+    val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+      .getMessageId
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(
+        s"""{
+           |  "using": [
+           |    "urn:ietf:params:jmap:core",
+           |    "urn:ietf:params:jmap:mail"],
+           |  "methodCalls": [[
+           |     "Email/get",
+           |     {
+           |       "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |       "ids": ["${messageId.serialize}"],
+           |       "properties": ["header:Message-Id:asMessageIds"]
+           |     },
+           |     "c1"]]
+           |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].list[0]")
+      .isEqualTo(
+        s"""{
+           |    "id": "${messageId.serialize}",
+           |    "header:Message-Id:asMessageIds": null
+           }""".stripMargin)
+  }
+
+  @Test
+  def emailGetShouldReturnSpecificHeaderWhenPartialInvalidMessageIds(server: 
GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+    val alicePath = MailboxPath.inbox(ALICE)
+    server.getProbe(classOf[MailboxProbeImpl]).createMailbox(alicePath)
+    val message: Message = Message.Builder
+      .of
+      .setSubject("test")
+      .setSender(ANDRE.asString())
+      .setFrom(ANDRE.asString())
+      .addField(new RawField("References", "invalid   bloblah \r\n 
<[email protected]>"))
+      .setBody("testmail", StandardCharsets.UTF_8)
+      .build
+    val messageId: MessageId = server.getProbe(classOf[MailboxProbeImpl])
+      .appendMessage(BOB.asString, bobPath, AppendCommand.from(message))
+      .getMessageId
+
+    val response = `given`
+      .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+      .body(
+        s"""{
+           |  "using": [
+           |    "urn:ietf:params:jmap:core",
+           |    "urn:ietf:params:jmap:mail"],
+           |  "methodCalls": [[
+           |     "Email/get",
+           |     {
+           |       "accountId": 
"29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+           |       "ids": ["${messageId.serialize}"],
+           |       "properties": ["header:References:asMessageIds"]
+           |     },
+           |     "c1"]]
+           |}""".stripMargin)
+    .when
+      .post
+    .`then`
+      .statusCode(SC_OK)
+      .contentType(JSON)
+      .extract
+      .body
+      .asString
+
+    assertThatJson(response)
+      .inPath("methodResponses[0][1].list[0]")
+      .isEqualTo(
+        s"""{
+           |    "id": "${messageId.serialize}",
+           |    "header:References:asMessageIds": [
+           |      "[email protected]"
+           |    ]
+           }""".stripMargin)
+  }
 }
\ No newline at end of file
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 ae33dc1..f937c44 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, 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, GroupName, Gr [...]
+import org.apache.james.jmap.mail.{Address, AddressesHeaderValue, BlobId, 
Charset, 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, GroupName, Gr [...]
 import org.apache.james.jmap.model._
 import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
 import play.api.libs.functional.syntax._
@@ -63,11 +63,13 @@ object EmailGetSerializer {
       "name" -> Json.toJson(o.name),
       "addresses" -> Json.toJson(o.addresses))
   private implicit val groupedAddressesHeaderWrites: 
Writes[GroupedAddressesHeaderValue] = 
Json.valueWrites[GroupedAddressesHeaderValue]
+  private implicit val messageIdsHeaderWrites: Writes[MessageIdsHeaderValue] = 
Json.valueWrites[MessageIdsHeaderValue]
   private implicit val emailHeaderWrites: Writes[EmailHeaderValue] = {
     case headerValue: RawHeaderValue => 
Json.toJson[RawHeaderValue](headerValue)
     case headerValue: TextHeaderValue => 
Json.toJson[TextHeaderValue](headerValue)
     case headerValue: AddressesHeaderValue => 
Json.toJson[AddressesHeaderValue](headerValue)
     case headerValue: GroupedAddressesHeaderValue => 
Json.toJson[GroupedAddressesHeaderValue](headerValue)
+    case headerValue: MessageIdsHeaderValue => 
Json.toJson[MessageIdsHeaderValue](headerValue)
   }
   private implicit val headersWrites: Writes[EmailHeader] = 
Json.writes[EmailHeader]
   private implicit val bodyValueWrites: Writes[EmailBodyValue] = 
Json.writes[EmailBodyValue]
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 ece0fe3..de92da1 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
@@ -167,6 +167,7 @@ object ParseOptions {
       case "asText" => Some(AsText)
       case "asAddresses" => Some(AsAddresses)
       case "asGroupedAddresses" => Some(AsGroupedAddresses)
+      case "asMessageIds" => Some(AsMessageIds)
       case _ => None
   }
 }
@@ -186,6 +187,9 @@ case object AsAddresses extends ParseOption {
 case object AsGroupedAddresses extends ParseOption {
   override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = 
Some(GroupedAddressesHeaderValue.from(field))
 }
+case object AsMessageIds extends ParseOption {
+  override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = 
Some(MessageIdsHeaderValue.from(field))
+}
 
 case class HeaderMessageId(value: String) extends AnyVal
 
@@ -246,13 +250,14 @@ object EmailHeaders {
       .map(MimeUtil.unscrambleHeaderValue)
       .map(Subject)
 
-  private def extractMessageId(mime4JMessage: Message, fieldName: String): 
Option[List[HeaderMessageId]] =
-    Option(mime4JMessage.getHeader.getFields(fieldName))
-      .map(_.asScala
-        .map(_.getBody)
-        .map(HeaderMessageId.from)
-        .toList)
-      .filter(_.nonEmpty)
+  private def extractMessageId(mime4JMessage: Message, fieldName: String): 
MessageIdsHeaderValue =
+    MessageIdsHeaderValue(
+      Option(mime4JMessage.getHeader.getFields(fieldName))
+        .map(_.asScala
+          .map(_.getBody)
+          .map(HeaderMessageId.from)
+          .toList)
+        .filter(_.nonEmpty))
 
   private def extractAddresses(mime4JMessage: Message, fieldName: String): 
Option[AddressesHeaderValue] =
     extractLastField(mime4JMessage, fieldName)
@@ -279,9 +284,9 @@ object EmailHeaders {
 }
 
 case class EmailHeaders(headers: List[EmailHeader],
-                        messageId: Option[List[HeaderMessageId]],
-                        inReplyTo: Option[List[HeaderMessageId]],
-                        references: Option[List[HeaderMessageId]],
+                        messageId: MessageIdsHeaderValue,
+                        inReplyTo: MessageIdsHeaderValue,
+                        references: MessageIdsHeaderValue,
                         to: Option[AddressesHeaderValue],
                         cc: Option[AddressesHeaderValue],
                         bcc: Option[AddressesHeaderValue],
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 e1da31b..58db534 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
@@ -20,10 +20,12 @@
 package org.apache.james.jmap.mail
 
 import java.nio.charset.StandardCharsets.US_ASCII
+import java.util.Date
 
+import org.apache.commons.lang3.StringUtils
 import org.apache.james.mime4j.codec.{DecodeMonitor, DecoderUtil}
 import org.apache.james.mime4j.dom.address.{AddressList, Group, Address => 
Mime4jAddress, Mailbox => Mime4jMailbox}
-import org.apache.james.mime4j.field.AddressListFieldImpl
+import org.apache.james.mime4j.field.{AddressListFieldImpl, DateTimeFieldImpl}
 import org.apache.james.mime4j.stream.Field
 import org.apache.james.mime4j.util.MimeUtil
 
@@ -75,6 +77,23 @@ object GroupedAddressesHeaderValue extends EmailHeaderValue {
   }
 }
 
+object MessageIdsHeaderValue {
+  def from(field: Field): MessageIdsHeaderValue = {
+    val messageIds: List[HeaderMessageId] = 
MimeUtil.unfold(StringUtils.normalizeSpace(field.getBody))
+      .split(' ')
+      .flatMap(body => {
+        if(body.startsWith("<") && body.endsWith(">") && body.contains("@")) {
+          scala.Right(HeaderMessageId.from(body))
+        } else {
+          Left()
+        }
+      }.toOption)
+      .toList
+
+      MessageIdsHeaderValue(Option(messageIds).filter(_.nonEmpty))
+  }
+}
+
 case class EmailHeaderName(value: String) extends AnyVal
 
 sealed trait EmailHeaderValue
@@ -82,5 +101,6 @@ case class RawHeaderValue(value: String) extends 
EmailHeaderValue
 case class TextHeaderValue(value: String) extends EmailHeaderValue
 case class AddressesHeaderValue(value: List[EmailAddress]) extends 
EmailHeaderValue
 case class GroupedAddressesHeaderValue(value: List[EmailAddressGroup]) extends 
EmailHeaderValue
+case class MessageIdsHeaderValue(value: Option[List[HeaderMessageId]]) extends 
EmailHeaderValue
 
 case class EmailHeader(name: EmailHeaderName, value: EmailHeaderValue)


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

Reply via email to