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 2021/06/14 02:04:37 UTC

[james-project] branch master updated (fe8f9d1 -> bd6277c)

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 fe8f9d1  [PERFORMANCE] Avoid using Flux.from where possible
     new 9465e04  JAMES-3597 Write tests demonstrating deleted messages are not filtered out
     new bd6277c  JAMES-3597 Exclude deleted messages from JMAP Email/query

The 2 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:
 .../mailbox/model/MultimailboxesSearchQuery.java   | 12 ++++
 .../integration/GetMessageListMethodTest.java      | 64 ++++++----------------
 .../jmap/draft/methods/GetMessageListMethod.java   |  4 +-
 .../jmap/event/PopulateEmailQueryViewListener.java | 51 +++++++++++++++--
 .../event/PopulateEmailQueryViewListenerTest.java  | 46 ++++++++++++++++
 .../contract/EmailQueryMethodContract.scala        | 56 +++++++++++++++++++
 .../james/jmap/method/EmailQueryMethod.scala       |  8 ++-
 .../data/jmap/EmailQueryViewPopulator.java         |  4 +-
 .../PopulateEmailQueryViewRequestToTaskTest.java   | 25 +++++++++
 9 files changed, 215 insertions(+), 55 deletions(-)

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


[james-project] 02/02: JAMES-3597 Exclude deleted messages from JMAP Email/query

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 bd6277c44bdbe905c7b28dabe37a5a57959e572a
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Jun 11 13:46:55 2021 +0700

    JAMES-3597 Exclude deleted messages from JMAP Email/query
---
 .../mailbox/model/MultimailboxesSearchQuery.java   | 12 +++++
 .../integration/GetMessageListMethodTest.java      | 48 --------------------
 .../jmap/draft/methods/GetMessageListMethod.java   |  4 +-
 .../jmap/event/PopulateEmailQueryViewListener.java | 51 +++++++++++++++++++---
 .../event/PopulateEmailQueryViewListenerTest.java  | 46 +++++++++++++++++++
 .../contract/EmailQueryMethodContract.scala        |  5 +--
 .../james/jmap/method/EmailQueryMethod.scala       |  8 +++-
 .../data/jmap/EmailQueryViewPopulator.java         |  4 +-
 .../PopulateEmailQueryViewRequestToTaskTest.java   | 25 +++++++++++
 9 files changed, 143 insertions(+), 60 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MultimailboxesSearchQuery.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MultimailboxesSearchQuery.java
index 4785111..7db38f8 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MultimailboxesSearchQuery.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MultimailboxesSearchQuery.java
@@ -171,4 +171,16 @@ public class MultimailboxesSearchQuery {
     public Namespace getNamespace() {
         return namespace;
     }
+
+    public MultimailboxesSearchQuery addCriterion(SearchQuery.Criterion criterion) {
+        return new MultimailboxesSearchQuery(
+            SearchQuery.builder()
+                .andCriteria(searchQuery.getCriteria())
+                .andCriteria(criterion)
+                .sorts(searchQuery.getSorts())
+                .build(),
+            inMailboxes,
+            notInMailboxes,
+            namespace);
+    }
 }
diff --git a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java
index 44ca726..bcdb080 100644
--- a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java
+++ b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java
@@ -94,7 +94,6 @@ import org.apache.james.utils.TestIMAPClient;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
@@ -222,7 +221,6 @@ public abstract class GetMessageListMethodTest {
             .body(ARGUMENTS + ".messageIds", contains(messageId));
     }
 
-    @Ignore("JAMES-3597 Deleted messages are exposed over JMAP")
     @Test
     public void getMessageListShouldFilterMessagesMarkedAsDeleted() throws Exception {
         mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, ALICE.asString(), "mailbox");
@@ -2187,29 +2185,6 @@ public abstract class GetMessageListMethodTest {
     }
 
     @Test
-    public void getMessageListHasKeywordShouldIgnoreDeleted() throws Exception {
-        mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, ALICE.asString(), "mailbox");
-
-        ComposedMessageId messageNotFlagged = mailboxProbe.appendMessage(ALICE.asString(), ALICE_MAILBOX,
-            new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
-        ComposedMessageId messageFlagged = mailboxProbe.appendMessage(ALICE.asString(), ALICE_MAILBOX,
-            new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags(Flags.Flag.DELETED));
-
-        await();
-
-        given()
-            .header("Authorization", aliceAccessToken.asString())
-            .body("[[\"getMessageList\", {\"filter\":{\"hasKeyword\":\"$Deleted\"}}, \"#0\"]]")
-        .when()
-            .post("/jmap")
-        .then()
-            .statusCode(200)
-            .body(NAME, equalTo("messageList"))
-            .body(ARGUMENTS + ".messageIds",
-                containsInAnyOrder(messageFlagged.getMessageId().serialize(), messageNotFlagged.getMessageId().serialize()));
-    }
-
-    @Test
     public void getMessageListHasKeywordShouldIgnoreRecent() throws Exception {
         mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, ALICE.asString(), "mailbox");
 
