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/09/25 01:51:48 UTC
[james-project] 03/13: JAMES-3379 Email/get specific parsed
headers: asDate
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 <dl...@linagora.com>
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: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org