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 2023/04/13 04:35:04 UTC

[james-project] branch master updated (3c3824f696 -> c773dccb07)

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 3c3824f696 [PERF] S3 Blob Store should wrap blocking calls (#1515)
     new d55415a4f5 JAMES-3899 WithStorageDirective: Integration test
     new 286bbb181d JAMES-3899 WithStorageDirective: Support flag storage directives
     new ac56437cc4 JAMES-3899 WithStorageDirective: Refactoring - introduce a StorageDirective POJO
     new 6ce94deb80 JAMES-3899 Promote StorageDirective into mailet-api
     new c773dccb07 JAMES-3899 JavaDoc for StorageDirective

The 5 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:
 .../java/org/apache/mailet/StorageDirective.java   | 212 +++++++++++++++++++++
 ...va => WithStorageDirectiveIntegrationTest.java} | 152 ++++++++-------
 .../james/transport/mailets/RandomStoring.java     |   9 +-
 .../james/transport/mailets/ToSenderFolder.java    |   7 +-
 .../transport/mailets/WithStorageDirective.java    |  60 ++++--
 .../transport/mailets/delivery/MailStore.java      |   2 -
 .../mailets/delivery/MailboxAppender.java          |   3 +-
 .../mailets/delivery/MailboxAppenderImpl.java      |  31 +--
 .../mailets/delivery/SimpleMailStore.java          |  34 ++--
 .../mailets/jsieve/delivery/SievePoster.java       |   9 +-
 .../mailets/WithStorageDirectiveTest.java          |  97 ++++++++++
 .../mailets/delivery/MailboxAppenderImplTest.java  |  40 +++-
 .../mailets/delivery/SieveIntegrationTest.java     |   4 +-
 .../mailets/delivery/SimpleMailStoreTest.java      |   6 +-
 .../james/jmap/mailet/filter/ActionApplier.java    |  10 +-
 15 files changed, 531 insertions(+), 145 deletions(-)
 create mode 100644 mailet/api/src/main/java/org/apache/mailet/StorageDirective.java
 copy server/mailet/integration-testing/src/test/java/org/apache/james/mailets/{SmtpAuthIntegrationTest.java => WithStorageDirectiveIntegrationTest.java} (53%)


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


[james-project] 01/05: JAMES-3899 WithStorageDirective: Integration test

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 d55415a4f5997e94ce4f8ff4d018606cbffa7613
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Apr 7 12:31:50 2023 +0700

    JAMES-3899 WithStorageDirective: Integration test
---
 .../WithStorageDirectiveIntegrationTest.java       | 93 ++++++++++++++++++++++
 1 file changed, 93 insertions(+)

diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/WithStorageDirectiveIntegrationTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/WithStorageDirectiveIntegrationTest.java
new file mode 100644
index 0000000000..bf33b13891
--- /dev/null
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/WithStorageDirectiveIntegrationTest.java
@@ -0,0 +1,93 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailets;
+
+import static org.apache.james.mailets.configuration.Constants.DEFAULT_DOMAIN;
+import static org.apache.james.mailets.configuration.Constants.FROM;
+import static org.apache.james.mailets.configuration.Constants.LOCALHOST_IP;
+import static org.apache.james.mailets.configuration.Constants.PASSWORD;
+import static org.apache.james.mailets.configuration.Constants.RECIPIENT;
+import static org.apache.james.mailets.configuration.Constants.awaitAtMostOneMinute;
+
+import java.io.File;
+
+import org.apache.james.mailets.configuration.CommonProcessors;
+import org.apache.james.mailets.configuration.MailetConfiguration;
+import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.modules.protocols.ImapGuiceProbe;
+import org.apache.james.modules.protocols.SmtpGuiceProbe;
+import org.apache.james.probe.DataProbe;
+import org.apache.james.transport.mailets.WithStorageDirective;
+import org.apache.james.transport.matchers.SenderIsLocal;
+import org.apache.james.utils.DataProbeImpl;
+import org.apache.james.utils.SMTPMessageSender;
+import org.apache.james.utils.TestIMAPClient;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.io.TempDir;
+
+class WithStorageDirectiveIntegrationTest {
+    @RegisterExtension
+    public TestIMAPClient testIMAPClient = new TestIMAPClient();
+    @RegisterExtension
+    public SMTPMessageSender messageSender = new SMTPMessageSender(DEFAULT_DOMAIN);
+
+    private TemporaryJamesServer jamesServer;
+
+    @AfterEach
+    void tearDown() {
+        jamesServer.shutdown();
+    }
+
+    @Test
+    void targetFolderNameShouldWork(@TempDir File temporaryFolder) throws Exception {
+        setUp(temporaryFolder, MailetConfiguration.builder()
+            .matcher(SenderIsLocal.class)
+            .mailet(WithStorageDirective.class)
+            .addProperty("targetFolderName", "target"));
+
+        testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(RECIPIENT, PASSWORD)
+            .create("target");
+
+        messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .authenticate(FROM, PASSWORD)
+            .sendMessage(FROM, RECIPIENT);
+
+        testIMAPClient.select("target")
+            .awaitMessage(awaitAtMostOneMinute);
+    }
+
+    private void setUp(File temporaryFolder, MailetConfiguration.Builder mailet) throws Exception {
+        jamesServer = TemporaryJamesServer.builder()
+            .withMailetContainer(TemporaryJamesServer.defaultMailetContainerConfiguration()
+                .putProcessor(ProcessorConfiguration.transport()
+                    .addMailet(mailet)
+                    .addMailetsFrom(CommonProcessors.transport())))
+            .build(temporaryFolder);
+        jamesServer.start();
+
+        DataProbe dataProbe = jamesServer.getProbe(DataProbeImpl.class);
+        dataProbe.addDomain(DEFAULT_DOMAIN);
+        dataProbe.addUser(RECIPIENT, PASSWORD);
+        dataProbe.addUser(FROM, PASSWORD);
+    }
+}


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


[james-project] 03/05: JAMES-3899 WithStorageDirective: Refactoring - introduce a StorageDirective POJO

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 ac56437cc4966ca7c20b8c4b067663f4acf68f44
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Apr 7 14:00:24 2023 +0700

    JAMES-3899 WithStorageDirective: Refactoring - introduce a StorageDirective POJO
---
 .../james/transport/mailets/RandomStoring.java     |   7 +-
 .../james/transport/mailets/ToSenderFolder.java    |   3 +-
 .../transport/mailets/WithStorageDirective.java    |  46 ++---
 .../transport/mailets/delivery/MailStore.java      |   5 -
 .../mailets/delivery/MailboxAppender.java          |   5 +-
 .../mailets/delivery/MailboxAppenderImpl.java      |   7 +-
 .../mailets/delivery/SimpleMailStore.java          |  74 ++------
 .../mailets/delivery/StorageDirective.java         | 196 +++++++++++++++++++++
 .../mailets/jsieve/delivery/SievePoster.java       |   7 +-
 .../mailets/delivery/MailboxAppenderImplTest.java  |  28 ++-
 .../mailets/delivery/SieveIntegrationTest.java     |   4 +-
 .../mailets/delivery/SimpleMailStoreTest.java      |   8 +-
 12 files changed, 265 insertions(+), 125 deletions(-)

diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RandomStoring.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RandomStoring.java
index 0465e68c54..36ba29d60f 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RandomStoring.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RandomStoring.java
@@ -38,6 +38,7 @@ import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.apache.james.transport.mailets.delivery.MailStore;
+import org.apache.james.transport.mailets.delivery.StorageDirective;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.mailet.Attribute;
 import org.apache.mailet.Mail;
@@ -83,7 +84,11 @@ public class RandomStoring extends GenericMailet {
 
         mail.setRecipients(mailAddresses);
         reroutingInfos.forEach(reroutingInfo ->
-            mail.setAttribute(Attribute.convertToAttribute(MailStore.DELIVERY_PATH_PREFIX + reroutingInfo.getUser().asString(), reroutingInfo.getMailbox())));
+                StorageDirective.builder()
+                    .targetFolder(reroutingInfo.getMailbox())
+                    .build()
+                    .encodeAsAttributes(reroutingInfo.getUser())
+                    .forEach(mail::setAttribute));
     }
 
     @Override
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java
index d808b81874..7c95a58955 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java
@@ -29,6 +29,7 @@ import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.transport.mailets.delivery.MailboxAppenderImpl;
+import org.apache.james.transport.mailets.delivery.StorageDirective;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
 import org.apache.mailet.Experimental;
@@ -88,7 +89,7 @@ public class ToSenderFolder extends GenericMailet {
             MailAddress sender = mail.getMaybeSender().get();
             Username username = retrieveUser(sender);
 
-            mailboxAppender.append(mail.getMessage(), username, folder, NO_FLAGS).block();
+            mailboxAppender.append(mail.getMessage(), username, StorageDirective.builder().targetFolder(folder).build()).block();
 
             LOGGER.error("Local delivery with ToSenderFolder mailet for mail {} with sender {} in folder {}", mail.getName(), sender, folder);
         }
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java
index 857f69b48e..c10bb7aafc 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java
@@ -26,20 +26,14 @@ import javax.inject.Inject;
 import javax.mail.MessagingException;
 
 import org.apache.james.core.MailAddress;
