This is an automated email from the ASF dual-hosted git repository.
rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new 12486ef563 JAMES-4016 JMAP email keywords in mixed JMAP/IMAP usage
12486ef563 is described below
commit 12486ef563583e68146df1293d3a675d698c5491
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Mar 29 10:36:30 2024 +0100
JAMES-4016 JMAP email keywords in mixed JMAP/IMAP usage
---
.../org/apache/james/modules/MailboxProbeImpl.java | 13 ++++
.../rfc8621/contract/EmailGetMethodContract.scala | 70 +++++++++++++++++++++-
.../scala/org/apache/james/jmap/mail/Email.scala | 17 ++++--
3 files changed, 93 insertions(+), 7 deletions(-)
diff --git
a/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
b/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
index 645298cddb..b01c6ea653 100644
---
a/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
+++
b/server/container/guice/mailbox/src/main/java/org/apache/james/modules/MailboxProbeImpl.java
@@ -35,6 +35,7 @@ import org.apache.james.core.Username;
import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.MessageUid;
import org.apache.james.mailbox.SubscriptionManager;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.ByteSourceContent;
@@ -43,6 +44,7 @@ import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MailboxMetaData;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.model.MultimailboxesSearchQuery;
import org.apache.james.mailbox.model.search.MailboxQuery;
import org.apache.james.mailbox.model.search.Wildcard;
@@ -166,6 +168,17 @@ public class MailboxProbeImpl implements GuiceProbe,
MailboxProbe {
}
}
+ public void copy(Username username, MailboxPath source, MailboxPath
destination, MessageUid uid) throws MailboxException {
+ MailboxSession mailboxSession =
mailboxManager.createSystemSession(username);
+ mailboxManager.copyMessages(MessageRange.one(uid), source,
destination, mailboxSession);
+ }
+
+ public void setFlags(Username username, MailboxPath mailboxPath,
MessageUid uid, Flags flags) throws MailboxException {
+ MailboxSession mailboxSession =
mailboxManager.createSystemSession(username);
+ MessageManager messageManager = mailboxManager.getMailbox(mailboxPath,
mailboxSession);
+ messageManager.setFlags(flags, MessageManager.FlagsUpdateMode.REPLACE,
MessageRange.one(uid), mailboxSession);
+ }
+
public ComposedMessageId appendMessage(String username, MailboxPath
mailboxPath, MessageManager.AppendCommand appendCommand)
throws MailboxException {
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 ddd8ba8c8c..397e759721 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
@@ -39,14 +39,14 @@ import org.apache.james.jmap.api.change.State
import org.apache.james.jmap.api.model.AccountId
import org.apache.james.jmap.core.ResponseObject.SESSION_STATE
import org.apache.james.jmap.core.UuidState.INSTANCE
-import org.apache.james.jmap.draft.JmapGuiceProbe
+import org.apache.james.jmap.draft.{JmapGuiceProbe}
import org.apache.james.jmap.http.UserCredential
import
org.apache.james.jmap.rfc8621.contract.EmailGetMethodContract.createTestMessage
import
org.apache.james.jmap.rfc8621.contract.Fixture.{ACCEPT_RFC8621_VERSION_HEADER,
ALICE, ANDRE, ANDRE_ACCOUNT_ID, ANDRE_PASSWORD, BOB, BOB_PASSWORD, DOMAIN,
authScheme, baseRequestSpecBuilder}
import org.apache.james.jmap.rfc8621.contract.probe.DelegationProbe
import org.apache.james.mailbox.MessageManager.AppendCommand
import org.apache.james.mailbox.model.MailboxACL.Right
-import org.apache.james.mailbox.model.{MailboxACL, MailboxId, MailboxPath,
MessageId}
+import org.apache.james.mailbox.model.{ComposedMessageId, MailboxACL,
MailboxId, MailboxPath, MessageId}
import org.apache.james.mime4j.dom.Message
import org.apache.james.mime4j.message.MultipartBuilder
import org.apache.james.mime4j.stream.RawField
@@ -6765,6 +6765,72 @@ trait EmailGetMethodContract {
""".stripMargin)
}
+ @Test
+ def shouldAggregateKeywordsAccrossMailbox(server: GuiceJamesServer): Unit = {
+ val message: Message = createTestMessage
+
+ val flags1: Flags = new Flags(Flags.Flag.ANSWERED)
+ flags1.add(Flags.Flag.FLAGGED)
+ flags1.add("f1")
+ flags1.add("f2")
+
+ val flags2: Flags = new Flags(Flags.Flag.SEEN)
+ flags2.add(Flags.Flag.FLAGGED)
+ flags2.add("f3")
+ flags2.add("f2")
+
+ val path1 = MailboxPath.inbox(BOB)
+ val path2 = MailboxPath.forUser(BOB, "box2")
+ val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
+ mailboxProbe.createMailbox(path1)
+ mailboxProbe.createMailbox(path2)
+ val messageId: ComposedMessageId =
mailboxProbe.appendMessage(BOB.asString(), path1, AppendCommand.builder()
+ .withFlags(flags1)
+ .build(message))
+ mailboxProbe.copy(BOB, path1, path2, messageId.getUid)
+ mailboxProbe.setFlags(BOB, path1, messageId.getUid, flags2)
+
+ 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.getMessageId.serialize}"],
+ | "properties": ["keywords"]
+ | },
+ | "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.getMessageId.serialize}",
+ | "keywords": {
+ | "$$flagged": true,
+ | "f1": true,
+ | "f2": true,
+ | "f3": true,
+ | "$$seen": true,
+ | "$$answered": true
+ | }
+ | }
+ """.stripMargin)
+ }
+
@Test
def emailGetShouldReturnSpecificHeadersAsRaw(server: GuiceJamesServer): Unit
= {
val bobPath = MailboxPath.inbox(BOB)
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 40b2b626fc..179e627fe3 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
@@ -68,6 +68,13 @@ import scala.util.{Failure, Success, Try}
object Email {
private val logger: Logger = LoggerFactory.getLogger(classOf[EmailView])
+ def mergeKeywords(messages: Seq[MessageResult]): Try[Keywords] = {
+ messages.map(_.getFlags)
+ .map(LENIENT_KEYWORDS_FACTORY.fromFlags)
+ .sequence
+ .map(list => list.reduce(_ ++ _))
+ }
+
val defaultCharset = Option(System.getenv("james.jmap.default.charset"))
.map(value => java.nio.charset.Charset.forName(value))
.getOrElse(StandardCharsets.US_ASCII)
@@ -498,7 +505,7 @@ private class EmailMetadataViewFactory
@Inject()(zoneIdProvider: ZoneIdProvider)
.map(Success(_))
.getOrElse(Failure(new IllegalArgumentException("No message
supplied")))
blobId <- BlobId.of(messageId)
- keywords <- LENIENT_KEYWORDS_FACTORY.fromFlags(firstMessage.getFlags)
+ keywords <- Email.mergeKeywords(message._2)
} yield {
EmailMetadataView(
metadata = EmailMetadata(
@@ -528,7 +535,7 @@ private class EmailHeaderViewFactory
@Inject()(zoneIdProvider: ZoneIdProvider) e
.getOrElse(Failure(new IllegalArgumentException("No message
supplied")))
mime4JMessage <- Email.parseAsMime4JMessage(firstMessage)
blobId <- BlobId.of(messageId)
- keywords <- LENIENT_KEYWORDS_FACTORY.fromFlags(firstMessage.getFlags)
+ keywords <- Email.mergeKeywords(message._2)
} yield {
EmailHeaderView(
metadata = EmailMetadata(
@@ -605,7 +612,7 @@ private class EmailFullViewFactory
@Inject()(zoneIdProvider: ZoneIdProvider, pre
bodyStructure <- EmailBodyPart.of(request.bodyProperties,
zoneIdProvider.get(), blobId, mime4JMessage)
bodyValues <- extractBodyValues(htmlTextExtractor)(bodyStructure,
request)
preview <- Try(previewFactory.fromMime4JMessage(mime4JMessage))
- keywords <- LENIENT_KEYWORDS_FACTORY.fromFlags(firstMessage.getFlags)
+ keywords <- Email.mergeKeywords(message._2)
} yield {
EmailFullView(
metadata = EmailMetadata(
@@ -753,7 +760,7 @@ private class EmailFastViewReader
@Inject()(messageIdManager: MessageIdManager,
.getOrElse(Failure(new IllegalArgumentException("No message
supplied")))
mime4JMessage <- Email.parseAsMime4JMessage(firstMessage)
blobId <- BlobId.of(messageId)
- keywords <- LENIENT_KEYWORDS_FACTORY.fromFlags(firstMessage.getFlags)
+ keywords <- Email.mergeKeywords(message._2)
} yield {
EmailFastView(
metadata = EmailMetadata(
@@ -844,7 +851,7 @@ private class EmailFastViewWithAttachmentsMetadataReader
@Inject()(messageIdMana
.getOrElse(Failure(new IllegalArgumentException("No message
supplied")))
mime4JMessage <- Email.parseAsMime4JMessage(firstMessage)
blobId <- BlobId.of(messageId)
- keywords <- LENIENT_KEYWORDS_FACTORY.fromFlags(firstMessage.getFlags)
+ keywords <- Email.mergeKeywords(message._2)
} yield {
EmailFastViewWithAttachments(
metadata = EmailMetadata(
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]