@@ -2233,29 +2208,6 @@ public abstract class GetMessageListMethodTest {
     }
 
     @Test
-    public void getMessageListNotKeywordShouldIgnoreDeleted() throws Exception {
-        mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, ALICE.asString(), "mailbox");
-
-        ComposedMessageId messageNotFlagged = mailboxProbe.appendMessage(ALICE.asString(), ALICE_MAILBOX,
-            new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags());
-        ComposedMessageId messageFlagged = mailboxProbe.appendMessage(ALICE.asString(), ALICE_MAILBOX,
-            new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()), new Date(), false, new Flags(Flags.Flag.DELETED));
-
-        await();
-
-        given()
-            .header("Authorization", aliceAccessToken.asString())
-            .body("[[\"getMessageList\", {\"filter\":{\"notKeyword\":\"$Deleted\"}}, \"#0\"]]")
-        .when()
-            .post("/jmap")
-        .then()
-            .statusCode(200)
-            .body(NAME, equalTo("messageList"))
-            .body(ARGUMENTS + ".messageIds",
-                containsInAnyOrder(messageFlagged.getMessageId().serialize(), messageNotFlagged.getMessageId().serialize()));
-    }
-
-    @Test
     public void getMessageListNotKeywordShouldIgnoreRecent() throws Exception {
         mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, ALICE.asString(), "mailbox");
 
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
index bbf09d4..e655108 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/methods/GetMessageListMethod.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.jmap.draft.methods;
 
+import static javax.mail.Flags.Flag.DELETED;
 import static org.apache.james.util.ReactorUtils.context;
 
 import java.time.ZonedDateTime;
@@ -221,7 +222,8 @@ public class GetMessageListMethod implements Method {
             .subscribeOn(Schedulers.parallel());
 
         return searchQuery
-            .flatMapMany(Throwing.function(query -> mailboxManager.search(query, mailboxSession, limit)))
+            .flatMapMany(Throwing.function(query ->
+                mailboxManager.search(query.addCriterion(SearchQuery.flagIsUnSet(DELETED)), mailboxSession, limit)))
             .skip(position)
             .reduce(GetMessageListResponse.builder(), GetMessageListResponse.Builder::messageId)
             .map(GetMessageListResponse.Builder::build);
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/PopulateEmailQueryViewListener.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/PopulateEmailQueryViewListener.java
index 8dbf751..722cd52 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/PopulateEmailQueryViewListener.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/PopulateEmailQueryViewListener.java
@@ -19,6 +19,9 @@
 
 package org.apache.james.jmap.event;
 
+import static javax.mail.Flags.Flag.DELETED;
+import static org.apache.james.util.ReactorUtils.publishIfPresent;
+
 import java.io.IOException;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
@@ -35,17 +38,19 @@ import org.apache.james.mailbox.MessageIdManager;
 import org.apache.james.mailbox.SessionProvider;
 import org.apache.james.mailbox.events.MailboxEvents.Added;
 import org.apache.james.mailbox.events.MailboxEvents.Expunged;
+import org.apache.james.mailbox.events.MailboxEvents.FlagsUpdated;
 import org.apache.james.mailbox.events.MailboxEvents.MailboxDeletion;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.FetchGroup;
+import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.MessageResult;
+import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mime4j.dom.Message;
 import org.apache.james.mime4j.stream.MimeConfig;
 import org.reactivestreams.Publisher;
 
-import com.github.fge.lambdas.Throwing;
 import com.google.common.collect.ImmutableList;
 
 import reactor.core.publisher.Flux;
@@ -78,6 +83,7 @@ public class PopulateEmailQueryViewListener implements ReactiveGroupEventListene
     @Override
     public boolean isHandling(Event event) {
         return event instanceof Added
+            || event instanceof FlagsUpdated
             || event instanceof Expunged
             || event instanceof MailboxDeletion;
     }
@@ -90,6 +96,9 @@ public class PopulateEmailQueryViewListener implements ReactiveGroupEventListene
         if (event instanceof Expunged) {
             return handleExpunged((Expunged) event);
         }
+        if (event instanceof FlagsUpdated) {
+            return handleFlagsUpdated((FlagsUpdated) event);
+        }
         if (event instanceof MailboxDeletion) {
             return handleMailboxDeletion((MailboxDeletion) event);
         }
@@ -107,6 +116,31 @@ public class PopulateEmailQueryViewListener implements ReactiveGroupEventListene
             .then();
     }
 
