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 546550f1cd65bb03538bf9515ed7995b21909561
Author: LanKhuat <[email protected]>
AuthorDate: Mon Sep 21 18:05:55 2020 +0700

    JAMES-3379 Email/get specific parsed headers: asGroupedAddresses
---
 .../rfc8621/contract/EmailGetMethodContract.scala  | 206 ++++++++++++++++++++-
 .../james/jmap/json/EmailGetSerializer.scala       |   9 +-
 .../scala/org/apache/james/jmap/mail/Email.scala   |   4 +
 .../apache/james/jmap/mail/EmailAddressGroup.scala |  24 +++
 .../org/apache/james/jmap/mail/EmailHeader.scala   |  34 ++++
 5 files changed, 274 insertions(+), 3 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 6f71695..601ba30 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
@@ -28,7 +28,6 @@ import io.restassured.RestAssured.{`given`, 
requestSpecification}
 import io.restassured.http.ContentType.JSON
 import javax.mail.Flags
 import net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson
-import net.javacrumbs.jsonunit.core.Option
 import net.javacrumbs.jsonunit.core.Option.IGNORING_ARRAY_ORDER
 import net.javacrumbs.jsonunit.core.internal.Options
 import org.apache.http.HttpStatus.SC_OK
@@ -5945,7 +5944,7 @@ trait EmailGetMethodContract {
   }
 
   @Test
-  def emailGetShouldReturnEmptyWhenCannotParseAsAdresses(server: 
GuiceJamesServer): Unit = {
+  def emailGetShouldReturnEmptyWhenCannotParseAsAddresses(server: 
GuiceJamesServer): Unit = {
     val bobPath = MailboxPath.inbox(BOB)
     server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
     val alicePath = MailboxPath.inbox(ALICE)
@@ -5995,4 +5994,207 @@ trait EmailGetMethodContract {
            |    "header:To:asAddresses": []
            }""".stripMargin)
   }
+
+  @Test
+  def emailGetShouldReturnGroupedAddresses(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("To", "\"  user1  \" <[email protected]>, \"  
user2  \" <[email protected]>, Friends: <[email protected]>, \"user4\" 
<[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:To:asGroupedAddresses"]
+           |     },
+           |     "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:To:asGroupedAddresses": [
+           |      {
+           |        "name": null,
+           |        "addresses": [
+           |          {
+           |            "name": "user1",
+           |            "email": "[email protected]"
+           |          },
+           |          {
+           |            "name": "user2",
+           |            "email": "[email protected]"
+           |          }
+           |        ]
+           |      },
+           |      {
+           |        "name": "Friends",
+           |        "addresses": [
+           |          {
+           |            "email": "[email protected]"
+           |          },
+           |          {
+           |            "name": "user4",
+           |            "email": "[email protected]"
+           |          }
+           |        ]
+           |      }
+           |    ]
+           |}""".stripMargin)
+  }
+
+  @Test
+  def emailGetShouldReturnAsGroupedAddressesWithoutGroupInfo(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("To", "\"  user1  \" <[email protected]>, \"  
user2  \" <[email protected]>, <[email protected]>, \"user4\" 
<[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:To:asGroupedAddresses"]
+           |     },
+           |     "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:To:asGroupedAddresses": [
+           |      {
+           |        "name": null,
+           |        "addresses": [
+           |          {
+           |            "name": "user1",
+           |            "email": "[email protected]"
+           |          },
+           |          {
+           |            "name": "user2",
+           |            "email": "[email protected]"
+           |          },
+           |          {
+           |            "email": "[email protected]"
+           |          },
+           |          {
+           |            "name": "user4",
+           |            "email": "[email protected]"
+           |          }
+           |        ]
+           |      }
+           |    ]
+           |}""".stripMargin)
+  }
+
+  @Test
+  def emailGetShouldReturnEmptyWhenCannotParseAsGroupedAddresses(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("To", "blahblah"))
+      .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:To:asGroupedAddresses"]
+           |     },
+           |     "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:To:asGroupedAddresses": []
+           }""".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 475e1df..ae33dc1 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, EmailBody, EmailBodyMetadata, 
EmailBodyPart, EmailBodyValue, EmailFastView, EmailFullView, EmailGetRequest, 
EmailGetResponse, EmailHeader, EmailHeaderName, EmailHeaderValue, 
EmailHeaderView, EmailHeaders, EmailIds, EmailMetadata, EmailMetadataView, 
EmailNotFound, EmailView, EmailerName, FetchAllBodyValues, FetchHTMLBodyValues, 
FetchTextBodyValues, HasAttachment, HeaderMessageId,  [...]
+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._
@@ -57,10 +57,17 @@ object EmailGetSerializer {
   private implicit val rawHeaderWrites: Writes[RawHeaderValue] = 
Json.valueWrites[RawHeaderValue]
   private implicit val textHeaderWrites: Writes[TextHeaderValue] = 
Json.valueWrites[TextHeaderValue]
   private implicit val addressesHeaderWrites: Writes[AddressesHeaderValue] = 
Json.valueWrites[AddressesHeaderValue]
+  private implicit val GroupNameWrites: Writes[GroupName] = 
Json.valueWrites[GroupName]
+  private implicit val emailAddressGroupWrites: Writes[EmailAddressGroup] = 
(o: EmailAddressGroup) =>
+    Json.obj(
+      "name" -> Json.toJson(o.name),
+      "addresses" -> Json.toJson(o.addresses))
+  private implicit val groupedAddressesHeaderWrites: 
Writes[GroupedAddressesHeaderValue] = 
Json.valueWrites[GroupedAddressesHeaderValue]
   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)
   }
   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 560e509..ece0fe3 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