-import org.apache.james.core.Username;
-import org.apache.james.transport.mailets.delivery.MailStore;
+import org.apache.james.transport.mailets.delivery.StorageDirective;
 import org.apache.james.user.api.UsersRepository;
-import org.apache.mailet.Attribute;
-import org.apache.mailet.AttributeName;
-import org.apache.mailet.AttributeValue;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.GenericMailet;
 
 import com.github.fge.lambdas.consumers.ThrowingConsumer;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.primitives.Booleans;
 
 /**
  * WithStorageDirective position storage directive for the recipients of this email.
@@ -74,10 +68,7 @@ public class WithStorageDirective extends GenericMailet {
 
     private final UsersRepository usersRepository;
 
-    private Optional<AttributeValue<String>> targetFolderName;
-    private Optional<AttributeValue<Boolean>> seen;
-    private Optional<AttributeValue<Boolean>> important;
-    private Optional<AttributeValue<Collection<AttributeValue<?>>>> keywords;
+    private StorageDirective storageDirective;
 
     @Inject
     public WithStorageDirective(UsersRepository usersRepository) {
@@ -86,28 +77,20 @@ public class WithStorageDirective extends GenericMailet {
 
     @Override
     public void init() throws MessagingException {
-        Preconditions.checkState(
-            Booleans.countTrue(
-                getInitParameterAsOptional(TARGET_FOLDER_NAME).isPresent(),
-                getInitParameterAsOptional(SEEN).isPresent(),
-                getInitParameterAsOptional(IMPORTANT).isPresent(),
-                getInitParameterAsOptional(KEYWORDS).isPresent()) > 0,
-                "Expecting one of the storage directives to be specified: [%s, %s, %s, %s]",
-                TARGET_FOLDER_NAME, SEEN, IMPORTANT, KEYWORDS);
         Preconditions.checkState(validBooleanParameter(getInitParameterAsOptional(SEEN)), "'%s' needs to be a boolean", SEEN);
         Preconditions.checkState(validBooleanParameter(getInitParameterAsOptional(IMPORTANT)), "'%s' needs to be a boolean", IMPORTANT);
 
-        targetFolderName = getInitParameterAsOptional(TARGET_FOLDER_NAME).map(AttributeValue::of);
-        seen = getInitParameterAsOptional(SEEN).map(Boolean::parseBoolean).map(AttributeValue::of);
-        important = getInitParameterAsOptional(IMPORTANT).map(Boolean::parseBoolean).map(AttributeValue::of);
-        keywords = getInitParameterAsOptional(KEYWORDS).map(this::parseKeywords).map(AttributeValue::of);
+        storageDirective = StorageDirective.builder()
+            .targetFolder(getInitParameterAsOptional(TARGET_FOLDER_NAME))
+            .seen(getInitParameterAsOptional(SEEN).map(Boolean::parseBoolean))
+            .important(getInitParameterAsOptional(IMPORTANT).map(Boolean::parseBoolean))
+            .keywords(getInitParameterAsOptional(KEYWORDS).map(this::parseKeywords))
+            .build();
     }
 
-    private Collection<AttributeValue<?>> parseKeywords(String s) {
+    private Collection<String> parseKeywords(String s) {
         return KEYWORD_SPLITTER
-            .splitToStream(s)
-            .map(AttributeValue::of)
-            .collect(ImmutableSet.toImmutableSet());
+            .splitToList(s);
     }
 
     private boolean validBooleanParameter(Optional<String> parameter) {
@@ -122,12 +105,7 @@ public class WithStorageDirective extends GenericMailet {
     }
 
     public ThrowingConsumer<MailAddress> addStorageDirective(Mail mail) {
-        return recipient -> {
-            Username username = usersRepository.getUsername(recipient);
-            targetFolderName.ifPresent(value -> mail.setAttribute(new Attribute(AttributeName.of(MailStore.DELIVERY_PATH_PREFIX + username.asString()), value)));
-            seen.ifPresent(value -> mail.setAttribute(new Attribute(AttributeName.of(MailStore.SEEN_PREFIX + username.asString()), value)));
-            important.ifPresent(value -> mail.setAttribute(new Attribute(AttributeName.of(MailStore.IMPORTANT_PREFIX + username.asString()), value)));
-            keywords.ifPresent(value -> mail.setAttribute(new Attribute(AttributeName.of(MailStore.KEYWORDS_PREFIX + username.asString()), value)));
-        };
+        return recipient -> storageDirective.encodeAsAttributes(usersRepository.getUsername(recipient))
+            .forEach(mail::setAttribute);
     }
 }
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStore.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStore.java
index 22241067e9..04a42aa9f8 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStore.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStore.java
@@ -24,10 +24,5 @@ import org.apache.mailet.Mail;
 import org.reactivestreams.Publisher;
 
 public interface MailStore {
-    String DELIVERY_PATH_PREFIX = "DeliveryPath_";
-    String SEEN_PREFIX = "Seen_";
-    String IMPORTANT_PREFIX = "Important_";
-    String KEYWORDS_PREFIX = "Keywords_";
-
     Publisher<Void> storeMail(MailAddress recipient, Mail mail);
 }
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java
index 0303f20976..d40e817315 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java
@@ -19,9 +19,6 @@
 
 package org.apache.james.transport.mailets.delivery;
 
-import java.util.Optional;
-
-import javax.mail.Flags;
 import javax.mail.MessagingException;
 import javax.mail.internet.MimeMessage;
 
@@ -30,5 +27,5 @@ import org.apache.james.mailbox.model.ComposedMessageId;
 import org.reactivestreams.Publisher;
 
 public interface MailboxAppender {
-    Publisher<ComposedMessageId> append(MimeMessage mail, Username user, String folder, Optional<Flags> flags) throws MessagingException;
+    Publisher<ComposedMessageId> append(MimeMessage mail, Username user, StorageDirective storageDirective) throws MessagingException;
 }
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java
index ab59df51a1..ca0d06f056 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java
@@ -43,6 +43,7 @@ import org.apache.james.server.core.MimeMessageUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
 
 import reactor.core.publisher.Mono;
@@ -56,9 +57,11 @@ public class MailboxAppenderImpl implements MailboxAppender {
         this.mailboxManager = mailboxManager;
     }
 
-    public Mono<ComposedMessageId> append(MimeMessage mail, Username user, String folder, Optional<Flags> flags) throws MessagingException {
+    public Mono<ComposedMessageId> append(MimeMessage mail, Username user, StorageDirective storageDirective) throws MessagingException {
+        Preconditions.checkArgument(storageDirective.getTargetFolder().isPresent(), "'targetFolder' field is needed");
+
         MailboxSession session = createMailboxSession(user);
-        return append(mail, user, useSlashAsSeparator(folder, session), flags, session)
+        return append(mail, user, useSlashAsSeparator(storageDirective.getTargetFolder().get(), session), storageDirective.getFlags(), session)
             .map(AppendResult::getId);
     }
 
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java
index 44d2de771b..d63795f121 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java
@@ -101,16 +101,15 @@ public class SimpleMailStore implements MailStore {
     @Override
     public Mono<Void> storeMail(MailAddress recipient, Mail mail) {
         Username username = computeUsername(recipient);
-        String locatedFolder = locateFolder(username, mail);
+        StorageDirective storageDirective = StorageDirective.fromMail(computeUsername(recipient), mail)
+            .withDefaultFolder(folder);
 
         try {
-            return Mono.from(mailboxAppender.append(mail.getMessage(), username,
-                locateFolder(username, mail),
-                extractFlags(username, mail)))
+            return Mono.from(mailboxAppender.append(mail.getMessage(), username, storageDirective))
                 .doOnSuccess(ids -> {
                     metric.increment();
                     LOGGER.info("Local delivered mail {} with messageId {} successfully from {} to {} in folder {} with composedMessageId {}",
-                        mail.getName(), getMessageId(mail), mail.getMaybeSender().asString(), recipient.asPrettyString(), locatedFolder, ids);
+                        mail.getName(), getMessageId(mail), mail.getMaybeSender().asString(), recipient.asPrettyString(), storageDirective.getTargetFolder().get(), ids);
                 })
                 .then();
         } catch (MessagingException e) {
@@ -118,51 +117,13 @@ public class SimpleMailStore implements MailStore {
         }
     }
 
-    private Optional<Flags> extractFlags(Username username, Mail mail) {
-        Optional<Attribute> seen = mail.getAttribute(AttributeName.of(MailStore.SEEN_PREFIX + username.asString()));
-        Optional<Attribute> important = mail.getAttribute(AttributeName.of(MailStore.IMPORTANT_PREFIX + username.asString()));
-        Optional<Attribute> keywords = mail.getAttribute(AttributeName.of(MailStore.KEYWORDS_PREFIX + username.asString()));
-
-        if (seen.isEmpty() && important.isEmpty() && keywords.isEmpty()) {
-            return Optional.empty();
+    private Username computeUsername(MailAddress recipient) {
+        try {
+            return usersRepository.getUsername(recipient);
+        } catch (UsersRepositoryException e) {
+            LOGGER.warn("Unable to retrieve username for {}", recipient.asPrettyString(), e);
+            return Username.of(recipient.asString());
         }
-
-        Flags flags = new Flags();
-        flags.add(encodeFlag(seen, Flags.Flag.SEEN));
-        flags.add(encodeFlag(important, Flags.Flag.FLAGGED));
-        extractKeywords(keywords).forEach(flags::add);
-
-        return Optional.of(flags);
-    }
-
-    @SuppressWarnings("unchecked")
-    private Stream<String> extractKeywords(Optional<Attribute> keywords) {
-        return keywords
-            .map(Attribute::getValue)
-            .map(AttributeValue::getValue)
-            .filter(Collection.class::isInstance)
-            .map(Collection.class::cast)
-            .stream()
-            .flatMap(Collection::stream)
-            .filter(AttributeValue.class::isInstance)
-            .map(AttributeValue.class::cast)
-            .map(a -> ((AttributeValue<?>) a).getValue())
-            .filter(String.class::isInstance)
-            .map(String.class::cast);
-    }
-
-    private Flags encodeFlag(Optional<Attribute> attr, Flags.Flag flag) {
-        Flags flags = new Flags();
-        attr.map(Attribute::getValue)
-            .map(AttributeValue::getValue)
-            .filter(Boolean.class::isInstance)
-            .map(Boolean.class::cast)
-            .ifPresent(seenFlag -> {
-                if (seenFlag) {
-                    flags.add(flag);
-                }
-            });
-        return flags;
     }
 
     private String getMessageId(Mail mail) {
@@ -173,19 +134,4 @@ public class SimpleMailStore implements MailStore {
             return null;
         }
     }
-
-    private String locateFolder(Username username, Mail mail) {
-        return AttributeUtils
-            .getValueAndCastFromMail(mail, AttributeName.of(DELIVERY_PATH_PREFIX + username.asString()), String.class)
-            .orElse(folder);
-    }
-
-    private Username computeUsername(MailAddress recipient) {
-        try {
-            return usersRepository.getUsername(recipient);
-        } catch (UsersRepositoryException e) {
-            LOGGER.warn("Unable to retrieve username for {}", recipient.asPrettyString(), e);
-            return Username.of(recipient.asString());
-        }
-    }
 }
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/StorageDirective.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/StorageDirective.java
new file mode 100644
index 0000000000..0edb65659e
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/StorageDirective.java
@@ -0,0 +1,196 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+package org.apache.james.transport.mailets.delivery;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.mail.Flags;
+
+import org.apache.james.core.Username;
+import org.apache.mailet.Attribute;
+import org.apache.mailet.AttributeName;
+import org.apache.mailet.AttributeUtils;
+import org.apache.mailet.AttributeValue;
+import org.apache.mailet.Mail;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.primitives.Booleans;
+
+public class StorageDirective {
+    public static class Builder {
+        private Optional<String> targetFolder = Optional.empty();
+        private Optional<Boolean> seen = Optional.empty();
+        private Optional<Boolean> important = Optional.empty();
+        private Optional<Collection<String>> keywords = Optional.empty();
+
+        public Builder seen(Optional<Boolean> value) {
+            this.seen = value;
+            return this;
+        }
+
+        public Builder important(Optional<Boolean> value) {
+            this.important = value;
+            return this;
+        }
+
+        public Builder targetFolder(Optional<String> value) {
+            this.targetFolder = value;
+            return this;
+        }
+
+        public Builder targetFolder(String value) {
+            this.targetFolder = Optional.of(value);
+            return this;
+        }
+
+        public Builder keywords(Optional<Collection<String>> value) {
+            this.keywords = value;
+            return this;
+        }
+
+        public StorageDirective build() {
+            Preconditions.checkState(
+                Booleans.countTrue(
+                    seen.isPresent(),
+                    important.isPresent(),
+                    targetFolder.isPresent(),
+                    keywords.isPresent()) > 0,
+                "Expecting one of the storage directives to be specified: [targetFolder, seen, important, keywords]");
+
+            return new StorageDirective(targetFolder, seen, important, keywords);
+        }
+    }
+
+    private static final String DELIVERY_PATH_PREFIX = "DeliveryPath_";
+    private static final String SEEN_PREFIX = "Seen_";
+    private static final String IMPORTANT_PREFIX = "Important_";
+    private static final String KEYWORDS_PREFIX = "Keywords_";
+
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static StorageDirective fromMail(Username username, Mail mail) {
+        Optional<Attribute> seen = mail.getAttribute(AttributeName.of(SEEN_PREFIX + username.asString()));
+        Optional<Attribute> important = mail.getAttribute(AttributeName.of(IMPORTANT_PREFIX + username.asString()));
+        Optional<Attribute> keywords = mail.getAttribute(AttributeName.of(KEYWORDS_PREFIX + username.asString()));
+
+        return new StorageDirective(
+            locateFolder(username, mail),
+            asBooleanOptional(seen),
+            asBooleanOptional(important),
+            extractKeywords(keywords));
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Optional<Collection<String>> extractKeywords(Optional<Attribute> keywords) {
+        Stream<String> stream = keywords
+            .map(Attribute::getValue)
+            .map(AttributeValue::getValue)
+            .filter(Collection.class::isInstance)
+            .map(Collection.class::cast)
+            .stream()
+            .flatMap(Collection::stream)
+            .filter(AttributeValue.class::isInstance)
+            .map(AttributeValue.class::cast)
+            .map(a -> ((AttributeValue<?>) a).getValue())
+            .filter(String.class::isInstance)
+            .map(String.class::cast);
+        Collection<String> result = stream.collect(ImmutableSet.toImmutableSet());
+
+        return Optional.of(result)
+            .filter(c -> !c.isEmpty());
+    }
+
+    private static Optional<Boolean> asBooleanOptional(Optional<Attribute> attr) {
+        return attr.map(Attribute::getValue)
+            .map(AttributeValue::getValue)
+            .filter(Boolean.class::isInstance)
+            .map(Boolean.class::cast);
+    }
+
+    private static Optional<String> locateFolder(Username username, Mail mail) {
+        return AttributeUtils
+            .getValueAndCastFromMail(mail, AttributeName.of(DELIVERY_PATH_PREFIX + username.asString()), String.class);
+    }
+
+    private final Optional<String> targetFolder;
+    private final Optional<Boolean> seen;
+    private final Optional<Boolean> important;
+    private final Optional<Collection<String>> keywords;
+
+    private StorageDirective(Optional<String> targetFolder,
+                            Optional<Boolean> seen,
+                            Optional<Boolean> important,
+                            Optional<Collection<String>> keywords) {
+        this.targetFolder = targetFolder;
+        this.seen = seen;
+        this.important = important;
+        this.keywords = keywords;
+    }
+
+    public Optional<Flags> getFlags() {
+        if (seen.isEmpty() && important.isEmpty() && keywords.isEmpty()) {
+            return Optional.empty();
+        }
+
+        Flags flags = new Flags();
+        seen.ifPresent(seenFlag -> {
+                if (seenFlag) {
+                    flags.add(Flags.Flag.SEEN);
+                }
+            });
+        important.ifPresent(seenFlag -> {
+                if (seenFlag) {
+                    flags.add(Flags.Flag.FLAGGED);
+                }
+            });
+        keywords.stream().flatMap(Collection::stream).forEach(flags::add);
+        return Optional.of(flags);
+    }
+
+    public Stream<Attribute> encodeAsAttributes(Username username) {
+        return Stream.of(
+            targetFolder.map(value -> new Attribute(AttributeName.of(DELIVERY_PATH_PREFIX + username.asString()), AttributeValue.of(value))),
+            seen.map(value -> new Attribute(AttributeName.of(SEEN_PREFIX + username.asString()), AttributeValue.of(value))),
+            important.map(value -> new Attribute(AttributeName.of(IMPORTANT_PREFIX + username.asString()), AttributeValue.of(value))),
+            keywords.map(value -> new Attribute(AttributeName.of(KEYWORDS_PREFIX + username.asString()), asAttributeValue(value))))
+            .flatMap(Optional::stream);
+    }
+
+    private AttributeValue asAttributeValue(Collection<String> value) {
+        return AttributeValue.of(value.stream()
+            .map(AttributeValue::of)
+            .collect(ImmutableSet.toImmutableSet()));
+    }
+
+    public StorageDirective withDefaultFolder(String folder) {
+        if (targetFolder.isEmpty()) {
+            return new StorageDirective(Optional.of(folder), seen, important, keywords);
+        }
+        return this;
+    }
+
+    public Optional<String> getTargetFolder() {
+        return targetFolder;
+    }
+}
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/delivery/SievePoster.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/delivery/SievePoster.java
index 7e19c2949d..a43aa33932 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/delivery/SievePoster.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/delivery/SievePoster.java
@@ -26,6 +26,7 @@ import javax.mail.MessagingException;
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
 import org.apache.james.transport.mailets.delivery.MailStore;
+import org.apache.james.transport.mailets.delivery.StorageDirective;
 import org.apache.james.transport.mailets.jsieve.Poster;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
@@ -51,7 +52,11 @@ public class SievePoster implements Poster {
             if (scheme.equals("mailbox")) {
                 UserAndPath userAndPath = retrieveUserAndPath(url, endOfScheme);
 
-                mail.setAttribute(Attribute.convertToAttribute(MailStore.DELIVERY_PATH_PREFIX + userAndPath.user, userAndPath.path));
+                StorageDirective.builder()
+                    .targetFolder(userAndPath.path)
+                    .build()
+                    .encodeAsAttributes(Username.of(userAndPath.user))
+                    .forEach(mail::setAttribute);
             } else {
                 throw new MessagingException("Unsupported protocol");
             }
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java
index e9a007975e..46498ea7c6 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java
@@ -49,6 +49,9 @@ class MailboxAppenderImplTest {
 
     public static final Username USER = Username.of("user");
     public static final String FOLDER = "folder";
+    public static final StorageDirective STORAGE_DIRECTIVE = StorageDirective.builder()
+        .targetFolder(FOLDER)
+        .build();
     public static final String EMPTY_FOLDER = "";
     private static final Optional<Flags> NO_FLAGS = Optional.empty();
 
@@ -73,7 +76,7 @@ class MailboxAppenderImplTest {
 
     @Test
     void appendShouldAddMessageToDesiredMailbox() throws Exception {
-        Mono.from(testee.append(mimeMessage, USER, FOLDER, NO_FLAGS)).block();
+        Mono.from(testee.append(mimeMessage, USER, STORAGE_DIRECTIVE)).block();
 
         MessageResultIterator messages = mailboxManager.getMailbox(MailboxPath.forUser(USER, FOLDER), session)
             .getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session);
@@ -87,7 +90,7 @@ class MailboxAppenderImplTest {
         MailboxPath mailboxPath = MailboxPath.forUser(USER, FOLDER);
         mailboxManager.createMailbox(mailboxPath, session);
 
-        Mono.from(testee.append(mimeMessage, USER, FOLDER, NO_FLAGS)).block();
+        Mono.from(testee.append(mimeMessage, USER, STORAGE_DIRECTIVE)).block();
 
         MessageResultIterator messages = mailboxManager.getMailbox(mailboxPath, session)
             .getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session);
@@ -98,13 +101,17 @@ class MailboxAppenderImplTest {
 
     @Test
     void appendShouldNotAppendToEmptyFolder() {
-        assertThatThrownBy(() -> Mono.from(testee.append(mimeMessage, USER, EMPTY_FOLDER, NO_FLAGS)).block())
+        assertThatThrownBy(() -> Mono.from(testee.append(mimeMessage, USER, StorageDirective.builder()
+            .targetFolder(EMPTY_FOLDER)
+            .build())).block())
             .isInstanceOf(MessagingException.class);
     }
 
     @Test
     void appendShouldRemovePathSeparatorAsFirstChar() throws Exception {
-        Mono.from(testee.append(mimeMessage, USER, "." + FOLDER, NO_FLAGS)).block();
+        Mono.from(testee.append(mimeMessage, USER, StorageDirective.builder()
+            .targetFolder("." + FOLDER)
+            .build())).block();
 
         MessageResultIterator messages = mailboxManager.getMailbox(MailboxPath.forUser(USER, FOLDER), session)
             .getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session);
@@ -115,7 +122,10 @@ class MailboxAppenderImplTest {
 
     @Test
     void appendShouldSupportFlags() throws Exception {
-        Mono.from(testee.append(mimeMessage, USER, "." + FOLDER, Optional.of(new Flags(Flags.Flag.SEEN)))).block();
+        Mono.from(testee.append(mimeMessage, USER, StorageDirective.builder()
+            .targetFolder("." + FOLDER)
+            .seen(Optional.of(true))
+            .build())).block();
 
         MessageResultIterator messages = mailboxManager.getMailbox(MailboxPath.forUser(USER, FOLDER), session)
             .getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session);
@@ -125,7 +135,9 @@ class MailboxAppenderImplTest {
 
     @Test
     void appendShouldReplaceSlashBySeparator() throws Exception {
-        Mono.from(testee.append(mimeMessage, USER, FOLDER + "/any", NO_FLAGS)).block();
+        Mono.from(testee.append(mimeMessage, USER, StorageDirective.builder()
+            .targetFolder(FOLDER + "/any")
+            .build())).block();
 
         MessageResultIterator messages = mailboxManager.getMailbox(MailboxPath.forUser(USER, FOLDER + ".any"), session)
             .getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session);
@@ -137,7 +149,9 @@ class MailboxAppenderImplTest {
     @RepeatedTest(20)
     void appendShouldNotFailInConcurrentEnvironment() throws Exception {
         ConcurrentTestRunner.builder()
-            .reactorOperation((a, b) -> Mono.from(testee.append(mimeMessage, USER, FOLDER + "/any", NO_FLAGS)).then())
+            .reactorOperation((a, b) -> Mono.from(testee.append(mimeMessage, USER, StorageDirective.builder()
+                .targetFolder(FOLDER + "/any")
+                .build())).then())
             .threadCount(100)
             .runSuccessfullyWithin(Duration.ofMinutes(1));
     }
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SieveIntegrationTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SieveIntegrationTest.java
index a0ce6b08ca..c2eeffbb95 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SieveIntegrationTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SieveIntegrationTest.java
@@ -66,12 +66,12 @@ class SieveIntegrationTest {
     private static final MailboxPath INBOX = MailboxPath.inbox(LOCAL_USER);
     private static final MailboxPath INBOX_ANY = MailboxPath.forUser(LOCAL_USER, "INBOX.any");
 
-    private static final AttributeName ATTRIBUTE_NAME = AttributeName.of(MailStore.DELIVERY_PATH_PREFIX + LOCAL_PART);
+    private static final AttributeName ATTRIBUTE_NAME = AttributeName.of("DeliveryPath_" + LOCAL_PART);
     private static final Attribute ATTRIBUTE_INBOX = new Attribute(ATTRIBUTE_NAME, AttributeValue.of(expressMailboxNameWithSlash(INBOX.getName())));
     private static final Attribute ATTRIBUTE_INBOX_ANY = new Attribute(ATTRIBUTE_NAME, AttributeValue.of(expressMailboxNameWithSlash(INBOX_ANY.getName())));
     private static final Attribute ATTRIBUTE_SELECTED_MAILBOX = new Attribute(ATTRIBUTE_NAME, AttributeValue.of(expressMailboxNameWithSlash(SELECTED_MAILBOX.getName())));
     private static final Attribute ATTRIBUTE_NOT_SELECTED_MAILBOX = new Attribute(ATTRIBUTE_NAME, AttributeValue.of(expressMailboxNameWithSlash(NOT_SELECTED_MAILBOX.getName())));
-    private static final AttributeName ATTRIBUTE_NAME_DOMAIN = AttributeName.of(MailStore.DELIVERY_PATH_PREFIX + RECEIVER_DOMAIN_COM);
+    private static final AttributeName ATTRIBUTE_NAME_DOMAIN = AttributeName.of("DeliveryPath_" + RECEIVER_DOMAIN_COM);
     private static final Attribute ATTRIBUTE_INBOX_DOMAIN = new Attribute(ATTRIBUTE_NAME_DOMAIN, AttributeValue.of(expressMailboxNameWithSlash(INBOX.getName())));
 
     private Sieve testee;
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SimpleMailStoreTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SimpleMailStoreTest.java
index fabb57a14f..f57f936d89 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SimpleMailStoreTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SimpleMailStoreTest.java
@@ -50,7 +50,7 @@ class SimpleMailStoreTest {
     @BeforeEach
     void setUp() throws Exception {
         mailboxAppender = mock(MailboxAppenderImpl.class);
-        when(mailboxAppender.append(any(), any(), any(), any())).thenReturn(Mono.empty());
+        when(mailboxAppender.append(any(), any(), any())).thenReturn(Mono.empty());
         usersRepository = mock(UsersRepository.class);
         testee = SimpleMailStore.builder()
             .usersRepository(usersRepository)
@@ -76,7 +76,7 @@ class SimpleMailStoreTest {
             .build();
         testee.storeMail(recipient, mail);
 
-        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.asString())), eq(FOLDER), any());
+        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.asString())), any());
     }
 
     @Test
@@ -89,7 +89,7 @@ class SimpleMailStoreTest {
             .build();
         testee.storeMail(recipient, mail);
 
-        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.getLocalPart())), eq(FOLDER), any());
+        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.getLocalPart())), any());
     }
 
     @Test
@@ -102,6 +102,6 @@ class SimpleMailStoreTest {
             .build();
         testee.storeMail(recipient, mail);
 
-        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.toString())), eq(FOLDER), any());
+        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.toString())), any());
     }
 }


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


[james-project] 04/05: JAMES-3899 Promote StorageDirective into mailet-api

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 6ce94deb80945ddaf148a16d1c8ae0318ac97e21
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Apr 7 14:03:31 2023 +0700

    JAMES-3899 Promote StorageDirective into mailet-api
    
    This is a very useful trick: now we can rely on it everywhere and
    fully avoid coupling + manually dealing with attributes.
    
    Note that this is acceptable as this is just a POJO with very limited
    logic (encoding + decoding into mail attributes).
---
 .../api/src/main/java/org/apache/mailet}/StorageDirective.java |  7 +------
 .../java/org/apache/james/transport/mailets/RandomStoring.java |  4 +---
 .../org/apache/james/transport/mailets/ToSenderFolder.java     |  2 +-
 .../apache/james/transport/mailets/WithStorageDirective.java   |  2 +-
 .../james/transport/mailets/delivery/MailboxAppender.java      |  1 +
 .../james/transport/mailets/delivery/MailboxAppenderImpl.java  |  1 +
 .../james/transport/mailets/delivery/SimpleMailStore.java      | 10 +---------
 .../james/transport/mailets/jsieve/delivery/SievePoster.java   |  4 +---
 .../transport/mailets/delivery/MailboxAppenderImplTest.java    |  1 +
 .../org/apache/james/jmap/mailet/filter/ActionApplier.java     | 10 +++++++---
 10 files changed, 16 insertions(+), 26 deletions(-)

diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/StorageDirective.java b/mailet/api/src/main/java/org/apache/mailet/StorageDirective.java
similarity index 96%
rename from server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/StorageDirective.java
rename to mailet/api/src/main/java/org/apache/mailet/StorageDirective.java
index 0edb65659e..f931d041fe 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/StorageDirective.java
+++ b/mailet/api/src/main/java/org/apache/mailet/StorageDirective.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations      *
  * under the License.                                           *
  ****************************************************************/