+
+    private Publisher<Void> handleFlagsUpdated(FlagsUpdated flagsUpdated) {
+        MailboxSession session = sessionProvider.createSystemSession(flagsUpdated.getUsername());
+
+        Mono<Void> removeMessagesMarkedAsDeleted = Flux.fromIterable(flagsUpdated.getUpdatedFlags())
+            .filter(updatedFlags -> updatedFlags.isModifiedToSet(DELETED))
+            .map(UpdatedFlags::getMessageId)
+            .handle(publishIfPresent())
+            .concatMap(messageId -> view.delete(flagsUpdated.getMailboxId(), messageId))
+            .then();
+
+        Mono<Void> addMessagesNoLongerMarkedAsDeleted = Flux.fromIterable(flagsUpdated.getUpdatedFlags())
+            .filter(updatedFlags -> updatedFlags.isModifiedToUnset(DELETED))
+            .map(UpdatedFlags::getMessageId)
+            .handle(publishIfPresent())
+            .concatMap(messageId ->
+                Flux.from(messageIdManager.getMessagesReactive(ImmutableList.of(messageId), FetchGroup.HEADERS, session))
+                    .next())
+            .concatMap(message -> handleAdded(flagsUpdated.getMailboxId(), message))
+            .then();
+
+        return removeMessagesMarkedAsDeleted
+            .then(addMessagesNoLongerMarkedAsDeleted);
+    }
+
     private Mono<Void> handleAdded(Added added) {
         MailboxSession session = sessionProvider.createSystemSession(added.getUsername());
         return Flux.fromStream(added.getUids().stream()
@@ -117,14 +151,21 @@ public class PopulateEmailQueryViewListener implements ReactiveGroupEventListene
 
     private Mono<Void> handleAdded(Added added, MessageMetaData messageMetaData, MailboxSession session) {
         MessageId messageId = messageMetaData.getMessageId();
-        ZonedDateTime receivedAt = ZonedDateTime.ofInstant(messageMetaData.getInternalDate().toInstant(), ZoneOffset.UTC);
 
         return Flux.from(messageIdManager.getMessagesReactive(ImmutableList.of(messageId), FetchGroup.HEADERS, session))
             .next()
-            .map(Throwing.function(this::parseMessage))
-            .map(message -> Optional.ofNullable(message.getDate()).orElse(messageMetaData.getInternalDate()))
+            .filter(message -> !message.getFlags().contains(DELETED))
+            .flatMap(messageResult -> handleAdded(added.getMailboxId(), messageResult));
+    }
+
+    public Mono<Void> handleAdded(MailboxId mailboxId, MessageResult messageResult) {
+        ZonedDateTime receivedAt = ZonedDateTime.ofInstant(messageResult.getInternalDate().toInstant(), ZoneOffset.UTC);
+
+        return Mono.fromCallable(() -> parseMessage(messageResult))
+            .map(message -> Optional.ofNullable(message.getDate()).orElse(messageResult.getInternalDate()))
             .map(date -> ZonedDateTime.ofInstant(date.toInstant(), ZoneOffset.UTC))
-            .flatMap(sentAt -> view.save(added.getMailboxId(), sentAt, receivedAt, messageId));
+            .flatMap(sentAt -> view.save(mailboxId, sentAt, receivedAt, messageResult.getMessageId()))
+            .then();
     }
 
     private Message parseMessage(MessageResult messageResult) throws IOException, MailboxException {
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java
index e77ace4..98a75c3 100644
--- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/PopulateEmailQueryViewListenerTest.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.jmap.event;
 
+import static javax.mail.Flags.Flag.DELETED;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.nio.charset.StandardCharsets;
@@ -26,6 +27,8 @@ import java.time.Duration;
 import java.time.ZonedDateTime;
 import java.util.Date;
 
+import javax.mail.Flags;
+
 import org.apache.james.core.Username;
 import org.apache.james.events.Group;
 import org.apache.james.events.InVMEventBus;
@@ -41,6 +44,7 @@ import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.store.FakeAuthenticator;
 import org.apache.james.mailbox.store.FakeAuthorizator;
 import org.apache.james.mailbox.store.SessionProviderImpl;
@@ -129,6 +133,48 @@ public class PopulateEmailQueryViewListenerTest {
     }
 
     @Test
+    void appendingADeletedMessageSHouldNotAddItToTheView() throws Exception {
+        inboxMessageManager.appendMessage(
+            MessageManager.AppendCommand.builder()
+                .withInternalDate(Date.from(ZonedDateTime.parse("2014-10-30T15:12:00Z").toInstant()))
+                .withFlags(new Flags(DELETED))
+                .build(emptyMessage(Date.from(ZonedDateTime.parse("2014-10-30T14:12:00Z").toInstant()))),
+            mailboxSession).getId();
+
+        assertThat(view.listMailboxContent(inboxId, Limit.limit(12)).collectList().block())
+            .isEmpty();
+    }
+
+    @Test
+    void removingDeletedFlagsShouldAddItToTheView() throws Exception {
+        ComposedMessageId composedId = inboxMessageManager.appendMessage(
+            MessageManager.AppendCommand.builder()
+                .withInternalDate(Date.from(ZonedDateTime.parse("2014-10-30T15:12:00Z").toInstant()))
+                .withFlags(new Flags(DELETED))
+                .build(emptyMessage(Date.from(ZonedDateTime.parse("2014-10-30T14:12:00Z").toInstant()))),
+            mailboxSession).getId();
+
+        inboxMessageManager.setFlags(new Flags(), MessageManager.FlagsUpdateMode.REPLACE, MessageRange.all(), mailboxSession);
+
+        assertThat(view.listMailboxContent(inboxId, Limit.limit(12)).collectList().block())
+            .containsOnly(composedId.getMessageId());
+    }
+
+    @Test
+    void addingDeletedFlagsShouldRemoveItToTheView() throws Exception {
+        inboxMessageManager.appendMessage(
+            MessageManager.AppendCommand.builder()
+                .withInternalDate(Date.from(ZonedDateTime.parse("2014-10-30T15:12:00Z").toInstant()))
+                .build(emptyMessage(Date.from(ZonedDateTime.parse("2014-10-30T14:12:00Z").toInstant()))),
+            mailboxSession).getId();
+
+        inboxMessageManager.setFlags(new Flags(DELETED), MessageManager.FlagsUpdateMode.REPLACE, MessageRange.all(), mailboxSession);
+
+        assertThat(view.listMailboxContent(inboxId, Limit.limit(12)).collectList().block())
+            .isEmpty();
+    }
+
+    @Test
     void deletingMailboxShouldClearTheView() throws Exception {
         inboxMessageManager.appendMessage(
             MessageManager.AppendCommand.builder()
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/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index f2b1cbd..ca93414 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -55,7 +55,7 @@ import org.apache.james.util.ClassLoaderUtils
 import org.apache.james.utils.DataProbeImpl
 import org.awaitility.Awaitility
 import org.awaitility.Durations.ONE_HUNDRED_MILLISECONDS
-import org.junit.jupiter.api.{BeforeEach, Disabled, Test}
+import org.junit.jupiter.api.{BeforeEach, Test}
 import org.junit.jupiter.params.ParameterizedTest
 import org.junit.jupiter.params.provider.{Arguments, MethodSource, ValueSource}
 import org.threeten.extra.Seconds
@@ -206,9 +206,8 @@ trait EmailQueryMethodContract {
     }
   }
 
-  @Disabled("JAMES-3597 Deleted messages are exposed over JMAP")
   @Test
-  def messagesMarkedAsDeeltedShouldNotBeExposedOverJMAP(server: GuiceJamesServer): Unit = {
+  def messagesMarkedAsDeletedShouldNotBeExposedOverJMAP(server: GuiceJamesServer): Unit = {
     val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
     mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
 
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 186e80c..ad4cace 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
@@ -23,6 +23,7 @@ import java.time.ZonedDateTime
 import cats.implicits._
 import eu.timepit.refined.auto._
 import javax.inject.Inject
+import javax.mail.Flags.Flag.DELETED
 import org.apache.james.jmap.JMAPConfiguration
 import org.apache.james.jmap.api.projections.EmailQueryView
 import org.apache.james.jmap.core.CapabilityIdentifier.{CapabilityIdentifier, JMAP_CORE, JMAP_MAIL}
@@ -36,7 +37,7 @@ import org.apache.james.jmap.routes.SessionSupplier
 import org.apache.james.jmap.utils.search.MailboxFilter
 import org.apache.james.jmap.utils.search.MailboxFilter.QueryFilter
 import org.apache.james.mailbox.exception.MailboxNotFoundException
-import org.apache.james.mailbox.model.{MailboxId, MessageId, MultimailboxesSearchQuery}
+import org.apache.james.mailbox.model.{MailboxId, MessageId, MultimailboxesSearchQuery, SearchQuery}
 import org.apache.james.mailbox.{MailboxManager, MailboxSession}
 import org.apache.james.metrics.api.MetricFactory
 import org.apache.james.util.streams.{Limit => JavaLimit}
@@ -151,7 +152,10 @@ class EmailQueryMethod @Inject() (serializer: EmailQuerySerializer,
       limit = Some(limitToUse).filterNot(used => request.limit.map(_.value).contains(used.value)))
 
   private def executeQueryAgainstSearchIndex(mailboxSession: MailboxSession, searchQuery: MultimailboxesSearchQuery, position: Position, limitToUse: Limit): SMono[Seq[MessageId]] =
-    SFlux.fromPublisher(mailboxManager.search(searchQuery, mailboxSession, position.value + limitToUse))
+    SFlux.fromPublisher(mailboxManager.search(
+        searchQuery.addCriterion(SearchQuery.flagIsUnSet(DELETED)),
+        mailboxSession,
+        position.value + limitToUse))
       .drop(position.value)
       .collectSeq()
 
diff --git a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/EmailQueryViewPopulator.java b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/EmailQueryViewPopulator.java
index 7a77db5..76c1e0e 100644
--- a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/EmailQueryViewPopulator.java
+++ b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/EmailQueryViewPopulator.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.webadmin.data.jmap;
 
+import static javax.mail.Flags.Flag.DELETED;
 import static org.apache.james.mailbox.MailboxManager.MailboxSearchFetchType.Minimal;
 
 import java.io.IOException;
@@ -134,7 +135,8 @@ public class EmailQueryViewPopulator {
             return Iterators.toFlux(usersRepository.list())
                 .map(mailboxManager::createSystemSession)
                 .doOnNext(any -> progress.incrementProcessedUserCount())
-                .flatMap(session -> listUserMailboxMessages(progress, session), USER_CONCURRENCY);
+                .flatMap(session -> listUserMailboxMessages(progress, session), USER_CONCURRENCY)
+                .filter(messageResult -> !messageResult.getFlags().contains(DELETED));
         } catch (UsersRepositoryException e) {
             return Flux.error(e);
         }
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 402ed7e..4cda472 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
@@ -22,10 +22,13 @@ package org.apache.james.webadmin.data.jmap;
 import static io.restassured.RestAssured.given;
 import static io.restassured.RestAssured.when;
 import static io.restassured.RestAssured.with;
+import static javax.mail.Flags.Flag.DELETED;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
 
+import javax.mail.Flags;
+
 import org.apache.james.core.Username;
 import org.apache.james.domainlist.api.DomainList;
 import org.apache.james.jmap.memory.projections.MemoryEmailQueryView;
@@ -268,6 +271,28 @@ class PopulateEmailQueryViewRequestToTaskTest {
     }
 
     @Test
+    void populateShouldNotUpdateProjectionForDeletedMessages() throws Exception {
+        ComposedMessageId messageId = mailboxManager.getMailbox(bobInboxboxId, bobSession).appendMessage(
+            MessageManager.AppendCommand.builder()
+                .withFlags(new Flags(DELETED))
+                .build("header: value\r\n\r\nbody"),
+            bobSession).getId();
+
+        String taskId = with()
+            .queryParam("action", "populateEmailQueryView")
+            .post()
+            .jsonPath()
+            .get("taskId");
+
+        with()
+            .basePath(TasksRoutes.BASE)
+            .get(taskId + "/await");
+
+        assertThat(view.listMailboxContent(messageId.getMailboxId(), Limit.from(12)).collectList().block())
+            .isEmpty();
+    }
+
+    @Test
     void populateShouldBeIdempotent() throws Exception {
         ComposedMessageId messageId = mailboxManager.getMailbox(bobInboxboxId, bobSession).appendMessage(
             MessageManager.AppendCommand.builder().build("header: value\r\n\r\nbody"),

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


[james-project] 01/02: JAMES-3597 Write tests demonstrating deleted messages are not filtered out

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 9465e043272fbcc123f9e7ee3e7b5990d15c0743
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Jun 11 13:12:55 2021 +0700

    JAMES-3597 Write tests demonstrating deleted messages are not filtered out
---
 .../integration/GetMessageListMethodTest.java      | 20 ++++++++
 .../contract/EmailQueryMethodContract.scala        | 59 +++++++++++++++++++++-
 2 files changed, 78 insertions(+), 1 deletion(-)

diff --git a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java
index 51a989a..44ca726 100644
--- a/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java
+++ b/server/protocols/jmap-draft-integration-testing/jmap-draft-integration-testing-common/src/test/java/org/apache/james/jmap/draft/methods/integration/GetMessageListMethodTest.java
@@ -94,6 +94,7 @@ import org.apache.james.utils.TestIMAPClient;
 import org.hamcrest.Matchers;
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 
@@ -221,6 +222,25 @@ public abstract class GetMessageListMethodTest {
             .body(ARGUMENTS + ".messageIds", contains(messageId));
     }
 
+    @Ignore("JAMES-3597 Deleted messages are exposed over JMAP")
+    @Test
+    public void getMessageListShouldFilterMessagesMarkedAsDeleted() throws Exception {
+        mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, ALICE.asString(), "mailbox");
+
+        mailboxProbe.appendMessage(ALICE.asString(), ALICE_MAILBOX,
+            ClassLoader.getSystemResourceAsStream("eml/twoAttachments.eml"), new Date(), false, new Flags(Flags.Flag.DELETED));
+        await();
+
+        given()
+            .header("Authorization", aliceAccessToken.asString())
+            .body("[[\"getMessageList\", {\"filter\":{\"text\":\"tiramisu\"}}, \"#0\"]]")
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(ARGUMENTS + ".messageIds", empty());
+    }
+
     @Category(BasicFeature.class)
     @Test
     public void searchByFromFieldShouldSupportUTF8FromName() throws Exception {
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/EmailQueryMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
index 24942d5..f2b1cbd 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailQueryMethodContract.scala
@@ -55,7 +55,7 @@ import org.apache.james.util.ClassLoaderUtils
 import org.apache.james.utils.DataProbeImpl
 import org.awaitility.Awaitility
 import org.awaitility.Durations.ONE_HUNDRED_MILLISECONDS
-import org.junit.jupiter.api.{BeforeEach, Test}
+import org.junit.jupiter.api.{BeforeEach, Disabled, Test}
 import org.junit.jupiter.params.ParameterizedTest
 import org.junit.jupiter.params.provider.{Arguments, MethodSource, ValueSource}
 import org.threeten.extra.Seconds
@@ -206,6 +206,63 @@ trait EmailQueryMethodContract {
     }
   }
 
+  @Disabled("JAMES-3597 Deleted messages are exposed over JMAP")
+  @Test
+  def messagesMarkedAsDeeltedShouldNotBeExposedOverJMAP(server: GuiceJamesServer): Unit = {
+    val mailboxProbe = server.getProbe(classOf[MailboxProbeImpl])
+    mailboxProbe.createMailbox(MailboxPath.inbox(BOB))
+
+    mailboxProbe
+      .appendMessage(BOB.asString, MailboxPath.inbox(BOB),
+        AppendCommand.builder()
+          .withFlags(new Flags(Flags.Flag.DELETED))
+          .build(ClassLoaderUtils.getSystemResourceAsSharedStream("eml/multipart_simple.eml")))
+      .getMessageId
+
+    val request =
+      s"""{
+         |  "using": [
+         |    "urn:ietf:params:jmap:core",
+         |    "urn:ietf:params:jmap:mail"],
+         |  "methodCalls": [[
+         |    "Email/query",
+         |    {
+         |      "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |      "filter": {"hasAttachment":true}
+         |    },
+         |    "c1"]]
+         |}""".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).isEqualTo(
+      s"""{
+         |    "sessionState": "${SESSION_STATE.value}",
+         |    "methodResponses": [[
+         |            "Email/query",
+         |            {
+         |                "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6",
+         |                "queryState": "${generateQueryState()}",
+         |                "canCalculateChanges": false,
+         |                "position": 0,
+         |                "limit": 256,
+         |                "ids": []
+         |            },
+         |            "c1"
+         |        ]]
+         |}""".stripMargin)
+  }
+
   @Test
   def emailQueryShouldAcceptMailboxCreationIdForInMailboxOtherThan(server: GuiceJamesServer): Unit = {
     val request =

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