@@ -166,6 +166,7 @@ object ParseOptions {
       case "asRaw" => Some(AsRaw)
       case "asText" => Some(AsText)
       case "asAddresses" => Some(AsAddresses)
+      case "asGroupedAddresses" => Some(AsGroupedAddresses)
       case _ => None
   }
 }
@@ -182,6 +183,9 @@ case object AsText extends ParseOption {
 case object AsAddresses extends ParseOption {
   override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = 
Some(AddressesHeaderValue.from(field))
 }
+case object AsGroupedAddresses extends ParseOption {
+  override def extractHeaderValue(field: Field): Option[EmailHeaderValue] = 
Some(GroupedAddressesHeaderValue.from(field))
+}
 
 case class HeaderMessageId(value: String) extends AnyVal
 
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala
new file mode 100644
index 0000000..b423996
--- /dev/null
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/EmailAddressGroup.scala
@@ -0,0 +1,24 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.mail
+
+case class GroupName(value: String) extends AnyVal
+
+case class EmailAddressGroup(name: Option[GroupName], addresses: 
List[EmailAddress])
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 98133ec..e1da31b 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
@@ -22,10 +22,13 @@ package org.apache.james.jmap.mail
 import java.nio.charset.StandardCharsets.US_ASCII
 
 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.stream.Field
 import org.apache.james.mime4j.util.MimeUtil
 
+import scala.jdk.CollectionConverters._
+
 object EmailHeader {
   def apply(field: Field): EmailHeader = 
EmailHeader(EmailHeaderName(field.getName), RawHeaderValue.from(field))
 }
@@ -42,11 +45,42 @@ object AddressesHeaderValue extends EmailHeaderValue {
   def from(field: Field): AddressesHeaderValue = 
AddressesHeaderValue(EmailAddress.from(AddressListFieldImpl.PARSER.parse(field, 
DecodeMonitor.SILENT).getAddressList))
 }
 
+object GroupedAddressesHeaderValue extends EmailHeaderValue {
+  def from(field: Field): GroupedAddressesHeaderValue = {
+    val addresses: List[Mime4jAddress] =
+      Option(AddressListFieldImpl.PARSER.parse(field, 
DecodeMonitor.SILENT).getAddressList)
+      .getOrElse(new AddressList())
+      .asScala
+      .toList
+
+    val groups: List[EmailAddressGroup] = addresses
+      .flatMap({
+        case group: Group => Some(group)
+        case _ => None
+      })
+      .map(group => EmailAddressGroup(Some(GroupName(group.getName)), 
EmailAddress.from(group.getMailboxes)))
+
+    val addressesWithoutGroup: List[EmailAddress] = addresses
+      .flatMap({
+        case mailbox: Mime4jMailbox => Some(mailbox)
+        case _ => None
+      })
+      .map(EmailAddress.from(_))
+
+    if (addresses.isEmpty) {
+      GroupedAddressesHeaderValue(List())
+    } else {
+      GroupedAddressesHeaderValue(List(EmailAddressGroup(None, 
addressesWithoutGroup)) ++ groups)
+    }
+  }
+}
+
 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 GroupedAddressesHeaderValue(value: List[EmailAddressGroup]) 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