-package org.apache.james.transport.mailets.delivery;
+package org.apache.mailet;
 
 import java.util.Collection;
 import java.util.Optional;
@@ -25,11 +25,6 @@ import java.util.stream.Stream;
 import javax.mail.Flags;
 
 import org.apache.james.core.Username;
-import org.apache.mailet.Attribute;
-import org.apache.mailet.AttributeName;
-import org.apache.mailet.AttributeUtils;
-import org.apache.mailet.AttributeValue;
-import org.apache.mailet.Mail;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RandomStoring.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RandomStoring.java
index 36ba29d60f..6b817023d3 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RandomStoring.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RandomStoring.java
@@ -37,11 +37,9 @@ import org.apache.james.core.Username;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.model.search.MailboxQuery;
-import org.apache.james.transport.mailets.delivery.MailStore;
-import org.apache.james.transport.mailets.delivery.StorageDirective;
 import org.apache.james.user.api.UsersRepository;
-import org.apache.mailet.Attribute;
 import org.apache.mailet.Mail;
+import org.apache.mailet.StorageDirective;
 import org.apache.mailet.base.GenericMailet;
 
 import com.github.fge.lambdas.Throwing;
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java
index 7c95a58955..cd7a736f35 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java
@@ -29,11 +29,11 @@ import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.transport.mailets.delivery.MailboxAppenderImpl;
-import org.apache.james.transport.mailets.delivery.StorageDirective;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
 import org.apache.mailet.Experimental;
 import org.apache.mailet.Mail;
