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 58aa72417956fd5e7b548be6c26563b64fdc3bda Author: Rene Cordier <[email protected]> AuthorDate: Fri Aug 29 11:28:17 2025 +0700 JAMES-3340 Add collapseThread option as part of the EmailQueryView API --- .../projections/CassandraEmailQueryView.java | 12 +++--- .../projections/PostgresEmailQueryView.java | 12 +++--- .../PostgresEmailQueryViewManagerRLSTest.java | 5 ++- .../james/jmap/api/projections/EmailQueryView.java | 12 +++--- .../memory/projections/MemoryEmailQueryView.java | 12 +++--- .../api/projections/EmailQueryViewContract.java | 47 +++++++++++----------- .../james/jmap/method/EmailQueryMethod.scala | 27 ++++++++++--- .../event/PopulateEmailQueryViewListenerTest.java | 15 +++---- .../PopulateEmailQueryViewRequestToTaskTest.java | 8 ++-- 9 files changed, 85 insertions(+), 65 deletions(-) diff --git a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/projections/CassandraEmailQueryView.java b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/projections/CassandraEmailQueryView.java index 2f674d5a10..ab838eb895 100644 --- a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/projections/CassandraEmailQueryView.java +++ b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/projections/CassandraEmailQueryView.java @@ -178,7 +178,7 @@ public class CassandraEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSortedBySentAt(MailboxId mailboxId, Limit limit) { + public Flux<MessageId> listMailboxContentSortedBySentAt(MailboxId mailboxId, Limit limit, boolean collapseThreads) { Preconditions.checkArgument(!limit.isUnlimited(), "Limit should be defined"); CassandraId cassandraId = (CassandraId) mailboxId; @@ -189,7 +189,7 @@ public class CassandraEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSortedByReceivedAt(MailboxId mailboxId, Limit limit) { + public Flux<MessageId> listMailboxContentSortedByReceivedAt(MailboxId mailboxId, Limit limit, boolean collapseThreads) { Preconditions.checkArgument(!limit.isUnlimited(), "Limit should be defined"); CassandraId cassandraId = (CassandraId) mailboxId; @@ -200,7 +200,7 @@ public class CassandraEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSinceAfterSortedBySentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentSinceAfterSortedBySentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { Preconditions.checkArgument(!limit.isUnlimited(), "Limit should be defined"); CassandraId cassandraId = (CassandraId) mailboxId; @@ -220,7 +220,7 @@ public class CassandraEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSinceAfterSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentSinceAfterSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { Preconditions.checkArgument(!limit.isUnlimited(), "Limit should be defined"); CassandraId cassandraId = (CassandraId) mailboxId; @@ -233,7 +233,7 @@ public class CassandraEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentBeforeSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentBeforeSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { Preconditions.checkArgument(!limit.isUnlimited(), "Limit should be defined"); CassandraId cassandraId = (CassandraId) mailboxId; @@ -246,7 +246,7 @@ public class CassandraEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSinceSentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentSinceSentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { Preconditions.checkArgument(!limit.isUnlimited(), "Limit should be defined"); CassandraId cassandraId = (CassandraId) mailboxId; diff --git a/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/projections/PostgresEmailQueryView.java b/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/projections/PostgresEmailQueryView.java index 1e1b866faf..4831d046c8 100644 --- a/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/projections/PostgresEmailQueryView.java +++ b/server/data/data-jmap-postgres/src/main/java/org/apache/james/jmap/postgres/projections/PostgresEmailQueryView.java @@ -43,32 +43,32 @@ public class PostgresEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSortedBySentAt(MailboxId mailboxId, Limit limit) { + public Flux<MessageId> listMailboxContentSortedBySentAt(MailboxId mailboxId, Limit limit, boolean collapseThreads) { return emailQueryViewDAO.listMailboxContentSortedBySentAt(PostgresMailboxId.class.cast(mailboxId), limit); } @Override - public Flux<MessageId> listMailboxContentSortedByReceivedAt(MailboxId mailboxId, Limit limit) { + public Flux<MessageId> listMailboxContentSortedByReceivedAt(MailboxId mailboxId, Limit limit, boolean collapseThreads) { return emailQueryViewDAO.listMailboxContentSortedByReceivedAt(PostgresMailboxId.class.cast(mailboxId), limit); } @Override - public Flux<MessageId> listMailboxContentSinceAfterSortedBySentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentSinceAfterSortedBySentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { return emailQueryViewDAO.listMailboxContentSinceAfterSortedBySentAt(PostgresMailboxId.class.cast(mailboxId), since, limit); } @Override - public Flux<MessageId> listMailboxContentSinceAfterSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentSinceAfterSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { return emailQueryViewDAO.listMailboxContentSinceAfterSortedByReceivedAt(PostgresMailboxId.class.cast(mailboxId), since, limit); } @Override - public Flux<MessageId> listMailboxContentBeforeSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentBeforeSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { return emailQueryViewDAO.listMailboxContentBeforeSortedByReceivedAt(PostgresMailboxId.class.cast(mailboxId), since, limit); } @Override - public Flux<MessageId> listMailboxContentSinceSentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentSinceSentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { return emailQueryViewDAO.listMailboxContentSinceSentAt(PostgresMailboxId.class.cast(mailboxId), since, limit); } diff --git a/server/data/data-jmap-postgres/src/test/java/org/apache/james/jmap/postgres/projections/PostgresEmailQueryViewManagerRLSTest.java b/server/data/data-jmap-postgres/src/test/java/org/apache/james/jmap/postgres/projections/PostgresEmailQueryViewManagerRLSTest.java index 99427ba58e..695bc78a92 100644 --- a/server/data/data-jmap-postgres/src/test/java/org/apache/james/jmap/postgres/projections/PostgresEmailQueryViewManagerRLSTest.java +++ b/server/data/data-jmap-postgres/src/test/java/org/apache/james/jmap/postgres/projections/PostgresEmailQueryViewManagerRLSTest.java @@ -41,6 +41,7 @@ public class PostgresEmailQueryViewManagerRLSTest { public static final ThreadId THREAD_ID_1 = ThreadId.fromBaseMessageId(MESSAGE_ID_FACTORY.generate()); private static final ZonedDateTime DATE_1 = ZonedDateTime.parse("2010-10-30T15:12:00Z"); private static final ZonedDateTime DATE_2 = ZonedDateTime.parse("2010-10-30T16:12:00Z"); + private static final boolean NO_COLLAPSE_THREAD = false; @RegisterExtension static PostgresExtension postgresExtension = PostgresExtension.withRowLevelSecurity(PostgresEmailQueryViewDataDefinition.MODULE); @@ -58,7 +59,7 @@ public class PostgresEmailQueryViewManagerRLSTest { emailQueryViewManager.getEmailQueryView(username).save(MAILBOX_ID_1, DATE_1, DATE_2, MESSAGE_ID_1, THREAD_ID_1).block(); - assertThat(emailQueryViewManager.getEmailQueryView(username).listMailboxContentSortedByReceivedAt(MAILBOX_ID_1, Limit.limit(1)).collectList().block()) + assertThat(emailQueryViewManager.getEmailQueryView(username).listMailboxContentSortedByReceivedAt(MAILBOX_ID_1, Limit.limit(1), NO_COLLAPSE_THREAD).collectList().block()) .isNotEmpty(); } @@ -69,7 +70,7 @@ public class PostgresEmailQueryViewManagerRLSTest { emailQueryViewManager.getEmailQueryView(username).save(MAILBOX_ID_1, DATE_1, DATE_2, MESSAGE_ID_1, THREAD_ID_1).block(); - assertThat(emailQueryViewManager.getEmailQueryView(username2).listMailboxContentSortedByReceivedAt(MAILBOX_ID_1, Limit.limit(1)).collectList().block()) + assertThat(emailQueryViewManager.getEmailQueryView(username2).listMailboxContentSortedByReceivedAt(MAILBOX_ID_1, Limit.limit(1), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } } diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/EmailQueryView.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/EmailQueryView.java index e7b1975613..57112768ed 100644 --- a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/EmailQueryView.java +++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/projections/EmailQueryView.java @@ -111,7 +111,7 @@ public interface EmailQueryView { * * @return messageIds of the messages in this mailbox, sorted by sentAt. */ - Flux<MessageId> listMailboxContentSortedBySentAt(MailboxId mailboxId, Limit limit); + Flux<MessageId> listMailboxContentSortedBySentAt(MailboxId mailboxId, Limit limit, boolean collapseThreads); /** * @@ -134,7 +134,7 @@ public interface EmailQueryView { * * @return messageIds of the messages in this mailbox, sorted by receivedAt. */ - Flux<MessageId> listMailboxContentSortedByReceivedAt(MailboxId mailboxId, Limit limit); + Flux<MessageId> listMailboxContentSortedByReceivedAt(MailboxId mailboxId, Limit limit, boolean collapseThreads); /** * Sample JMAP requests: @@ -157,7 +157,7 @@ public interface EmailQueryView { * * @return messageIds of the messages in this mailbox, since being "after". Sorted by sentAt. */ - Flux<MessageId> listMailboxContentSinceAfterSortedBySentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit); + Flux<MessageId> listMailboxContentSinceAfterSortedBySentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads); /** * Sample JMAP requests: @@ -180,7 +180,7 @@ public interface EmailQueryView { * * @return messageIds of the messages in this mailbox, since being "after". Sorted by receivedAt. */ - Flux<MessageId> listMailboxContentSinceAfterSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit); + Flux<MessageId> listMailboxContentSinceAfterSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads); /** * Sample JMAP requests: @@ -203,7 +203,7 @@ public interface EmailQueryView { * * @return messageIds of the messages in this mailbox, since being "after". Sorted by receivedAt. */ - Flux<MessageId> listMailboxContentBeforeSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit); + Flux<MessageId> listMailboxContentBeforeSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads); /** * Sample JMAP requests: @@ -214,7 +214,7 @@ public interface EmailQueryView { * * @return messageIds of the messages in this mailbox, sorted by sentAt, since being sentAt */ - Flux<MessageId> listMailboxContentSinceSentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit); + Flux<MessageId> listMailboxContentSinceSentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads); Mono<Void> delete(MailboxId mailboxId, MessageId messageId); diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/projections/MemoryEmailQueryView.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/projections/MemoryEmailQueryView.java index 27b4493aae..2504f027fe 100644 --- a/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/projections/MemoryEmailQueryView.java +++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/projections/MemoryEmailQueryView.java @@ -47,7 +47,7 @@ public class MemoryEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSortedBySentAt(MailboxId mailboxId, Limit limit) { + public Flux<MessageId> listMailboxContentSortedBySentAt(MailboxId mailboxId, Limit limit, boolean collapseThreads) { Preconditions.checkArgument(!limit.isUnlimited(), "Limit should be defined"); return Flux.fromIterable(entries.row(mailboxId).values()) @@ -57,7 +57,7 @@ public class MemoryEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSinceSentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentSinceSentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { Preconditions.checkArgument(!limit.isUnlimited(), "Limit should be defined"); return Flux.fromIterable(entries.row(mailboxId).values()) @@ -68,7 +68,7 @@ public class MemoryEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSinceAfterSortedBySentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentSinceAfterSortedBySentAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { Preconditions.checkArgument(!limit.isUnlimited(), "Limit should be defined"); return Flux.fromIterable(entries.row(mailboxId).values()) @@ -79,7 +79,7 @@ public class MemoryEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSortedByReceivedAt(MailboxId mailboxId, Limit limit) { + public Flux<MessageId> listMailboxContentSortedByReceivedAt(MailboxId mailboxId, Limit limit, boolean collapseThreads) { return Flux.fromIterable(entries.row(mailboxId).values()) .sort(Comparator.comparing(Entry::getReceivedAt).reversed()) .map(Entry::getMessageId) @@ -87,7 +87,7 @@ public class MemoryEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentSinceAfterSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit) { + public Flux<MessageId> listMailboxContentSinceAfterSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime since, Limit limit, boolean collapseThreads) { return Flux.fromIterable(entries.row(mailboxId).values()) .filter(e -> e.getReceivedAt().isAfter(since) || e.getReceivedAt().isEqual(since)) .sort(Comparator.comparing(Entry::getReceivedAt).reversed()) @@ -96,7 +96,7 @@ public class MemoryEmailQueryView implements EmailQueryView { } @Override - public Flux<MessageId> listMailboxContentBeforeSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime before, Limit limit) { + public Flux<MessageId> listMailboxContentBeforeSortedByReceivedAt(MailboxId mailboxId, ZonedDateTime before, Limit limit, boolean collapseThreads) { return Flux.fromIterable(entries.row(mailboxId).values()) .filter(e -> e.getReceivedAt().isBefore(before) || e.getReceivedAt().isEqual(before)) .sort(Comparator.comparing(Entry::getReceivedAt).reversed()) diff --git a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/projections/EmailQueryViewContract.java b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/projections/EmailQueryViewContract.java index 5283f37939..ea2b3e7e88 100644 --- a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/projections/EmailQueryViewContract.java +++ b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/projections/EmailQueryViewContract.java @@ -32,6 +32,7 @@ import org.apache.james.util.streams.Limit; import org.junit.jupiter.api.Test; public interface EmailQueryViewContract { + boolean NO_COLLAPSE_THREAD = false; ZonedDateTime DATE_1 = ZonedDateTime.parse("2010-10-30T15:12:00Z"); ZonedDateTime DATE_2 = ZonedDateTime.parse("2010-10-30T16:12:00Z"); ZonedDateTime DATE_3 = ZonedDateTime.parse("2010-10-30T17:12:00Z"); @@ -56,7 +57,7 @@ public interface EmailQueryViewContract { @Test default void listMailboxContentShouldReturnEmptyByDefault() { - assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -66,7 +67,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_3, DATE_4, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_2, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId2(), messageId3(), messageId1()); } @@ -76,7 +77,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_3, DATE_4, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_2, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(2)).collectList().block()) + assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(2), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId2(), messageId3()); } @@ -86,7 +87,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_3, DATE_4, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_5, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_3, Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_3, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId3(), messageId2()); } @@ -96,7 +97,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_3, DATE_4, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_5, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_7, Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_7, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -106,7 +107,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_3, DATE_4, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_5, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_1, Limit.limit(2)).collectList().block()) + assertThat(testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_1, Limit.limit(2), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId3(), messageId2()); } @@ -116,7 +117,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_3, DATE_4, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_5, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_2, Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_2, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId3(), messageId2()); } @@ -126,7 +127,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_3, DATE_4, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_5, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_1, Limit.limit(2)).collectList().block()) + assertThat(testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_1, Limit.limit(2), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId3(), messageId2()); } @@ -136,7 +137,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_3, DATE_4, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_5, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_7, Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_7, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -148,7 +149,7 @@ public interface EmailQueryViewContract { testee().delete(mailboxId1()).block(); - assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -160,7 +161,7 @@ public interface EmailQueryViewContract { testee().delete(mailboxId1(), messageId2()).block(); - assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId3(), messageId1()); } @@ -172,7 +173,7 @@ public interface EmailQueryViewContract { testee().delete(mailboxId1()).block(); - assertThat(testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_4, Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_4, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -184,7 +185,7 @@ public interface EmailQueryViewContract { testee().delete(mailboxId1()).block(); - assertThat(testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_4, Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_4, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -193,7 +194,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_1, DATE_2, messageId1(), threadId()).block(); testee().save(mailboxId1(), DATE_1, DATE_2, messageId1(), threadId()).block(); - assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId1()); } @@ -202,7 +203,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_1, DATE_2, messageId1(), threadId()).block(); testee().save(mailboxId1(), DATE_1, DATE_2, messageId2(), threadId()).block(); - assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactlyInAnyOrder(messageId1(), messageId2()); } @@ -214,7 +215,7 @@ public interface EmailQueryViewContract { testee().delete(mailboxId1(), messageId2()).block(); - assertThat(testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_3, Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_3, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId3()); } @@ -226,7 +227,7 @@ public interface EmailQueryViewContract { testee().delete(mailboxId1(), messageId2()).block(); - assertThat(testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_3, Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_3, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId3()); } @@ -236,7 +237,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_2, DATE_3, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_5, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentSortedByReceivedAt(mailboxId1(), Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSortedByReceivedAt(mailboxId1(), Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId3(), messageId1(), messageId2()); } @@ -246,7 +247,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_2, DATE_3, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_5, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentSinceAfterSortedByReceivedAt(mailboxId1(), DATE_4, Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentSinceAfterSortedByReceivedAt(mailboxId1(), DATE_4, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId3(), messageId1()); } @@ -256,7 +257,7 @@ public interface EmailQueryViewContract { testee().save(mailboxId1(), DATE_2, DATE_3, messageId2(), threadId()).block(); testee().save(mailboxId1(), DATE_5, DATE_6, messageId3(), threadId()).block(); - assertThat(testee().listMailboxContentBeforeSortedByReceivedAt(mailboxId1(), DATE_4, Limit.limit(12)).collectList().block()) + assertThat(testee().listMailboxContentBeforeSortedByReceivedAt(mailboxId1(), DATE_4, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsExactly(messageId1(), messageId2()); } @@ -272,19 +273,19 @@ public interface EmailQueryViewContract { @Test default void listMailboxContentShouldThrowOnUndefinedLimit() { - assertThatThrownBy(() -> testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.unlimited()).blockLast()) + assertThatThrownBy(() -> testee().listMailboxContentSortedBySentAt(mailboxId1(), Limit.unlimited(), NO_COLLAPSE_THREAD).blockLast()) .isInstanceOf(IllegalArgumentException.class); } @Test default void listMailboxContentSinceSentAtShouldThrowOnUndefinedLimit() { - assertThatThrownBy(() -> testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_3, Limit.unlimited()).blockLast()) + assertThatThrownBy(() -> testee().listMailboxContentSinceSentAt(mailboxId1(), DATE_3, Limit.unlimited(), NO_COLLAPSE_THREAD).blockLast()) .isInstanceOf(IllegalArgumentException.class); } @Test default void listMailboxContentSinceReceivedAtShouldThrowOnUndefinedLimit() { - assertThatThrownBy(() -> testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_3, Limit.unlimited()).blockLast()) + assertThatThrownBy(() -> testee().listMailboxContentSinceAfterSortedBySentAt(mailboxId1(), DATE_3, Limit.unlimited(), NO_COLLAPSE_THREAD).blockLast()) .isInstanceOf(IllegalArgumentException.class); } } diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala index a24a7e225c..5da2a03994 100644 --- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala +++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailQueryMethod.scala @@ -25,7 +25,7 @@ import eu.timepit.refined.auto._ import jakarta.inject.Inject import jakarta.mail.Flags.Flag.DELETED import org.apache.james.jmap.JMAPConfiguration -import org.apache.james.jmap.api.projections.{EmailQueryView, EmailQueryViewManager} +import org.apache.james.jmap.api.projections.EmailQueryViewManager import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL} import org.apache.james.jmap.core.Invocation.{Arguments, MethodName} import org.apache.james.jmap.core.Limit.Limit @@ -113,9 +113,10 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer, val condition: FilterCondition = request.filter.get.asInstanceOf[FilterCondition] val mailboxId: MailboxId = condition.inMailbox.get val after: ZonedDateTime = condition.after.get.asUTC + val collapseThreads: Boolean = getCollapseThreads(request) val queryViewEntries: SFlux[MessageId] = SFlux.fromPublisher(emailQueryViewManager.getEmailQueryView(mailboxSession.getUser) - .listMailboxContentSinceAfterSortedBySentAt(mailboxId, after, JavaLimit.from(limitToUse.value + position.value))) + .listMailboxContentSinceAfterSortedBySentAt(mailboxId, after, JavaLimit.from(limitToUse.value + position.value), collapseThreads)) fromQueryViewEntries(mailboxId, queryViewEntries, mailboxSession, position, limitToUse, namespace) } @@ -124,8 +125,10 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer, val condition: FilterCondition = request.filter.get.asInstanceOf[FilterCondition] val mailboxId: MailboxId = condition.inMailbox.get val after: ZonedDateTime = condition.after.get.asUTC + val collapseThreads: Boolean = getCollapseThreads(request) + val queryViewEntries: SFlux[MessageId] = SFlux.fromPublisher(emailQueryViewManager.getEmailQueryView(mailboxSession.getUser) - .listMailboxContentSinceAfterSortedByReceivedAt(mailboxId, after, JavaLimit.from(limitToUse.value + position.value))) + .listMailboxContentSinceAfterSortedByReceivedAt(mailboxId, after, JavaLimit.from(limitToUse.value + position.value), collapseThreads)) fromQueryViewEntries(mailboxId, queryViewEntries, mailboxSession, position, limitToUse, namespace) } @@ -134,24 +137,30 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer, val condition: FilterCondition = request.filter.get.asInstanceOf[FilterCondition] val mailboxId: MailboxId = condition.inMailbox.get val before: ZonedDateTime = condition.before.get.asUTC + val collapseThreads: Boolean = getCollapseThreads(request) + val queryViewEntries: SFlux[MessageId] = SFlux.fromPublisher(emailQueryViewManager.getEmailQueryView(mailboxSession.getUser) - .listMailboxContentBeforeSortedByReceivedAt(mailboxId, before, JavaLimit.from(limitToUse.value + position.value))) + .listMailboxContentBeforeSortedByReceivedAt(mailboxId, before, JavaLimit.from(limitToUse.value + position.value), collapseThreads)) fromQueryViewEntries(mailboxId, queryViewEntries, mailboxSession, position, limitToUse, namespace) } private def queryViewForListingSortedBySentAt(mailboxSession: MailboxSession, position: Position, limitToUse: Limit, request: EmailQueryRequest, namespace: Namespace): SMono[Seq[MessageId]] = { val mailboxId: MailboxId = request.filter.get.asInstanceOf[FilterCondition].inMailbox.get + val collapseThreads: Boolean = getCollapseThreads(request) + val queryViewEntries: SFlux[MessageId] = SFlux.fromPublisher(emailQueryViewManager.getEmailQueryView(mailboxSession.getUser) - .listMailboxContentSortedBySentAt(mailboxId, JavaLimit.from(limitToUse.value + position.value))) + .listMailboxContentSortedBySentAt(mailboxId, JavaLimit.from(limitToUse.value + position.value), collapseThreads)) fromQueryViewEntries(mailboxId, queryViewEntries, mailboxSession, position, limitToUse, namespace) } private def queryViewForListingSortedByReceivedAt(mailboxSession: MailboxSession, position: Position, limitToUse: Limit, request: EmailQueryRequest, namespace: Namespace): SMono[Seq[MessageId]] = { val mailboxId: MailboxId = request.filter.get.asInstanceOf[FilterCondition].inMailbox.get + val collapseThreads: Boolean = getCollapseThreads(request) + val queryViewEntries: SFlux[MessageId] = SFlux.fromPublisher(emailQueryViewManager - .getEmailQueryView(mailboxSession.getUser).listMailboxContentSortedByReceivedAt(mailboxId, JavaLimit.from(limitToUse.value + position.value))) + .getEmailQueryView(mailboxSession.getUser).listMailboxContentSortedByReceivedAt(mailboxId, JavaLimit.from(limitToUse.value + position.value), collapseThreads)) fromQueryViewEntries(mailboxId, queryViewEntries, mailboxSession, position, limitToUse, namespace) } @@ -194,6 +203,12 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer, request.filter.exists(_.inMailboxAndBeforeFilterOnly) && request.sort.contains(Set(Comparator.RECEIVED_AT_DESC)) + private def getCollapseThreads(request: EmailQueryRequest): Boolean = + request.collapseThreads match { + case Some(collapseThreads) => collapseThreads.value + case None => false + } + private def toResponse(request: EmailQueryRequest, position: Position, limitToUse: Limit, ids: Seq[MessageId]): EmailQueryResponse = EmailQueryResponse(accountId = request.accountId, queryState = QueryState.forIds(ids), diff --git a/server/protocols/jmap-rfc-8621/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java b/server/protocols/jmap-rfc-8621/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java index bdd6d8e532..f109c64c5b 100644 --- a/server/protocols/jmap-rfc-8621/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java +++ b/server/protocols/jmap-rfc-8621/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java @@ -75,6 +75,7 @@ public class PopulateEmailQueryViewListenerTest { private static final Username BOB = Username.of("bob"); private static final MailboxPath BOB_INBOX_PATH = MailboxPath.inbox(BOB); private static final MailboxPath BOB_OTHER_BOX_PATH = MailboxPath.forUser(BOB, "otherBox"); + private static final boolean NO_COLLAPSE_THREAD = false; MailboxSession mailboxSession; StoreMailboxManager mailboxManager; @@ -143,7 +144,7 @@ public class PopulateEmailQueryViewListenerTest { .build(emptyMessage(Date.from(ZonedDateTime.parse("2014-10-30T14:12:00Z").toInstant()))), mailboxSession).getId(); - assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12)).collectList().block()) + assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsOnly(composedId.getMessageId()); } @@ -156,7 +157,7 @@ public class PopulateEmailQueryViewListenerTest { .build(emptyMessage(Date.from(ZonedDateTime.parse("2014-10-30T14:12:00Z").toInstant()))), mailboxSession).getId(); - assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12)).collectList().block()) + assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -195,7 +196,7 @@ public class PopulateEmailQueryViewListenerTest { Mono.from(queryViewListener.reactiveEvent(addedOutDatedEvent)).block(); - assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(outboxId, Limit.limit(12)).collectList().block()) + assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(outboxId, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -211,7 +212,7 @@ public class PopulateEmailQueryViewListenerTest { inboxMessageManager.setFlags(new Flags(), MessageManager.FlagsUpdateMode.REPLACE, MessageRange.all(), mailboxSession); - assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12)).collectList().block()) + assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .containsOnly(composedId.getMessageId()); } @@ -225,7 +226,7 @@ public class PopulateEmailQueryViewListenerTest { inboxMessageManager.setFlags(new Flags(DELETED), MessageManager.FlagsUpdateMode.REPLACE, MessageRange.all(), mailboxSession); - assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12)).collectList().block()) + assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -239,7 +240,7 @@ public class PopulateEmailQueryViewListenerTest { mailboxManager.deleteMailbox(inboxId, mailboxSession); - assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12)).collectList().block()) + assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -253,7 +254,7 @@ public class PopulateEmailQueryViewListenerTest { inboxMessageManager.delete(ImmutableList.of(composedMessageId.getUid()), mailboxSession); - assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12)).collectList().block()) + assertThat(viewManager.getEmailQueryView(mailboxSession.getUser()).listMailboxContentSortedBySentAt(inboxId, Limit.limit(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } diff --git a/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateEmailQueryViewRequestToTaskTest.java b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateEmailQueryViewRequestToTaskTest.java index a44f96dc23..476e16d152 100644 --- a/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateEmailQueryViewRequestToTaskTest.java +++ b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateEmailQueryViewRequestToTaskTest.java @@ -85,6 +85,8 @@ class PopulateEmailQueryViewRequestToTaskTest { } } + private static final boolean NO_COLLAPSE_THREAD = false; + static final String BASE_PATH = "/:username/mailboxes"; static final DomainList NO_DOMAIN_LIST = null; @@ -265,7 +267,7 @@ class PopulateEmailQueryViewRequestToTaskTest { .basePath(TasksRoutes.BASE) .get(taskId + "/await"); - assertThat(view.listMailboxContentSortedBySentAt(messageId.getMailboxId(), Limit.from(12)).collectList().block()) + assertThat(view.listMailboxContentSortedBySentAt(messageId.getMailboxId(), Limit.from(12), NO_COLLAPSE_THREAD).collectList().block()) .containsOnly(messageId.getMessageId()); } @@ -287,7 +289,7 @@ class PopulateEmailQueryViewRequestToTaskTest { .basePath(TasksRoutes.BASE) .get(taskId + "/await"); - assertThat(view.listMailboxContentSortedBySentAt(messageId.getMailboxId(), Limit.from(12)).collectList().block()) + assertThat(view.listMailboxContentSortedBySentAt(messageId.getMailboxId(), Limit.from(12), NO_COLLAPSE_THREAD).collectList().block()) .isEmpty(); } @@ -315,7 +317,7 @@ class PopulateEmailQueryViewRequestToTaskTest { .basePath(TasksRoutes.BASE) .get(taskId2 + "/await"); - assertThat(view.listMailboxContentSortedBySentAt(messageId.getMailboxId(), Limit.from(12)).collectList().block()) + assertThat(view.listMailboxContentSortedBySentAt(messageId.getMailboxId(), Limit.from(12), NO_COLLAPSE_THREAD).collectList().block()) .containsOnly(messageId.getMessageId()); } } \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
