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/10/28 04:53:25 UTC

[james-project] branch master updated (513d2cb -> 85a9af8)

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git.


    from 513d2cb  JAMES-3436 Email/set create - syntax error validation
     new 17d98a0  JAMES-3436 Email/set create: Support keywords
     new 8e83fe5  JAMES-3436 Email/set create: Support receivedAt
     new 26784d5  JAMES-3436 Email/set create: Support convenience address headers
     new 370abe3  JAMES-3436 Email/set create: Multiple Sender addresses should be supported
     new f781ad0  JAMES-3436 Email/set create: Support sentAt
     new 08c28e9  JAMES-3436 Email/set create: Support convenience messageId headers
     new 432f2d6  JAMES-3408 Limit concurrency when retrieving mailbox counters
     new 4f6529b  JAMES-3437 MemoryMailQueueFactory should be a singleton
     new 05a38ba  MAILBOX-401 Demonstrate '-' causes address matching to fail
     new 85a9af8  Update RabbitMQConnectionFactory.java

The 10 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../rabbitmq/RabbitMQConnectionFactory.java        |   4 +
 .../ElasticSearchIntegrationTest.java              |  97 ++++++
 .../james/mailbox/store/StoreMailboxManager.java   |   4 +-
 .../modules/server/MemoryMailQueueModule.java      |   3 +-
 .../rfc8621/contract/EmailGetMethodContract.scala  |   6 +-
 .../rfc8621/contract/EmailSetMethodContract.scala  | 357 ++++++++++++++++++++-
 .../james/jmap/json/EmailGetSerializer.scala       |   3 +-
 .../james/jmap/json/EmailSetSerializer.scala       |  16 +-
 .../scala/org/apache/james/jmap/json/package.scala |   8 +
 .../scala/org/apache/james/jmap/mail/Email.scala   |  15 +-
 .../org/apache/james/jmap/mail/EmailAddress.scala  |  23 +-
 .../org/apache/james/jmap/mail/EmailHeader.scala   |  13 +-
 .../org/apache/james/jmap/mail/EmailSet.scala      |  31 +-
 .../apache/james/jmap/method/EmailSetMethod.scala  |  12 +-
 .../integration/WebAdminServerIntegrationTest.java |   3 +-
 15 files changed, 560 insertions(+), 35 deletions(-)


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 07/10: JAMES-3408 Limit concurrency when retrieving mailbox counters

Posted by bt...@apache.org.
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 432f2d686d693b6bedb5d891fd0f15cfdcaa020b
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Oct 27 12:52:25 2020 +0700

    JAMES-3408 Limit concurrency when retrieving mailbox counters
    
    4 in parrallel instead of 128 should limit pressure on the DB
---
 .../main/java/org/apache/james/mailbox/store/StoreMailboxManager.java | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