+import org.apache.mailet.StorageDirective;
 import org.apache.mailet.base.GenericMailet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java
index c10bb7aafc..4cc7a2d1c2 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java
@@ -26,9 +26,9 @@ import javax.inject.Inject;
 import javax.mail.MessagingException;
 
 import org.apache.james.core.MailAddress;
-import org.apache.james.transport.mailets.delivery.StorageDirective;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.mailet.Mail;
+import org.apache.mailet.StorageDirective;
 import org.apache.mailet.base.GenericMailet;
 
 import com.github.fge.lambdas.consumers.ThrowingConsumer;
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java
index d40e817315..4ac279db22 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java
@@ -24,6 +24,7 @@ import javax.mail.internet.MimeMessage;
 
 import org.apache.james.core.Username;
 import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.mailet.StorageDirective;
 import org.reactivestreams.Publisher;
 
 public interface MailboxAppender {
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java
index ca0d06f056..910279bd54 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java
@@ -40,6 +40,7 @@ import org.apache.james.mailbox.model.Content;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.server.core.MimeMessageInputStream;
 import org.apache.james.server.core.MimeMessageUtil;
+import org.apache.mailet.StorageDirective;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java
index d63795f121..c9e1620487 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java
@@ -19,11 +19,6 @@
 
 package org.apache.james.transport.mailets.delivery;
 
-import java.util.Collection;
-import java.util.Optional;
-import java.util.stream.Stream;
-
-import javax.mail.Flags;
 import javax.mail.MessagingException;
 
 import org.apache.james.core.MailAddress;
@@ -31,11 +26,8 @@ import org.apache.james.core.Username;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
-import org.apache.mailet.Attribute;
-import org.apache.mailet.AttributeName;
-import org.apache.mailet.AttributeUtils;
-import org.apache.mailet.AttributeValue;
 import org.apache.mailet.Mail;
+import org.apache.mailet.StorageDirective;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/delivery/SievePoster.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/delivery/SievePoster.java
index a43aa33932..1984c8aeca 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/delivery/SievePoster.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/jsieve/delivery/SievePoster.java
@@ -25,13 +25,11 @@ import javax.mail.MessagingException;
 
 import org.apache.james.core.MailAddress;
 import org.apache.james.core.Username;
-import org.apache.james.transport.mailets.delivery.MailStore;
-import org.apache.james.transport.mailets.delivery.StorageDirective;
 import org.apache.james.transport.mailets.jsieve.Poster;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
-import org.apache.mailet.Attribute;
 import org.apache.mailet.Mail;
+import org.apache.mailet.StorageDirective;
 
 public class SievePoster implements Poster {
     private final String folder;
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java
index 46498ea7c6..9bcc48b68b 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java
@@ -39,6 +39,7 @@ import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MessageResultIterator;
 import org.apache.james.util.concurrency.ConcurrentTestRunner;
+import org.apache.mailet.StorageDirective;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.RepeatedTest;
 import org.junit.jupiter.api.Test;
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
index 4883425292..e9ce7b9954 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/mailet/filter/ActionApplier.java
@@ -30,8 +30,8 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.model.MailboxId;
-import org.apache.mailet.Attribute;
 import org.apache.mailet.Mail;
+import org.apache.mailet.StorageDirective;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -98,8 +98,12 @@ public class ActionApplier {
             MessageManager messageManager = mailboxManager.getMailbox(mailboxId, mailboxSession);
 
             String mailboxName = messageManager.getMailboxPath().getName();
-            String attributeNameForUser = DELIVERY_PATH_PREFIX + username.asString();
-            mail.setAttribute(Attribute.convertToAttribute(attributeNameForUser, mailboxName));
+
+            StorageDirective.builder()
+                .targetFolder(mailboxName)
+                .build()
+                .encodeAsAttributes(username)
+                .forEach(mail::setAttribute);
         } catch (MailboxNotFoundException e) {
             LOGGER.info("Mailbox {} does not exist, but it was mentioned in a JMAP filtering rule", mailboxId, e);
         } catch (Exception e) {


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


[james-project] 02/05: JAMES-3899 WithStorageDirective: Support flag storage directives

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 286bbb181dc105d55e8e10986e3352a91dea6916
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Apr 7 12:33:33 2023 +0700

    JAMES-3899 WithStorageDirective: Support flag storage directives
    
     - Seen
     - Flagged
     - Keyword
---
 .../WithStorageDirectiveIntegrationTest.java       | 62 ++++++++++++++
 .../james/transport/mailets/ToSenderFolder.java    |  6 +-
 .../transport/mailets/WithStorageDirective.java    | 70 +++++++++++++---
 .../transport/mailets/delivery/MailStore.java      |  3 +
 .../mailets/delivery/MailboxAppender.java          |  5 +-
 .../mailets/delivery/MailboxAppenderImpl.java      | 27 +++---
 .../mailets/delivery/SimpleMailStore.java          | 60 ++++++++++++-
 .../mailets/WithStorageDirectiveTest.java          | 97 ++++++++++++++++++++++
 .../mailets/delivery/MailboxAppenderImplTest.java  | 25 ++++--
 .../mailets/delivery/SimpleMailStoreTest.java      |  8 +-
 10 files changed, 325 insertions(+), 38 deletions(-)

diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/WithStorageDirectiveIntegrationTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/WithStorageDirectiveIntegrationTest.java
index bf33b13891..506162e41f 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/WithStorageDirectiveIntegrationTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/WithStorageDirectiveIntegrationTest.java
@@ -25,6 +25,7 @@ import static org.apache.james.mailets.configuration.Constants.LOCALHOST_IP;
 import static org.apache.james.mailets.configuration.Constants.PASSWORD;
 import static org.apache.james.mailets.configuration.Constants.RECIPIENT;
 import static org.apache.james.mailets.configuration.Constants.awaitAtMostOneMinute;
+import static org.assertj.core.api.Assertions.assertThat;
 
 import java.io.File;
 
@@ -76,6 +77,67 @@ class WithStorageDirectiveIntegrationTest {
             .awaitMessage(awaitAtMostOneMinute);
     }
 
+    @Test
+    void seenShouldWork(@TempDir File temporaryFolder) throws Exception {
+        setUp(temporaryFolder, MailetConfiguration.builder()
+            .matcher(SenderIsLocal.class)
+            .mailet(WithStorageDirective.class)
+            .addProperty("seen", "true"));
+
+        testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(RECIPIENT, PASSWORD);
+
+        messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .authenticate(FROM, PASSWORD)
+            .sendMessage(FROM, RECIPIENT);
+
+        testIMAPClient.select("INBOX")
+            .awaitMessage(awaitAtMostOneMinute);
+
+        assertThat(testIMAPClient.hasAMessageWithFlags("\\Seen")).isTrue();
+    }
+
+    @Test
+    void importantShouldWork(@TempDir File temporaryFolder) throws Exception {
+        setUp(temporaryFolder, MailetConfiguration.builder()
+            .matcher(SenderIsLocal.class)
+            .mailet(WithStorageDirective.class)
+            .addProperty("important", "true"));
+
+        testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(RECIPIENT, PASSWORD);
+
+        messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .authenticate(FROM, PASSWORD)
+            .sendMessage(FROM, RECIPIENT);
+
+        testIMAPClient.select("INBOX")
+            .awaitMessage(awaitAtMostOneMinute);
+
+        assertThat(testIMAPClient.hasAMessageWithFlags("\\Flagged")).isTrue();
+    }
+
+    @Test
+    void keywordsShouldWork(@TempDir File temporaryFolder) throws Exception {
+        setUp(temporaryFolder, MailetConfiguration.builder()
+            .matcher(SenderIsLocal.class)
+            .mailet(WithStorageDirective.class)
+            .addProperty("keywords", "abc,def"));
+
+        testIMAPClient.connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+            .login(RECIPIENT, PASSWORD);
+
+        messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+            .authenticate(FROM, PASSWORD)
+            .sendMessage(FROM, RECIPIENT);
+
+        testIMAPClient.select("INBOX")
+            .awaitMessage(awaitAtMostOneMinute);
+
+        assertThat(testIMAPClient.hasAMessageWithFlags("abc")).isTrue();
+        assertThat(testIMAPClient.hasAMessageWithFlags("def")).isTrue();
+    }
+
     private void setUp(File temporaryFolder, MailetConfiguration.Builder mailet) throws Exception {
         jamesServer = TemporaryJamesServer.builder()
             .withMailetContainer(TemporaryJamesServer.defaultMailetContainerConfiguration()
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java
index 311ba38768..d808b81874 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/ToSenderFolder.java
@@ -18,8 +18,11 @@
  ****************************************************************/
 package org.apache.james.transport.mailets;
 
+import java.util.Optional;
+
 import javax.inject.Inject;
 import javax.inject.Named;
+import javax.mail.Flags;
 import javax.mail.MessagingException;
 
 import org.apache.james.core.MailAddress;
@@ -53,6 +56,7 @@ import org.slf4j.LoggerFactory;
 @Experimental
 public class ToSenderFolder extends GenericMailet {
     private static final Logger LOGGER = LoggerFactory.getLogger(ToSenderFolder.class);
+    private static final Optional<Flags> NO_FLAGS = Optional.empty();
 
     private final UsersRepository usersRepository;
     private final MailboxManager mailboxManager;
@@ -84,7 +88,7 @@ public class ToSenderFolder extends GenericMailet {
             MailAddress sender = mail.getMaybeSender().get();
             Username username = retrieveUser(sender);
 
-            mailboxAppender.append(mail.getMessage(), username, folder).block();
+            mailboxAppender.append(mail.getMessage(), username, folder, NO_FLAGS).block();
 
             LOGGER.error("Local delivery with ToSenderFolder mailet for mail {} with sender {} in folder {}", mail.getName(), sender, folder);
         }
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java
index 178b51aeae..857f69b48e 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/WithStorageDirective.java
@@ -19,10 +19,14 @@
 
 package org.apache.james.transport.mailets;
 
+import java.util.Collection;
+import java.util.Optional;
+
 import javax.inject.Inject;
 import javax.mail.MessagingException;
 
 import org.apache.james.core.MailAddress;
+import org.apache.james.core.Username;
 import org.apache.james.transport.mailets.delivery.MailStore;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.mailet.Attribute;
@@ -33,7 +37,9 @@ import org.apache.mailet.base.GenericMailet;
 
 import com.github.fge.lambdas.consumers.ThrowingConsumer;
 import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.primitives.Booleans;
 
 /**
  * WithStorageDirective position storage directive for the recipients of this email.
@@ -41,21 +47,37 @@ import com.google.common.base.Strings;
  * These directives are used by <strong>LocalDelivery</strong> mailet when adding the email to the recipients mailboxes.
  *
  * The following storage directives can be set:
- *  - targetFolderName: the folder to append the email in. (compulsory)
+ *  - targetFolderName: the folder to append the email in. Defaults to none (INBOX).
+ *  - seen: boolean, whether the message should be automatically marked as seen. Defaults to false.
+ *  - important: boolean, whether the message should be automatically marked as important. Defaults to false.
+ *  - keywords: set of string, encoded as a string (value are coma separated). IMAP user flags to set for the message. Defaults to none.
+ *
+ *  At least one of the storage directives should be set.
  *
  *  Example:
  *
  *  <mailet match="IsMarkedAsSpam" class="WithStorageDirective">
  *      <targetFolderName>Spam</targetFolderName>
+ *      <seen>true</seen>
+ *      <important>true</important>
+ *      <keywords>keyword1,keyword2</targetFolderName>
  *  </mailet>
  */
 public class WithStorageDirective extends GenericMailet {
-
-    public static final String TARGET_FOLDER_NAME = "targetFolderName";
+    static final String TARGET_FOLDER_NAME = "targetFolderName";
+    static final String SEEN = "seen";
+    static final String IMPORTANT = "important";
+    static final String KEYWORDS = "keywords";
+    private static final Splitter KEYWORD_SPLITTER = Splitter.on(',')
+        .omitEmptyStrings()
+        .trimResults();
 
     private final UsersRepository usersRepository;
 
-    private AttributeValue<String> targetFolderName;
+    private Optional<AttributeValue<String>> targetFolderName;
+    private Optional<AttributeValue<Boolean>> seen;
+    private Optional<AttributeValue<Boolean>> important;
+    private Optional<AttributeValue<Collection<AttributeValue<?>>>> keywords;
 
     @Inject
     public WithStorageDirective(UsersRepository usersRepository) {
@@ -64,13 +86,33 @@ public class WithStorageDirective extends GenericMailet {
 
     @Override
     public void init() throws MessagingException {
-        targetFolderName = AttributeValue.of(validateMailetConfiguration(TARGET_FOLDER_NAME));
+        Preconditions.checkState(
+            Booleans.countTrue(
+                getInitParameterAsOptional(TARGET_FOLDER_NAME).isPresent(),
+                getInitParameterAsOptional(SEEN).isPresent(),
+                getInitParameterAsOptional(IMPORTANT).isPresent(),
+                getInitParameterAsOptional(KEYWORDS).isPresent()) > 0,
+                "Expecting one of the storage directives to be specified: [%s, %s, %s, %s]",
+                TARGET_FOLDER_NAME, SEEN, IMPORTANT, KEYWORDS);
+        Preconditions.checkState(validBooleanParameter(getInitParameterAsOptional(SEEN)), "'%s' needs to be a boolean", SEEN);
+        Preconditions.checkState(validBooleanParameter(getInitParameterAsOptional(IMPORTANT)), "'%s' needs to be a boolean", IMPORTANT);
+
+        targetFolderName = getInitParameterAsOptional(TARGET_FOLDER_NAME).map(AttributeValue::of);
+        seen = getInitParameterAsOptional(SEEN).map(Boolean::parseBoolean).map(AttributeValue::of);
+        important = getInitParameterAsOptional(IMPORTANT).map(Boolean::parseBoolean).map(AttributeValue::of);
+        keywords = getInitParameterAsOptional(KEYWORDS).map(this::parseKeywords).map(AttributeValue::of);
     }
 
-    private String validateMailetConfiguration(String initParameterName) {
-        String initParameterValue = getInitParameter(initParameterName);
-        Preconditions.checkState(!Strings.isNullOrEmpty(initParameterValue), "You need to specify %s", initParameterName);
-        return initParameterValue;
+    private Collection<AttributeValue<?>> parseKeywords(String s) {
+        return KEYWORD_SPLITTER
+            .splitToStream(s)
+            .map(AttributeValue::of)
+            .collect(ImmutableSet.toImmutableSet());
+    }
+
+    private boolean validBooleanParameter(Optional<String> parameter) {
+        return parameter.map(v -> v.equals("true") || v.equals("false"))
+            .orElse(true);
     }
 
     @Override
@@ -81,9 +123,11 @@ public class WithStorageDirective extends GenericMailet {
 
     public ThrowingConsumer<MailAddress> addStorageDirective(Mail mail) {
         return recipient -> {
-            AttributeName attributeNameForUser = AttributeName.of(MailStore.DELIVERY_PATH_PREFIX + usersRepository.getUsername(recipient).asString());
-            mail.setAttribute(new Attribute(attributeNameForUser, targetFolderName));
+            Username username = usersRepository.getUsername(recipient);
+            targetFolderName.ifPresent(value -> mail.setAttribute(new Attribute(AttributeName.of(MailStore.DELIVERY_PATH_PREFIX + username.asString()), value)));
+            seen.ifPresent(value -> mail.setAttribute(new Attribute(AttributeName.of(MailStore.SEEN_PREFIX + username.asString()), value)));
+            important.ifPresent(value -> mail.setAttribute(new Attribute(AttributeName.of(MailStore.IMPORTANT_PREFIX + username.asString()), value)));
+            keywords.ifPresent(value -> mail.setAttribute(new Attribute(AttributeName.of(MailStore.KEYWORDS_PREFIX + username.asString()), value)));
         };
-
     }
 }
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStore.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStore.java
index 598f934658..22241067e9 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStore.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailStore.java
@@ -25,6 +25,9 @@ import org.reactivestreams.Publisher;
 
 public interface MailStore {
     String DELIVERY_PATH_PREFIX = "DeliveryPath_";
+    String SEEN_PREFIX = "Seen_";
+    String IMPORTANT_PREFIX = "Important_";
+    String KEYWORDS_PREFIX = "Keywords_";
 
     Publisher<Void> storeMail(MailAddress recipient, Mail mail);
 }
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java
index 097b073d3e..0303f20976 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppender.java
@@ -19,6 +19,9 @@
 
 package org.apache.james.transport.mailets.delivery;
 
+import java.util.Optional;
+
+import javax.mail.Flags;
 import javax.mail.MessagingException;
 import javax.mail.internet.MimeMessage;
 
@@ -27,5 +30,5 @@ import org.apache.james.mailbox.model.ComposedMessageId;
 import org.reactivestreams.Publisher;
 
 public interface MailboxAppender {
-    Publisher<ComposedMessageId> append(MimeMessage mail, Username user, String folder) throws MessagingException;
+    Publisher<ComposedMessageId> append(MimeMessage mail, Username user, String folder, Optional<Flags> flags) throws MessagingException;
 }
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java
index 76e500fd6e..ab59df51a1 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImpl.java
@@ -21,7 +21,9 @@ package org.apache.james.transport.mailets.delivery;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Optional;
 
+import javax.mail.Flags;
 import javax.mail.MessagingException;
 import javax.mail.internet.MimeMessage;
 
@@ -54,9 +56,9 @@ public class MailboxAppenderImpl implements MailboxAppender {
         this.mailboxManager = mailboxManager;
     }
 
-    public Mono<ComposedMessageId> append(MimeMessage mail, Username user, String folder) throws MessagingException {
+    public Mono<ComposedMessageId> append(MimeMessage mail, Username user, String folder, Optional<Flags> flags) throws MessagingException {
         MailboxSession session = createMailboxSession(user);
-        return append(mail, user, useSlashAsSeparator(folder, session), session)
+        return append(mail, user, useSlashAsSeparator(folder, session), flags, session)
             .map(AppendResult::getId);
     }
 
@@ -71,26 +73,29 @@ public class MailboxAppenderImpl implements MailboxAppender {
         return destination;
     }
 
-    private Mono<AppendResult> append(MimeMessage mail, Username user, String folder, MailboxSession mailboxSession) {
+    private Mono<AppendResult> append(MimeMessage mail, Username user, String folder, Optional<Flags> flags, MailboxSession mailboxSession) {
         MailboxPath mailboxPath = MailboxPath.forUser(user, folder);
         return Mono.using(
             () -> {
                 mailboxManager.startProcessingRequest(mailboxSession);
                 return mailboxSession;
             },
-            session -> appendMessageToMailbox(mail, session, mailboxPath),
+            session -> appendMessageToMailbox(mail, session, mailboxPath, flags),
             this::closeProcessing)
             .onErrorMap(MailboxException.class, e -> new MessagingException("Unable to access mailbox.", e));
     }
 
-    protected Mono<AppendResult> appendMessageToMailbox(MimeMessage mail, MailboxSession session, MailboxPath path) {
+    protected Mono<AppendResult> appendMessageToMailbox(MimeMessage mail, MailboxSession session, MailboxPath path, Optional<Flags> flags) {
         return createMailboxIfNotExist(session, path)
-            .flatMap(mailbox -> Mono.from(mailbox.appendMessageReactive(
-                MessageManager.AppendCommand.builder()
-                    .recent()
-                    .delivery()
-                    .build(extractContent(mail)),
-                session)));
+            .flatMap(mailbox -> Mono.from(mailbox.appendMessageReactive(appendCommand(flags).build(extractContent(mail)), session)));
+    }
+
+    private MessageManager.AppendCommand.Builder appendCommand(Optional<Flags> flags) {
+        MessageManager.AppendCommand.Builder builder = MessageManager.AppendCommand.builder()
+            .recent()
+            .delivery();
+        return flags.map(builder::withFlags)
+            .orElse(builder);
     }
 
     private Content extractContent(MimeMessage mail) {
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java
index 96edbbeb4a..44d2de771b 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/delivery/SimpleMailStore.java
@@ -19,6 +19,11 @@
 
 package org.apache.james.transport.mailets.delivery;
 
+import java.util.Collection;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.mail.Flags;
 import javax.mail.MessagingException;
 
 import org.apache.james.core.MailAddress;
@@ -26,8 +31,10 @@ import org.apache.james.core.Username;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
+import org.apache.mailet.Attribute;
 import org.apache.mailet.AttributeName;
 import org.apache.mailet.AttributeUtils;
+import org.apache.mailet.AttributeValue;
 import org.apache.mailet.Mail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -97,7 +104,9 @@ public class SimpleMailStore implements MailStore {
         String locatedFolder = locateFolder(username, mail);
 
         try {
-            return Mono.from(mailboxAppender.append(mail.getMessage(), username, locatedFolder))
+            return Mono.from(mailboxAppender.append(mail.getMessage(), username,
+                locateFolder(username, mail),
+                extractFlags(username, mail)))
                 .doOnSuccess(ids -> {
                     metric.increment();
                     LOGGER.info("Local delivered mail {} with messageId {} successfully from {} to {} in folder {} with composedMessageId {}",
@@ -108,7 +117,54 @@ public class SimpleMailStore implements MailStore {
             throw new RuntimeException("Could not retrieve mail message content", e);
         }
     }
-    
+
+    private Optional<Flags> extractFlags(Username username, Mail mail) {
+        Optional<Attribute> seen = mail.getAttribute(AttributeName.of(MailStore.SEEN_PREFIX + username.asString()));
+        Optional<Attribute> important = mail.getAttribute(AttributeName.of(MailStore.IMPORTANT_PREFIX + username.asString()));
+        Optional<Attribute> keywords = mail.getAttribute(AttributeName.of(MailStore.KEYWORDS_PREFIX + username.asString()));
+
+        if (seen.isEmpty() && important.isEmpty() && keywords.isEmpty()) {
+            return Optional.empty();
+        }
+
+        Flags flags = new Flags();
+        flags.add(encodeFlag(seen, Flags.Flag.SEEN));
+        flags.add(encodeFlag(important, Flags.Flag.FLAGGED));
+        extractKeywords(keywords).forEach(flags::add);
+
+        return Optional.of(flags);
+    }
+
+    @SuppressWarnings("unchecked")
+    private Stream<String> extractKeywords(Optional<Attribute> keywords) {
+        return keywords
+            .map(Attribute::getValue)
+            .map(AttributeValue::getValue)
+            .filter(Collection.class::isInstance)
+            .map(Collection.class::cast)
+            .stream()
+            .flatMap(Collection::stream)
+            .filter(AttributeValue.class::isInstance)
+            .map(AttributeValue.class::cast)
+            .map(a -> ((AttributeValue<?>) a).getValue())
+            .filter(String.class::isInstance)
+            .map(String.class::cast);
+    }
+
+    private Flags encodeFlag(Optional<Attribute> attr, Flags.Flag flag) {
+        Flags flags = new Flags();
+        attr.map(Attribute::getValue)
+            .map(AttributeValue::getValue)
+            .filter(Boolean.class::isInstance)
+            .map(Boolean.class::cast)
+            .ifPresent(seenFlag -> {
+                if (seenFlag) {
+                    flags.add(flag);
+                }
+            });
+        return flags;
+    }
+
     private String getMessageId(Mail mail) {
         try {
             return mail.getMessage().getMessageID();
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/WithStorageDirectiveTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/WithStorageDirectiveTest.java
index 5338cb8cc0..6ba7bf81c7 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/WithStorageDirectiveTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/WithStorageDirectiveTest.java
@@ -33,6 +33,7 @@ import org.apache.mailet.base.test.FakeMailetConfig;
 import org.assertj.core.api.SoftAssertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
 
 class WithStorageDirectiveTest {
     private static final DomainList NO_DOMAIN_LIST = null;
@@ -81,6 +82,102 @@ class WithStorageDirectiveTest {
                 new Attribute(recipient2, AttributeValue.of(targetFolderName)));
     }
 
+    @Test
+    void shouldSupportSeen() throws Exception {
+        testee.init(FakeMailetConfig.builder()
+            .setProperty(WithStorageDirective.SEEN, "true")
+            .build());
+
+        FakeMail mail = FakeMail.builder()
+            .name("name")
+            .recipients(MailAddressFixture.RECIPIENT1)
+            .build();
+
+        testee.service(mail);
+
+        AttributeName recipient1 = AttributeName.of("Seen_recipient1@localhost");
+        assertThat(mail.attributes())
+            .containsOnly(
+                new Attribute(recipient1, AttributeValue.of(true)));
+    }
+
+    @Test
+    void shouldSupportImportant() throws Exception {
+        testee.init(FakeMailetConfig.builder()
+            .setProperty(WithStorageDirective.IMPORTANT, "true")
+            .build());
+
+        FakeMail mail = FakeMail.builder()
+            .name("name")
+            .recipients(MailAddressFixture.RECIPIENT1)
+            .build();
+
+        testee.service(mail);
+
+        AttributeName recipient1 = AttributeName.of("Important_recipient1@localhost");
+        assertThat(mail.attributes())
+            .containsOnly(
+                new Attribute(recipient1, AttributeValue.of(true)));
+    }
+
+    @Test
+    void shouldSupportKeywords() throws Exception {
+        testee.init(FakeMailetConfig.builder()
+            .setProperty(WithStorageDirective.KEYWORDS, "abc,def")
+            .build());
+
+        FakeMail mail = FakeMail.builder()
+            .name("name")
+            .recipients(MailAddressFixture.RECIPIENT1)
+            .build();
+
+        testee.service(mail);
+
+        AttributeName recipient1 = AttributeName.of("Keywords_recipient1@localhost");
+        assertThat(mail.attributes())
+            .containsOnly(
+                new Attribute(recipient1, AttributeValue.of(ImmutableSet.of(
+                    AttributeValue.of("abc"),
+                    AttributeValue.of("def")))));
+    }
+
+    @Test
+    void shouldSanitizeKeywords() throws Exception {
+        testee.init(FakeMailetConfig.builder()
+            .setProperty(WithStorageDirective.KEYWORDS, "abc, def,,")
+            .build());
+
+        FakeMail mail = FakeMail.builder()
+            .name("name")
+            .recipients(MailAddressFixture.RECIPIENT1)
+            .build();
+
+        testee.service(mail);
+
+        AttributeName recipient1 = AttributeName.of("Keywords_recipient1@localhost");
+        assertThat(mail.attributes())
+            .containsOnly(
+                new Attribute(recipient1, AttributeValue.of(ImmutableSet.of(
+                    AttributeValue.of("abc"),
+                    AttributeValue.of("def")))));
+    }
+
+    @Test
+    void shouldRejectBadSeenValue() {
+        assertThatThrownBy(() -> testee.init(FakeMailetConfig.builder()
+            .setProperty(WithStorageDirective.SEEN, "bad")
+            .build()))
+            .isInstanceOf(IllegalStateException.class);
+    }
+
+    @Test
+    void shouldRejectBadImportantValue() {
+        assertThatThrownBy(() -> testee.init(FakeMailetConfig.builder()
+            .setProperty(WithStorageDirective.IMPORTANT, "bad")
+            .build()))
+            .isInstanceOf(IllegalStateException.class);
+    }
+
     @Test
     void serviceShouldNotThrowWhenNoRecipients() throws Exception {
         String targetFolderName = "Spam";
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java
index e50ffa49d6..e9a007975e 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/MailboxAppenderImplTest.java
@@ -23,7 +23,9 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.time.Duration;
+import java.util.Optional;
 
+import javax.mail.Flags;
 import javax.mail.MessagingException;
 import javax.mail.internet.MimeMessage;
 
@@ -48,6 +50,7 @@ class MailboxAppenderImplTest {
     public static final Username USER = Username.of("user");
     public static final String FOLDER = "folder";
     public static final String EMPTY_FOLDER = "";
+    private static final Optional<Flags> NO_FLAGS = Optional.empty();
 
     private MailboxAppenderImpl testee;
     private MailboxManager mailboxManager;
@@ -70,7 +73,7 @@ class MailboxAppenderImplTest {
 
     @Test
     void appendShouldAddMessageToDesiredMailbox() throws Exception {
-        Mono.from(testee.append(mimeMessage, USER, FOLDER)).block();
+        Mono.from(testee.append(mimeMessage, USER, FOLDER, NO_FLAGS)).block();
 
         MessageResultIterator messages = mailboxManager.getMailbox(MailboxPath.forUser(USER, FOLDER), session)
             .getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session);
@@ -84,7 +87,7 @@ class MailboxAppenderImplTest {
         MailboxPath mailboxPath = MailboxPath.forUser(USER, FOLDER);
         mailboxManager.createMailbox(mailboxPath, session);
 
-        Mono.from(testee.append(mimeMessage, USER, FOLDER)).block();
+        Mono.from(testee.append(mimeMessage, USER, FOLDER, NO_FLAGS)).block();
 
         MessageResultIterator messages = mailboxManager.getMailbox(mailboxPath, session)
             .getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session);
@@ -95,13 +98,13 @@ class MailboxAppenderImplTest {
 
     @Test
     void appendShouldNotAppendToEmptyFolder() {
-        assertThatThrownBy(() -> Mono.from(testee.append(mimeMessage, USER, EMPTY_FOLDER)).block())
+        assertThatThrownBy(() -> Mono.from(testee.append(mimeMessage, USER, EMPTY_FOLDER, NO_FLAGS)).block())
             .isInstanceOf(MessagingException.class);
     }
 
     @Test
     void appendShouldRemovePathSeparatorAsFirstChar() throws Exception {
-        Mono.from(testee.append(mimeMessage, USER, "." + FOLDER)).block();
+        Mono.from(testee.append(mimeMessage, USER, "." + FOLDER, NO_FLAGS)).block();
 
         MessageResultIterator messages = mailboxManager.getMailbox(MailboxPath.forUser(USER, FOLDER), session)
             .getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session);
@@ -110,9 +113,19 @@ class MailboxAppenderImplTest {
             .hasSize(1);
     }
 
+    @Test
+    void appendShouldSupportFlags() throws Exception {
+        Mono.from(testee.append(mimeMessage, USER, "." + FOLDER, Optional.of(new Flags(Flags.Flag.SEEN)))).block();
+
+        MessageResultIterator messages = mailboxManager.getMailbox(MailboxPath.forUser(USER, FOLDER), session)
+            .getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session);
+
+        assertThat(messages.next().getFlags().contains(Flags.Flag.SEEN)).isTrue();
+    }
+
     @Test
     void appendShouldReplaceSlashBySeparator() throws Exception {
-        Mono.from(testee.append(mimeMessage, USER, FOLDER + "/any")).block();
+        Mono.from(testee.append(mimeMessage, USER, FOLDER + "/any", NO_FLAGS)).block();
 
         MessageResultIterator messages = mailboxManager.getMailbox(MailboxPath.forUser(USER, FOLDER + ".any"), session)
             .getMessages(MessageRange.all(), FetchGroup.FULL_CONTENT, session);
@@ -124,7 +137,7 @@ class MailboxAppenderImplTest {
     @RepeatedTest(20)
     void appendShouldNotFailInConcurrentEnvironment() throws Exception {
         ConcurrentTestRunner.builder()
-            .reactorOperation((a, b) -> Mono.from(testee.append(mimeMessage, USER, FOLDER + "/any")).then())
+            .reactorOperation((a, b) -> Mono.from(testee.append(mimeMessage, USER, FOLDER + "/any", NO_FLAGS)).then())
             .threadCount(100)
             .runSuccessfullyWithin(Duration.ofMinutes(1));
     }
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SimpleMailStoreTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SimpleMailStoreTest.java
index d57e070e59..fabb57a14f 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SimpleMailStoreTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/delivery/SimpleMailStoreTest.java
@@ -50,7 +50,7 @@ class SimpleMailStoreTest {
     @BeforeEach
     void setUp() throws Exception {
         mailboxAppender = mock(MailboxAppenderImpl.class);
-        when(mailboxAppender.append(any(), any(), any())).thenReturn(Mono.empty());
+        when(mailboxAppender.append(any(), any(), any(), any())).thenReturn(Mono.empty());
         usersRepository = mock(UsersRepository.class);
         testee = SimpleMailStore.builder()
             .usersRepository(usersRepository)
@@ -76,7 +76,7 @@ class SimpleMailStoreTest {
             .build();
         testee.storeMail(recipient, mail);
 
-        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.asString())), eq(FOLDER));
+        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.asString())), eq(FOLDER), any());
     }
 
     @Test
@@ -89,7 +89,7 @@ class SimpleMailStoreTest {
             .build();
         testee.storeMail(recipient, mail);
 
-        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.getLocalPart())), eq(FOLDER));
+        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.getLocalPart())), eq(FOLDER), any());
     }
 
     @Test
@@ -102,6 +102,6 @@ class SimpleMailStoreTest {
             .build();
         testee.storeMail(recipient, mail);
 
-        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.toString())), eq(FOLDER));
+        verify(mailboxAppender).append(any(MimeMessage.class), eq(Username.of(recipient.toString())), eq(FOLDER), any());
     }
 }


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


[james-project] 05/05: JAMES-3899 JavaDoc for StorageDirective

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 c773dccb070326ce6dee3a9ae89b5307acb1f727
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Apr 7 14:13:09 2023 +0700

    JAMES-3899 JavaDoc for StorageDirective
---
 .../java/org/apache/mailet/StorageDirective.java    | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/mailet/api/src/main/java/org/apache/mailet/StorageDirective.java b/mailet/api/src/main/java/org/apache/mailet/StorageDirective.java
index f931d041fe..a55f46764e 100644
--- a/mailet/api/src/main/java/org/apache/mailet/StorageDirective.java
+++ b/mailet/api/src/main/java/org/apache/mailet/StorageDirective.java
@@ -30,6 +30,27 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.primitives.Booleans;
 
+/**
+ * StorageDirective allows mailets to set storage instructions applied by mailet container.
+ *
+ * Usage:
+ *
+ * <pre>
+ *     <code>
+ *         StorageDirective.builder()
+ *             .targetFolder("target")
+ *             .seen(true)
+ *             .important(true)
+ *             .keywords(ImmutableList.of("abc", "def")
+ *             .build()
+ *             .encodeAsAttributes(Username.of("bob@localhost")
+ *             .forEach(mail::setAttribute);
+ *      </code></code>
+ * </pre>
+ *
+ * This will result in this mail to be placed for bob@localhost into the folder target,
+ * with user flags abc, def and marked as Flagged and Seen.
+ */
 public class StorageDirective {
     public static class Builder {
         private Optional<String> targetFolder = Optional.empty();


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