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 00441ebc676176ff290287a5249a35c908fa8951
Author: LanKhuat <[email protected]>
AuthorDate: Tue Sep 22 18:36:56 2020 +0700

    JAMES-3379 Email/get specific parsed headers: asDate
---
 .../rfc8621/contract/EmailGetMethodContract.scala  | 107 +++++++++++++++++++++
 .../james/jmap/json/EmailGetSerializer.scala       |   4 +-
 .../scala/org/apache/james/jmap/mail/Email.scala   |  18 ++--
 .../org/apache/james/jmap/mail/EmailGet.scala      |  13 ++-
 .../org/apache/james/jmap/mail/EmailHeader.scala   |  11 ++-
 5 files changed, 142 insertions(+), 11 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 f266760..59b4a52 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
@@ -6415,4 +6415,111 @@ trait EmailGetMethodContract {
            |    ]
            }""".stripMargin)
   }
+
+  @Test
+  def emailGetShouldReturnSpecificHeaderAsDate(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("Date",
+        "Wed, 9 Sep 2020 07:00:26 +0200"))
+      .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:Date:asDate"]
+           |     },
+           |     "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:Date:asDate": "2020-09-09T05:00:26Z"
+           }""".stripMargin)
+  }
+
+  @Test
+  def emailGetShouldReturnNullWhenInvalidSpecificHeaderAsDate(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("Date", "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:Date:asDate"]
+           |     },
+           |     "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:Date:asDate": null
+           }""".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 f937c44..6712ef1 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, 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.model._
 import org.apache.james.mailbox.model.{Cid, MailboxId, MessageId}
 import play.api.libs.functional.syntax._
@@ -64,12 +64,14 @@ object EmailGetSerializer {
       "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 dateHeaderWrites: Writes[DateHeaderValue] = 
Json.valueWrites[DateHeaderValue]
   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)
+    case headerValue: DateHeaderValue => 
Json.toJson[DateHeaderValue](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 de92da1..62d225c 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
@@ -168,6 +168,7 @@ object ParseOptions {
       case "asAddresses" => Some(AsAddresses)
       case "asGroupedAddresses" => Some(AsGroupedAddresses)
       case "asMessageIds" => Some(AsMessageIds)
+      case "asDate" => Some(AsDate)
       case _ => None
   }
 }
@@ -190,7 +191,11 @@ case object AsGroupedAddresses extends ParseOption {
 case object AsMessageIds extends ParseOption {
   override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = 
Some(MessageIdsHeaderValue.from(field))
 }
+case object AsDate extends ParseOption {
+  override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = 
Some(DateHeaderValue.from(field, ZoneId.systemDefault()))
 
+  def extractHeaderValue(field: Field, zoneId: ZoneId): 
Option[EmailHeaderValue] = Some(DateHeaderValue.from(field, zoneId))
+}
 case class HeaderMessageId(value: String) extends AnyVal
 
 case class Subject(value: String) extends AnyVal
@@ -228,10 +233,10 @@ object EmailHeaders {
       sentAt = extractDate(mime4JMessage, "Date").map(date => 
UTCDate.from(date, zoneId)))
   }
 
-  def extractSpecificHeaders(properties: Option[Properties])(mime4JMessage: 
Message) = {
+  def extractSpecificHeaders(properties: Option[Properties])(zoneId: ZoneId, 
mime4JMessage: Message) = {
     properties.getOrElse(Properties.empty()).value
       .flatMap(property => SpecificHeaderRequest.from(property).toOption)
-      .map(_.retrieveHeader(mime4JMessage))
+      .map(_.retrieveHeader(zoneId, mime4JMessage))
       .toMap
   }
 
@@ -272,7 +277,7 @@ object EmailHeaders {
   private def extractDate(mime4JMessage: Message, fieldName: String): 
Option[Date] =
     extractLastField(mime4JMessage, fieldName)
       .flatMap {
-        case f: DateTimeField => Some(f.getDate)
+        case f: DateTimeField => Option(f.getDate)
         case _ => None
       }
 
@@ -424,10 +429,9 @@ private class EmailHeaderViewFactory 
@Inject()(zoneIdProvider: ZoneIdProvider) e
           size = sanitizeSize(firstMessage.getSize),
           keywords = keywords),
         header = EmailHeaders.from(zoneIdProvider.get())(mime4JMessage),
-        specificHeaders = 
EmailHeaders.extractSpecificHeaders(request.properties)(mime4JMessage))
+        specificHeaders = 
EmailHeaders.extractSpecificHeaders(request.properties)(zoneIdProvider.get(), 
mime4JMessage))
     }
   }
-
 }
 
 private class EmailFullViewFactory @Inject()(zoneIdProvider: ZoneIdProvider, 
previewFactory: Preview.Factory) extends EmailViewFactory[EmailFullView] {
@@ -468,7 +472,7 @@ private class EmailFullViewFactory 
@Inject()(zoneIdProvider: ZoneIdProvider, pre
           htmlBody = bodyStructure.htmlBody,
           attachments = bodyStructure.attachments,
           bodyValues = bodyValues),
-        specificHeaders = 
EmailHeaders.extractSpecificHeaders(request.properties)(mime4JMessage))
+        specificHeaders = 
EmailHeaders.extractSpecificHeaders(request.properties)(zoneIdProvider.get(), 
mime4JMessage))
     }
   }
 
@@ -612,7 +616,7 @@ private class EmailFastViewReader 
@Inject()(messageIdManager: MessageIdManager,
           hasAttachment = HasAttachment(fastView.hasAttachment),
           preview = fastView.getPreview),
         header = EmailHeaders.from(zoneIdProvider.get())(mime4JMessage),
-        specificHeaders = 
EmailHeaders.extractSpecificHeaders(request.properties)(mime4JMessage))
+        specificHeaders = 
EmailHeaders.extractSpecificHeaders(request.properties)(zoneIdProvider.get(), 
mime4JMessage))
     }
   }
 }
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
index 43e8c84..ea49946 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailGet.scala
@@ -19,6 +19,8 @@
 
 package org.apache.james.jmap.mail
 
+import java.time.ZoneId
+
 import eu.timepit.refined.api.Refined
 import eu.timepit.refined.auto._
 import eu.timepit.refined.numeric.NonNegative
@@ -30,6 +32,7 @@ import org.apache.james.jmap.model.State.State
 import org.apache.james.jmap.model.{AccountId, Properties}
 import org.apache.james.mime4j.dom.Message
 import org.apache.james.mime4j.stream.Field
+
 import scala.jdk.CollectionConverters._
 
 case class EmailIds(value: List[UnparsedEmailId])
@@ -83,11 +86,17 @@ case class EmailGetResponse(accountId: AccountId,
                             notFound: EmailNotFound)
 
 case class SpecificHeaderRequest(headerName: NonEmptyString, property: String, 
parseOption: Option[ParseOption]) {
-  def retrieveHeader(message: Message): (String, Option[EmailHeaderValue]) = {
+  def retrieveHeader(zoneId: ZoneId, message: Message): (String, 
Option[EmailHeaderValue]) = {
     val field: Option[Field] = Option(message.getHeader.getFields(property))
       .map(_.asScala)
       .flatMap(fields => fields.reverse.headOption)
 
-    (headerName, 
field.flatMap(parseOption.getOrElse(AsRaw).extractHeaderValue(_)))
+    (headerName, field.flatMap({
+      val option = parseOption.getOrElse(AsRaw)
+        option match {
+          case AsDate => AsDate.extractHeaderValue(_, zoneId)
+          case _ => option.extractHeaderValue
+        }
+    }))
   }
 }
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 58db534..343f6dd 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,9 +20,10 @@
 package org.apache.james.jmap.mail
 
 import java.nio.charset.StandardCharsets.US_ASCII
-import java.util.Date
+import java.time.ZoneId
 
 import org.apache.commons.lang3.StringUtils
+import org.apache.james.jmap.model.UTCDate
 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, DateTimeFieldImpl}
@@ -94,6 +95,13 @@ object MessageIdsHeaderValue {
   }
 }
 
+object DateHeaderValue extends EmailHeaderValue {
+  def from(field: Field, zoneId: ZoneId): DateHeaderValue =
+    Option(DateTimeFieldImpl.PARSER.parse(field, DecodeMonitor.SILENT).getDate)
+      .map(date => DateHeaderValue(Some(UTCDate.from(date, zoneId))))
+      .getOrElse(DateHeaderValue(None))
+}
+
 case class EmailHeaderName(value: String) extends AnyVal
 
 sealed trait EmailHeaderValue
@@ -102,5 +110,6 @@ 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 DateHeaderValue(value: Option[UTCDate]) 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