index 97c1972..6e075c0 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
@@ -650,11 +650,13 @@ public class StoreMailboxManager implements MailboxManager {
 
     private Function<Flux<Mailbox>, Flux<MailboxMetaData>> withCounters(MailboxSession session, List<Mailbox> mailboxes) {
         MessageMapper messageMapper = mailboxSessionMapperFactory.getMessageMapper(session);
+        int concurrency = 4;
         return mailboxFlux -> mailboxFlux
             .flatMap(mailbox -> retrieveCounters(messageMapper, mailbox, session)
                 .map(Throwing.<MailboxCounters, MailboxMetaData>function(
                     counters -> toMailboxMetadata(session, mailboxes, mailbox, counters))
-                    .sneakyThrow()));
+                    .sneakyThrow()),
+                concurrency);
     }
 
     private Function<Flux<Mailbox>, Flux<MailboxMetaData>> withoutCounters(MailboxSession session, List<Mailbox> mailboxes) {


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 05/10: JAMES-3436 Email/set create: Support sentAt

Posted by bt...@apache.org.
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 f781ad0c310f5941f6df56de3bccb007a0706706
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Oct 27 09:32:12 2020 +0700

    JAMES-3436 Email/set create: Support sentAt
---
 .../rfc8621/contract/EmailSetMethodContract.scala  | 58 ++++++++++++++++++++++
 .../org/apache/james/jmap/mail/EmailSet.scala      |  3 ++
 2 files changed, 61 insertions(+)

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 9cfbeec..c5ed96d 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
@@ -472,6 +472,64 @@ trait EmailSetMethodContract {
   }
 
   @Test
+  def createShouldSupportSentAt(server: GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+    val sentAt = ZonedDateTime.now().minusDays(1)
+
+    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
+         |          },
+         |          "sentAt": "${UTCDate(sentAt).asUTC.format(UTC_DATE_FORMAT)}"
+         |        }
+         |      }
+         |    }, "c1"],
+         |    ["Email/get",
+         |     {
+         |       "accountId": "$ACCOUNT_ID",
+         |       "ids": ["#aaaaaa"],
+         |       "properties": ["mailboxIds", "sentAt"]
+         |     },
+         |     "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"""[{
+          |  "mailboxIds": {
+          |    "${mailboxId.serialize}": true
+          |  },
+          |  "sentAt": "${UTCDate(sentAt).asUTC.format(UTC_DATE_FORMAT)}"
+          |}]""".stripMargin)
+  }
+
+  @Test
   def createShouldFailIfForbidden(server: GuiceJamesServer): Unit = {
     val andrePath = MailboxPath.inbox(ANDRE)
     val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
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 f64db7e..42d68fc 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
@@ -19,6 +19,7 @@
 package org.apache.james.jmap.mail
 
 import java.nio.charset.StandardCharsets
+import java.util.Date
 
 import eu.timepit.refined
 import eu.timepit.refined.api.Refined
@@ -59,6 +60,7 @@ case class EmailCreationRequest(mailboxIds: MailboxIds,
                                 sender: Option[AddressesHeaderValue],
                                 replyTo: Option[AddressesHeaderValue],
                                 subject: Option[Subject],
+                                sentAt: Option[UTCDate],
                                 keywords: Option[Keywords],
                                 receivedAt: Option[UTCDate]) {
   def toMime4JMessage: Message = {
@@ -70,6 +72,7 @@ case class EmailCreationRequest(mailboxIds: MailboxIds,
     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)
+    sentAt.map(_.asUTC).map(_.toInstant).map(Date.from).foreach(builder.setDate)
     builder.setBody("", StandardCharsets.UTF_8)
     builder.build()
   }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 03/10: JAMES-3436 Email/set create: Support convenience address headers

Posted by bt...@apache.org.
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 <bt...@linagora.com>
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": "modalif@domain.tld"}],
+         |          "bcc": [{"email": "benwa@apache.org"}],
+         |          "to": [{"email": "rcpt1@apache.org"}, {"email": "rcpt2@apache.org"}],
+         |          "from": [{"email": "rcpt2@apache.org"}, {"email": "rcpt3@apache.org"}],
+         |          "sender": [{"email": "rcpt4@apache.org"}],
+         |          "replyTo": [{"email": "rcpt6@apache.org"}, {"email": "rcpt7@apache.org"}]
+         |        }
+         |      }
+         |    }, "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": "modalif@domain.tld"}],
+          |          "bcc": [{"email": "benwa@apache.org"}],
+          |          "to": [{"email": "rcpt1@apache.org"}, {"email": "rcpt2@apache.org"}],
+          |          "from": [{"email": "rcpt2@apache.org"}, {"email": "rcpt3@apache.org"}],
+          |          "sender": [{"email": "rcpt4@apache.org"}],
+          |          "replyTo": [{"email": "rcpt6@apache.org"}, {"email": "rcpt7@apache.org"}]
+          |}]""".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: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 10/10: Update RabbitMQConnectionFactory.java

Posted by bt...@apache.org.
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 85a9af8b818afff8a4a380f6021c63ce9fdef46a
Author: wnfkwnfk <45...@users.noreply.github.com>
AuthorDate: Tue Oct 27 22:01:20 2020 +0800

    Update RabbitMQConnectionFactory.java
    
    when rabbitmq change guest's password, it cannot be connected and will throw exception:
    An unexpected connection driver error occured (Exception message: Socket closed)
    so we should set username and password here to make it work correctly
---
 .../org/apache/james/backends/rabbitmq/RabbitMQConnectionFactory.java | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQConnectionFactory.java b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQConnectionFactory.java
index 36989f9..cb45fc7 100644
--- a/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQConnectionFactory.java
+++ b/backends-common/rabbitmq/src/main/java/org/apache/james/backends/rabbitmq/RabbitMQConnectionFactory.java
@@ -50,6 +50,10 @@ public class RabbitMQConnectionFactory {
             connectionFactory.setChannelRpcTimeout(rabbitMQConfiguration.getChannelRpcTimeoutInMs());
             connectionFactory.setConnectionTimeout(rabbitMQConfiguration.getConnectionTimeoutInMs());
             connectionFactory.setNetworkRecoveryInterval(rabbitMQConfiguration.getNetworkRecoveryIntervalInMs());
+
+            connectionFactory.setUsername(rabbitMQConfiguration.getManagementCredentials().getUser());
+            connectionFactory.setPassword(String.valueOf(rabbitMQConfiguration.getManagementCredentials().getPassword()));
+
             return connectionFactory;
         } catch (Exception e) {
             throw new RuntimeException(e);


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 08/10: JAMES-3437 MemoryMailQueueFactory should be a singleton

Posted by bt...@apache.org.
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 4f6529b36c411b89a2821f9e98bf2002f02d35aa
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Oct 27 13:48:37 2020 +0700

    JAMES-3437 MemoryMailQueueFactory should be a singleton
---
 .../java/org/apache/james/modules/server/MemoryMailQueueModule.java    | 3 ++-
 .../james/webadmin/integration/WebAdminServerIntegrationTest.java      | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/server/MemoryMailQueueModule.java b/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/server/MemoryMailQueueModule.java
index 8af5b16..8a05148 100644
--- a/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/server/MemoryMailQueueModule.java
+++ b/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/server/MemoryMailQueueModule.java
@@ -25,13 +25,14 @@ import org.apache.james.queue.memory.MemoryMailQueueFactory;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
+import com.google.inject.Scopes;
 import com.google.inject.Singleton;
 
 public class MemoryMailQueueModule extends AbstractModule {
 
     @Override
     protected void configure() {
-
+        bind(MemoryMailQueueFactory.class).in(Scopes.SINGLETON);
     }
 
     @Provides
diff --git a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java
index 992a2e9..1730a1d 100644
--- a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java
@@ -100,7 +100,8 @@ public abstract class WebAdminServerIntegrationTest {
         when()
             .get(MailQueueRoutes.BASE_URL)
         .then()
-            .statusCode(HttpStatus.OK_200);
+            .statusCode(HttpStatus.OK_200)
+            .body("", containsInAnyOrder("spool", "outgoing"));
     }
 
     @Test


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 09/10: MAILBOX-401 Demonstrate '-' causes address matching to fail

Posted by bt...@apache.org.
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 05a38ba764e990683a1126f897612e8e41f0ec3a
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Oct 28 08:50:53 2020 +0700

    MAILBOX-401 Demonstrate '-' causes address matching to fail
---
 .../ElasticSearchIntegrationTest.java              | 97 ++++++++++++++++++++++
 1 file changed, 97 insertions(+)

diff --git a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java
index 043a3dc..46eb518 100644
--- a/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java
+++ b/mailbox/elasticsearch/src/test/java/org/apache/james/mailbox/elasticsearch/ElasticSearchIntegrationTest.java
@@ -51,6 +51,7 @@ import org.apache.james.metrics.tests.RecordingMetricFactory;
 import org.apache.james.mime4j.dom.Message;
 import org.apache.james.mime4j.stream.RawField;
 import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
@@ -292,4 +293,100 @@ class ElasticSearchIntegrationTest extends AbstractMessageSearchIndexTest {
         assertThat(messageManager.search(SearchQuery.of(SearchQuery.address(SearchQuery.AddressType.To, "bob@other.tld")), session))
             .containsOnly(messageId2.getUid());
     }
+
+    @Disabled("MAILBOX-401 '-' causes address matching to fail")
+    @Test
+    void localPartShouldBeMatchedWhenHyphen() throws Exception {
+        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
+        MailboxSession session = MailboxSessionUtil.create(USERNAME);
+        MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
+
+        Message.Builder messageBuilder = Message.Builder
+            .of()
+            .setSubject("test")
+            .setBody("testmail", StandardCharsets.UTF_8);
+
+        ComposedMessageId messageId1 = messageManager.appendMessage(
+            MessageManager.AppendCommand.builder().build(
+                messageBuilder
+                    .addField(new RawField("To", "alice-test@domain.tld"))
+                    .build()),
+            session).getId();
+
+        ComposedMessageId messageId2 = messageManager.appendMessage(
+            MessageManager.AppendCommand.builder().build(
+                messageBuilder
+                    .addField(new RawField("To", "bob@other.tld"))
+                    .build()),
+            session).getId();
+
+        elasticSearch.awaitForElasticSearch();
+
+        assertThat(messageManager.search(SearchQuery.of(SearchQuery.address(SearchQuery.AddressType.To, "alice-test")), session))
+            .containsOnly(messageId2.getUid());
+    }
+
+    @Disabled("MAILBOX-401 '-' causes address matching to fail")
+    @Test
+    void addressShouldBeMatchedWhenHyphen() throws Exception {
+        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
+        MailboxSession session = MailboxSessionUtil.create(USERNAME);
+        MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
+
+        Message.Builder messageBuilder = Message.Builder
+            .of()
+            .setSubject("test")
+            .setBody("testmail", StandardCharsets.UTF_8);
+
+        ComposedMessageId messageId1 = messageManager.appendMessage(
+            MessageManager.AppendCommand.builder().build(
+                messageBuilder
+                    .addField(new RawField("To", "alice-test@domain.tld"))
+                    .build()),
+            session).getId();
+
+        ComposedMessageId messageId2 = messageManager.appendMessage(
+            MessageManager.AppendCommand.builder().build(
+                messageBuilder
+                    .addField(new RawField("To", "bob@other.tld"))
+                    .build()),
+            session).getId();
+
+        elasticSearch.awaitForElasticSearch();
+
+        assertThat(messageManager.search(SearchQuery.of(SearchQuery.address(SearchQuery.AddressType.To, "alice-test@domain.tld")), session))
+            .containsOnly(messageId1.getUid());
+    }
+
+    @Disabled("MAILBOX-401 '-' causes address matching to fail")
+    @Test
+    void domainPartShouldBeMatchedWhenHyphen() throws Exception {
+        MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, INBOX);
+        MailboxSession session = MailboxSessionUtil.create(USERNAME);
+        MessageManager messageManager = storeMailboxManager.getMailbox(mailboxPath, session);
+
+        Message.Builder messageBuilder = Message.Builder
+            .of()
+            .setSubject("test")
+            .setBody("testmail", StandardCharsets.UTF_8);
+
+        ComposedMessageId messageId1 = messageManager.appendMessage(
+            MessageManager.AppendCommand.builder().build(
+                messageBuilder
+                    .addField(new RawField("To", "alice@domain-test.tld"))
+                    .build()),
+            session).getId();
+
+        ComposedMessageId messageId2 = messageManager.appendMessage(
+            MessageManager.AppendCommand.builder().build(
+                messageBuilder
+                    .addField(new RawField("To", "bob@other.tld"))
+                    .build()),
+            session).getId();
+
+        elasticSearch.awaitForElasticSearch();
+
+        assertThat(messageManager.search(SearchQuery.of(SearchQuery.address(SearchQuery.AddressType.To, "domain-test.tld")), session))
+            .containsOnly(messageId1.getUid());
+    }
 }
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 06/10: JAMES-3436 Email/set create: Support convenience messageId headers

Posted by bt...@apache.org.
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 08c28e928f34a2043d3bdb25b0bfe44ddd229628
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Oct 27 11:16:53 2020 +0700

    JAMES-3436 Email/set create: Support convenience messageId headers
---
 .../rfc8621/contract/EmailSetMethodContract.scala  | 58 ++++++++++++++++++++++
 .../james/jmap/json/EmailSetSerializer.scala       | 13 ++++-
 .../scala/org/apache/james/jmap/mail/Email.scala   |  8 +--
 .../org/apache/james/jmap/mail/EmailHeader.scala   |  7 ++-
 .../org/apache/james/jmap/mail/EmailSet.scala      |  7 +++
 5 files changed, 86 insertions(+), 7 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/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 c5ed96d..0e2f4bd 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
@@ -530,6 +530,64 @@ trait EmailSetMethodContract {
   }
 
   @Test
+  def createShouldSupportMessageIdHeaders(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
+         |          },
+         |          "references": ["aa@bb", "cc@dd"],
+         |          "inReplyTo": ["ee@ff", "gg@hh"],
+         |          "messageId": ["ii@jj", "kk@ll"]
+         |        }
+         |      }
+         |    }, "c1"],
+         |    ["Email/get",
+         |     {
+         |       "accountId": "$ACCOUNT_ID",
+         |       "ids": ["#aaaaaa"],
+         |       "properties": ["references", "inReplyTo", "messageId"]
+         |     },
+         |     "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"""[{
+           |  "references": ["aa@bb", "cc@dd"],
+           |  "inReplyTo": ["ee@ff", "gg@hh"],
+           |  "messageId": ["ii@jj", "kk@ll"]
+           |}]""".stripMargin)
+  }
+
+  @Test
   def createShouldFailIfForbidden(server: GuiceJamesServer): Unit = {
     val andrePath = MailboxPath.inbox(ANDRE)
     val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
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 a5f0cd2..caacce8 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,12 +23,12 @@ 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.{AddressesHeaderValue, DestroyIds, EmailAddress, EmailCreationRequest, EmailCreationResponse, EmailSetRequest, EmailSetResponse, EmailSetUpdate, EmailerName, MailboxIds, Subject}
+import org.apache.james.jmap.mail.{AddressesHeaderValue, DestroyIds, EmailAddress, EmailCreationRequest, EmailCreationResponse, EmailSetRequest, EmailSetResponse, EmailSetUpdate, EmailerName, HeaderMessageId, MailboxIds, MessageIdsHeaderValue, 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}
 import org.apache.james.mailbox.model.{MailboxId, MessageId}
-import play.api.libs.json.{JsBoolean, JsError, JsNull, JsObject, JsResult, JsString, JsSuccess, JsValue, Json, OWrites, Reads, Writes}
+import play.api.libs.json.{JsArray, JsBoolean, JsError, JsNull, JsObject, JsResult, JsString, JsSuccess, JsValue, Json, OWrites, Reads, Writes}
 
 import scala.util.Try
 
@@ -231,8 +231,17 @@ class EmailSetSerializer @Inject()(messageIdFactory: MessageId.Factory, mailboxI
 
   private implicit val subjectReads: Reads[Subject] = Json.valueReads[Subject]
   private implicit val emailerNameReads: Reads[EmailerName] = Json.valueReads[EmailerName]
+  private implicit val headerMessageIdReads: Reads[HeaderMessageId] = Json.valueReads[HeaderMessageId]
   private implicit val emailAddressReads: Reads[EmailAddress] = Json.reads[EmailAddress]
   private implicit val addressesHeaderValueReads: Reads[AddressesHeaderValue] = Json.valueReads[AddressesHeaderValue]
+  private implicit val messageIdsHeaderValueReads: Reads[MessageIdsHeaderValue] = {
+    case JsArray(value) => value.map(headerMessageIdReads.reads)
+      .map(_.asEither)
+      .toList
+      .sequence
+      .fold(e => JsError(e),
+        ids => JsSuccess(MessageIdsHeaderValue(Some(ids).filter(_.nonEmpty))))
+  }
   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/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index 42c2358..fb93168 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
@@ -276,10 +276,10 @@ object EmailHeaders {
   private def extractMessageId(mime4JMessage: Message, fieldName: String): MessageIdsHeaderValue =
     MessageIdsHeaderValue(
       Option(mime4JMessage.getHeader.getFields(fieldName))
-        .map(_.asScala
-          .map(_.getBody)
-          .map(HeaderMessageId.from)
-          .toList)
+        .map(_.asScala.toList)
+        .flatMap(fields => fields.map(field => MessageIdsHeaderValue.from(field).value)
+          .sequence
+          .map(_.flatten))
         .filter(_.nonEmpty))
 
   private def extractAddresses(mime4JMessage: Message, fieldName: String): 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 02f6645..9b877fb 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
@@ -128,7 +128,12 @@ case class AddressesHeaderValue(value: List[EmailAddress]) extends EmailHeaderVa
   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 MessageIdsHeaderValue(value: Option[List[HeaderMessageId]]) extends EmailHeaderValue {
+  def asString: Option[String] = value.map(messageIds => messageIds
+    .map(_.value)
+    .map(messageId => s"<${messageId}>")
+    .mkString(" "))
+}
 case class DateHeaderValue(value: Option[UTCDate]) extends EmailHeaderValue
 case class URLsHeaderValue(value: Option[List[HeaderURL]]) 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 42d68fc..767f021 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
@@ -33,6 +33,7 @@ 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 org.apache.james.mime4j.stream.RawField
 import play.api.libs.json.JsObject
 
 import scala.jdk.CollectionConverters._
@@ -53,6 +54,9 @@ object EmailSet {
 }
 
 case class EmailCreationRequest(mailboxIds: MailboxIds,
+                                messageId: Option[MessageIdsHeaderValue],
+                                references: Option[MessageIdsHeaderValue],
+                                inReplyTo: Option[MessageIdsHeaderValue],
                                 from: Option[AddressesHeaderValue],
                                 to: Option[AddressesHeaderValue],
                                 cc: Option[AddressesHeaderValue],
@@ -65,6 +69,9 @@ case class EmailCreationRequest(mailboxIds: MailboxIds,
                                 receivedAt: Option[UTCDate]) {
   def toMime4JMessage: Message = {
     val builder = Message.Builder.of
+    references.flatMap(_.asString).map(new RawField("References", _)).foreach(builder.setField)
+    inReplyTo.flatMap(_.asString).map(new RawField("In-Reply-To", _)).foreach(builder.setField)
+    messageId.flatMap(_.asString).map(new RawField(FieldName.MESSAGE_ID, _)).foreach(builder.setField)
     subject.foreach(value => builder.setSubject(value.value))
     from.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setFrom)
     to.flatMap(_.asMime4JMailboxList).map(_.asJava).foreach(builder.setTo)


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 04/10: JAMES-3436 Email/set create: Multiple Sender addresses should be supported

Posted by bt...@apache.org.
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 370abe372817223e6513797360c01b0439733dc9
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Oct 27 09:00:37 2020 +0700

    JAMES-3436 Email/set create: Multiple Sender addresses should be supported
---
 .../rfc8621/contract/EmailGetMethodContract.scala  |  8 +++-
 .../rfc8621/contract/EmailSetMethodContract.scala  | 52 ++++++++++++++++++++++
 .../scala/org/apache/james/jmap/mail/Email.scala   |  7 ++-
 3 files changed, 63 insertions(+), 4 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 47a5c3e..01ae19e 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
@@ -2010,7 +2010,7 @@ trait EmailGetMethodContract {
   }
 
   @Test
-  def senderPropertyShouldKeepFirstValue(server: GuiceJamesServer): Unit = {
+  def senderPropertyShouldDisplayBothValues(server: GuiceJamesServer): Unit = {
     val message: Message = Message.Builder
       .of
       .addField(new RawField("Sender",
@@ -2056,7 +2056,11 @@ trait EmailGetMethodContract {
          |         {
          |             "name": "user1",
          |             "email": "user1@domain.tld"
+         |          },
+         |          {
+         |             "email": "user2@domain.tld"
          |          }
+         |
          |    ]
          |}""".stripMargin)
   }
@@ -6223,7 +6227,7 @@ trait EmailGetMethodContract {
            |     "c1"]]
            |}""".stripMargin)
     .when
-      .post.prettyPeek
+      .post
     .`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 b04ca96..9cfbeec 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
@@ -299,6 +299,58 @@ trait EmailSetMethodContract {
   }
 
   @Test
+  def createWithMultipleSenderShouldNotCrash(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},
+         |          "sender": [{"email": "rcpt4@apache.org"}, {"email": "rcpt3@apache.org"}]
+         |        }
+         |      }
+         |    }, "c1"],
+         |    ["Email/get",
+         |     {
+         |       "accountId": "$ACCOUNT_ID",
+         |       "ids": ["#aaaaaa"],
+         |       "properties": ["sender"]
+         |     },
+         |     "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"""[{
+          |  "sender": [{"email": "rcpt4@apache.org"}, {"email": "rcpt3@apache.org"}]
+          |}]""".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/mail/Email.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/mail/Email.scala
index 0804583..42c2358 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
@@ -45,8 +45,9 @@ import org.apache.james.mailbox.{MailboxSession, MessageIdManager}
 import org.apache.james.mime4j.codec.DecodeMonitor
 import org.apache.james.mime4j.dom.field.{AddressListField, DateTimeField, MailboxField, MailboxListField}
 import org.apache.james.mime4j.dom.{Header, Message}
+import org.apache.james.mime4j.field.AddressListFieldLenientImpl
 import org.apache.james.mime4j.message.DefaultMessageBuilder
-import org.apache.james.mime4j.stream.{Field, MimeConfig}
+import org.apache.james.mime4j.stream.{Field, MimeConfig, RawFieldParser}
 import org.apache.james.mime4j.util.MimeUtil
 import org.slf4j.{Logger, LoggerFactory}
 import reactor.core.scala.publisher.{SFlux, SMono}
@@ -286,7 +287,9 @@ 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).toOption).flatten))
+        case f: MailboxField =>
+          val asMailboxListField = AddressListFieldLenientImpl.PARSER.parse(RawFieldParser.DEFAULT.parseField(f.getRaw), DecodeMonitor.SILENT)
+          Some(AddressesHeaderValue(EmailAddress.from(asMailboxListField.getAddressList)))
         case _ => None
       }
       .filter(_.value.nonEmpty)


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 02/10: JAMES-3436 Email/set create: Support receivedAt

Posted by bt...@apache.org.
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 8e83fe5427419109694c5403c9fcdec507ac3244
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Oct 26 08:45:56 2020 +0100

    JAMES-3436 Email/set create: Support receivedAt
---
 .../rfc8621/contract/EmailSetMethodContract.scala  | 63 ++++++++++++++++++++++
 .../org/apache/james/jmap/mail/EmailSet.scala      |  5 +-
 .../apache/james/jmap/method/EmailSetMethod.scala  | 11 ++--
 3 files changed, 71 insertions(+), 8 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/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 6992bb6..64bbdf6 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
@@ -20,6 +20,8 @@ package org.apache.james.jmap.rfc8621.contract
 
 import java.io.ByteArrayInputStream
 import java.nio.charset.StandardCharsets
+import java.time.ZonedDateTime
+import java.time.format.DateTimeFormatter
 import java.util.Date
 
 import io.netty.handler.codec.http.HttpHeaderNames.ACCEPT
@@ -31,6 +33,7 @@ import org.apache.http.HttpStatus.SC_OK
 import org.apache.james.GuiceJamesServer
 import org.apache.james.jmap.draft.{JmapGuiceProbe, MessageIdProbe}
 import org.apache.james.jmap.http.UserCredential
+import org.apache.james.jmap.model.UTCDate
 import org.apache.james.jmap.rfc8621.contract.Fixture.{ACCEPT_RFC8621_VERSION_HEADER, ACCOUNT_ID, ANDRE, ANDRE_PASSWORD, BOB, BOB_PASSWORD, DOMAIN, authScheme, baseRequestSpecBuilder}
 import org.apache.james.mailbox.FlagsBuilder
 import org.apache.james.mailbox.MessageManager.AppendCommand
@@ -48,6 +51,8 @@ import org.junit.jupiter.params.provider.ValueSource
 import scala.jdk.CollectionConverters._
 
 trait EmailSetMethodContract {
+  private lazy val UTC_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssX")
+
   @BeforeEach
   def setUp(server: GuiceJamesServer): Unit = {
     server.getProbe(classOf[DataProbeImpl])
@@ -295,6 +300,64 @@ trait EmailSetMethodContract {
   }
 
   @Test
+  def createShouldSupportReceivedAt(server: GuiceJamesServer): Unit = {
+    val bobPath = MailboxPath.inbox(BOB)
+    val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobPath)
+    val receivedAt = ZonedDateTime.now().minusDays(1)
+
+    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
+         |          },
+         |          "receivedAt": "${UTCDate(receivedAt).asUTC.format(UTC_DATE_FORMAT)}"
+         |        }
+         |      }
+         |    }, "c1"],
+         |    ["Email/get",
+         |     {
+         |       "accountId": "$ACCOUNT_ID",
+         |       "ids": ["#aaaaaa"],
+         |       "properties": ["mailboxIds", "receivedAt"]
+         |     },
+         |     "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"""[{
+          |  "mailboxIds": {
+          |    "${mailboxId.serialize}": true
+          |  },
+          |  "receivedAt": "${UTCDate(receivedAt).asUTC.format(UTC_DATE_FORMAT)}"
+          |}]""".stripMargin)
+  }
+
+  @Test
   def createShouldFailIfForbidden(server: GuiceJamesServer): Unit = {
     val andrePath = MailboxPath.inbox(ANDRE)
     val mailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(andrePath)
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 baedcf7..90c9c41 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
@@ -27,7 +27,7 @@ import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId}
 import org.apache.james.jmap.method.WithAccountId
 import org.apache.james.jmap.model.Id.Id
 import org.apache.james.jmap.model.State.State
-import org.apache.james.jmap.model.{AccountId, Keywords, SetError}
+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 play.api.libs.json.JsObject
@@ -50,7 +50,8 @@ object EmailSet {
 
 case class EmailCreationRequest(mailboxIds: MailboxIds,
                                 subject: Option[Subject],
-                                keywords: Option[Keywords]) {
+                                keywords: Option[Keywords],
+                                receivedAt: Option[UTCDate]) {
   def toMime4JMessage: Message = {
     val builder = Message.Builder.of
     subject.foreach(value => builder.setSubject(value.value))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
index 759cd30..5cf9af6 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
@@ -18,6 +18,8 @@
  ****************************************************************/
 package org.apache.james.jmap.method
 
+import java.time.ZonedDateTime
+import java.util.Date
 import java.util.function.Consumer
 
 import com.google.common.collect.ImmutableList
@@ -26,22 +28,18 @@ import javax.inject.Inject
 import javax.mail.Flags
 import org.apache.james.jmap.http.SessionSupplier
 import org.apache.james.jmap.json.{EmailSetSerializer, ResponseSerializer}
-import org.apache.james.jmap.mail.EmailSet.UnparsedMessageId
-import org.apache.james.jmap.mail.{DestroyIds, EmailSet, EmailSetRequest, EmailSetResponse, MailboxIds, ValidatedEmailSetUpdate}
 import org.apache.james.jmap.mail.EmailSet.{EmailCreationId, UnparsedMessageId}
-import org.apache.james.jmap.mail.{DestroyIds, EmailCreationRequest, EmailCreationResponse, EmailSet, EmailSetRequest, EmailSetResponse, EmailSetUpdate, MailboxIds, ValidatedEmailSetUpdate}
+import org.apache.james.jmap.mail.{DestroyIds, EmailCreationRequest, EmailCreationResponse, EmailSet, EmailSetRequest, EmailSetResponse, MailboxIds, ValidatedEmailSetUpdate}
 import org.apache.james.jmap.model.CapabilityIdentifier.CapabilityIdentifier
 import org.apache.james.jmap.model.DefaultCapabilities.{CORE_CAPABILITY, MAIL_CAPABILITY}
 import org.apache.james.jmap.model.Invocation.{Arguments, MethodName}
 import org.apache.james.jmap.model.KeywordsFactory.LENIENT_KEYWORDS_FACTORY
 import org.apache.james.jmap.model.SetError.SetErrorDescription
-import org.apache.james.jmap.model.{Capabilities, ClientId, Id, Invocation, ServerId, SetError, State}
+import org.apache.james.jmap.model.{Capabilities, ClientId, Id, Invocation, ServerId, SetError, State, UTCDate}
 import org.apache.james.mailbox.MessageManager.{AppendCommand, FlagsUpdateMode}
 import org.apache.james.mailbox.exception.MailboxNotFoundException
 import org.apache.james.mailbox.model.{ComposedMessageIdWithMetaData, DeleteResult, MailboxId, MessageId, MessageRange}
 import org.apache.james.mailbox.{MailboxManager, MailboxSession, MessageIdManager, MessageManager}
-import org.apache.james.mailbox.model.{ComposedMessageIdWithMetaData, DeleteResult, MailboxId, MessageId}
-import org.apache.james.mailbox.{MailboxManager, MailboxSession, MessageIdManager}
 import org.apache.james.metrics.api.MetricFactory
 import play.api.libs.json.{JsError, JsObject, JsSuccess}
 import reactor.core.scala.publisher.{SFlux, SMono}
@@ -232,6 +230,7 @@ class EmailSetMethod @Inject()(serializer: EmailSetSerializer,
           .appendMessage(AppendCommand.builder()
             .recent()
             .withFlags(request.keywords.map(_.asFlags).getOrElse(new Flags()))
+              .withInternalDate(Date.from(request.receivedAt.getOrElse(UTCDate(ZonedDateTime.now())).asUTC.toInstant))
             .build(request.toMime4JMessage),
             mailboxSession)
         CreationSuccess(clientId, EmailCreationResponse(appendResult.getId.getMessageId))


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org


[james-project] 01/10: JAMES-3436 Email/set create: Support keywords

Posted by bt...@apache.org.
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 17d98a0bc52cd1372911efdbb60bc973129e17f4
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Oct 26 14:24:47 2020 +0700

    JAMES-3436 Email/set create: Support keywords
---
 .../rfc8621/contract/EmailSetMethodContract.scala  | 64 +++++++++++++++++++++-
 .../org/apache/james/jmap/mail/EmailSet.scala      |  3 +-
 .../apache/james/jmap/method/EmailSetMethod.scala  |  1 +
 3 files changed, 66 insertions(+), 2 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/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 0e8595b..6992bb6 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
@@ -223,12 +223,74 @@ trait EmailSetMethodContract {
     assertThatJson(response)
       .whenIgnoringPaths("methodResponses[1][1].list[0].id")
       .inPath(s"methodResponses[1][1].list")
+      .isEqualTo(s"""[{
+          |  "mailboxIds": {
+          |    "${mailboxId.serialize}": true
+          |  },
+          |  "subject": "Boredome comes from a boring mind!"
+          |}]""".stripMargin)
+  }
+
+  @Test
+  def createShouldSupportKeywords(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
+         |          },
+         |          "keywords": {
+         |            "$$answered": true,
+         |            "music": true
+         |          }
+         |        }
+         |      }
+         |    }, "c1"],
+         |    ["Email/get",
+         |     {
+         |       "accountId": "$ACCOUNT_ID",
+         |       "ids": ["#aaaaaa"],
+         |       "properties": ["mailboxIds", "keywords"]
+         |     },
+         |     "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"""[{
           |  "mailboxIds": {
           |    "${mailboxId.serialize}": true
           |  },
-          |  "subject": "Boredome comes from a boring mind!"
+          |  "keywords": {
+          |    "$$answered": true,
+          |    "music": true
+          |  }
           |}]""".stripMargin)
   }
 
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 5985147..baedcf7 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
@@ -49,7 +49,8 @@ object EmailSet {
 }
 
 case class EmailCreationRequest(mailboxIds: MailboxIds,
-                                subject: Option[Subject]) {
+                                subject: Option[Subject],
+                                keywords: Option[Keywords]) {
   def toMime4JMessage: Message = {
     val builder = Message.Builder.of
     subject.foreach(value => builder.setSubject(value.value))
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
index e95abb9..759cd30 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSetMethod.scala
@@ -231,6 +231,7 @@ class EmailSetMethod @Inject()(serializer: EmailSetSerializer,
         val appendResult = mailboxManager.getMailbox(mailboxId, mailboxSession)
           .appendMessage(AppendCommand.builder()
             .recent()
+            .withFlags(request.keywords.map(_.asFlags).getOrElse(new Flags()))
             .build(request.toMime4JMessage),
             mailboxSession)
         CreationSuccess(clientId, EmailCreationResponse(appendResult.getId.getMessageId))


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org