You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2020/12/18 07:27:29 UTC

[james-project] 01/13: JAMES-3431 Matchers for working with DSN parameters

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 dfe87cef3ffe7661fe1656fe11af15c3adaae86d
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Dec 16 15:07:25 2020 +0700

    JAMES-3431 Matchers for working with DSN parameters
    
    They allow knowing when DSN notifications should be sent.
    
    This includes:
     - DSNSuccessRequested
     - DSNFailureRequested
     - DSNDelayRequested
---
 .../transport/matchers/DSNDelayRequested.java      |  98 ++++++++
 .../transport/matchers/DSNFailureRequested.java    | 102 ++++++++
 .../transport/matchers/DSNSuccessRequested.java    |  98 ++++++++
 .../transport/matchers/DSNDelayRequestedTest.java  | 261 +++++++++++++++++++++
 .../matchers/DSNFailureRequestedTest.java          | 259 ++++++++++++++++++++
 .../matchers/DSNSuccessRequestedTest.java          | 259 ++++++++++++++++++++
 6 files changed, 1077 insertions(+)

diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/DSNDelayRequested.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/DSNDelayRequested.java
new file mode 100644
index 0000000..bee7072
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/DSNDelayRequested.java
@@ -0,0 +1,98 @@
+/****************************************************************
+ * 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.matchers;
+
+import static org.apache.mailet.DsnParameters.Notify.DELAY;
+
+import java.util.Collection;
+import java.util.Optional;
+
+import javax.mail.MessagingException;
+
+import org.apache.james.core.MailAddress;
+import org.apache.mailet.DsnParameters;
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.GenericMatcher;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+/**
+ * Returns the list of Recipients for which DSN DELAY notifications
+ * should be generated.
+ *
+ * This include only by default recipients explicitly positioning DELAY value as part of the NOTIFY RCPT parameter.
+ *
+ * Example:
+ *
+ * <pre>
+ * &lt;mailet match="DSNDelayRequested" class="XXX"/&gt;
+ * </pre>
+ *
+ * This can be configured to also include recipients not having specified NOTIFY RCPT parameters.
+ *
+ * Example:
+ *
+ * <pre>
+ * &lt;mailet match="DSNDelayRequested=shouldMatchByDefault" class="XXX"/&gt;
+ * </pre>
+ */
+public class DSNDelayRequested extends GenericMatcher {
+    private static final boolean DEFAULT_PRESENT = true;
+    public static final String CONDITION = "shouldMatchByDefault";
+
+    private boolean shouldBeMatchedByDefault;
+
+    @Override
+    public void init() throws MessagingException {
+        Preconditions.checkState(Strings.isNullOrEmpty(getCondition()) || CONDITION.equals(getCondition()),
+            "DSNSuccessRequested condition, when specified, should be '%s'", CONDITION);
+        shouldBeMatchedByDefault = Optional.ofNullable(getCondition())
+            .map(CONDITION::equals)
+            .orElse(!DEFAULT_PRESENT);
+    }
+
+    @Override
+    public Collection<MailAddress> match(Mail mail) {
+        return mail.getRecipients().stream()
+            .filter(recipient -> delayRequested(mail, recipient))
+            .collect(Guavate.toImmutableList());
+    }
+
+    private Boolean delayRequested(Mail mail, MailAddress recipient) {
+        return mail.dsnParameters()
+            .map(dsnParameters -> delayRequested(recipient, dsnParameters))
+            .orElse(shouldBeMatchedByDefault);
+    }
+
+    private boolean delayRequested(MailAddress recipient, DsnParameters dsnParameters) {
+        return Optional.ofNullable(dsnParameters.getRcptParameters().get(recipient))
+            .map(rcptParams -> rcptParams.getNotifyParameter()
+                .map(notifies -> notifies.contains(DELAY))
+                .orElse(shouldBeMatchedByDefault))
+            .orElse(shouldBeMatchedByDefault);
+    }
+
+    @Override
+    public String getMatcherName() {
+        return "DSNDelayRequested";
+    }
+}
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/DSNFailureRequested.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/DSNFailureRequested.java
new file mode 100644
index 0000000..36ed8fb
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/DSNFailureRequested.java
@@ -0,0 +1,102 @@
+/****************************************************************
+ * 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.matchers;
+
+import static org.apache.mailet.DsnParameters.Notify.FAILURE;
+
+import java.util.Collection;
+import java.util.Optional;
+
+import javax.mail.MessagingException;
+
+import org.apache.james.core.MailAddress;
+import org.apache.james.util.FunctionalUtils;
+import org.apache.mailet.DsnParameters;
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.GenericMatcher;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+/**
+ * Returns the list of Recipients for which DSN FAILURE notifications
+ * should be generated.
+ *
+ * This include only by default recipients explicitly positioning FAILURE value as part of the NOTIFY RCPT parameter,
+ * as well as those not having specified NOTIFY RCPT parameters.
+ *
+ * Example:
+ *
+ * <pre>
+ * &lt;mailet match="DSNFailureRequested" class="XXX"/&gt;
+ * </pre>
+ *
+ * This can be configured to not include recipients not having specified NOTIFY RCPT parameters.
+ *
+ * Example:
+ *
+ * <pre>
+ * &lt;mailet match="DSNFailureRequested=shouldNotMatchByDefault" class="XXX"/&gt;
+ * </pre>
+ */
+public class DSNFailureRequested extends GenericMatcher {
+    private static final boolean DEFAULT_PRESENT = true;
+    public static final String CONDITION = "shouldNotMatchByDefault";
+
+    private boolean shouldBeMatchedByDefault;
+
+    @Override
+    public void init() throws MessagingException {
+        Preconditions.checkState(Strings.isNullOrEmpty(getCondition()) || CONDITION.equals(getCondition()),
+            "DSNFailureRequested condition, when specified, should be '%s'", CONDITION);
+        shouldBeMatchedByDefault = Optional.ofNullable(getCondition())
+            .map(CONDITION::equals)
+            .map(FunctionalUtils.negate())
+            .orElse(DEFAULT_PRESENT);
+    }
+
+    @Override
+    public Collection<MailAddress> match(Mail mail) {
+        return mail.getRecipients().stream()
+            .filter(recipient -> failureRequested(mail, recipient))
+            .collect(Guavate.toImmutableList());
+    }
+
+    private Boolean failureRequested(Mail mail, MailAddress recipient) {
+        return mail.dsnParameters()
+            .map(dsnParameters -> failureRequested(recipient, dsnParameters))
+            .orElse(shouldBeMatchedByDefault);
+    }
+
+    private boolean failureRequested(MailAddress recipient, DsnParameters dsnParameters) {
+        return Optional.ofNullable(dsnParameters.getRcptParameters().get(recipient))
+            .map(rcptParams -> rcptParams.getNotifyParameter()
+                .map(notifies -> notifies.contains(FAILURE))
+                .orElse(shouldBeMatchedByDefault))
+            .orElse(shouldBeMatchedByDefault);
+    }
+
+    @Override
+    public String getMatcherName() {
+        return "DSNFailureRequested";
+    }
+
+}
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/DSNSuccessRequested.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/DSNSuccessRequested.java
new file mode 100644
index 0000000..04d1e1e
--- /dev/null
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/matchers/DSNSuccessRequested.java
@@ -0,0 +1,98 @@
+/****************************************************************
+ * 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.matchers;
+
+import static org.apache.mailet.DsnParameters.Notify.SUCCESS;
+
+import java.util.Collection;
+import java.util.Optional;
+
+import javax.mail.MessagingException;
+
+import org.apache.james.core.MailAddress;
+import org.apache.mailet.DsnParameters;
+import org.apache.mailet.Mail;
+import org.apache.mailet.base.GenericMatcher;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+/**
+ * Returns the list of Recipients for which DSN SUCCESS notifications
+ * should be generated.
+ *
+ * This include only by default recipients explicitly positioning SUCCESS value as part of the NOTIFY RCPT parameter.
+ *
+ * Example:
+ *
+ * <pre>
+ * &lt;mailet match="DSNSuccessRequested" class="XXX"/&gt;
+ * </pre>
+ *
+ * This can be configured to also include recipients not having specified NOTIFY RCPT parameters.
+ *
+ * Example:
+ *
+ * <pre>
+ * &lt;mailet match="DSNSuccessRequested=shouldMatchByDefault" class="XXX"/&gt;
+ * </pre>
+ */
+public class DSNSuccessRequested extends GenericMatcher {
+    private static final boolean DEFAULT_PRESENT = true;
+    public static final String CONDITION = "shouldMatchByDefault";
+
+    private boolean shouldBeMatchedByDefault;
+
+    @Override
+    public void init() throws MessagingException {
+        Preconditions.checkState(Strings.isNullOrEmpty(getCondition()) || CONDITION.equals(getCondition()),
+            "DSNSuccessRequested condition, when specified, should be '%s'", CONDITION);
+        shouldBeMatchedByDefault = Optional.ofNullable(getCondition())
+            .map(CONDITION::equals)
+            .orElse(!DEFAULT_PRESENT);
+    }
+
+    @Override
+    public Collection<MailAddress> match(Mail mail) {
+        return mail.getRecipients().stream()
+            .filter(recipient -> successRequested(mail, recipient))
+            .collect(Guavate.toImmutableList());
+    }
+
+    private Boolean successRequested(Mail mail, MailAddress recipient) {
+        return mail.dsnParameters()
+            .map(dsnParameters -> successRequested(recipient, dsnParameters))
+            .orElse(shouldBeMatchedByDefault);
+    }
+
+    private boolean successRequested(MailAddress recipient, DsnParameters dsnParameters) {
+        return Optional.ofNullable(dsnParameters.getRcptParameters().get(recipient))
+            .map(rcptParams -> rcptParams.getNotifyParameter()
+                .map(notifies -> notifies.contains(SUCCESS))
+                .orElse(shouldBeMatchedByDefault))
+            .orElse(shouldBeMatchedByDefault);
+    }
+
+    @Override
+    public String getMatcherName() {
+        return "DSNSuccessRequested";
+    }
+}
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/DSNDelayRequestedTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/DSNDelayRequestedTest.java
new file mode 100644
index 0000000..7a6f714
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/DSNDelayRequestedTest.java
@@ -0,0 +1,261 @@
+/****************************************************************
+ * 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.matchers;
+
+import static org.apache.mailet.DsnParameters.Notify.DELAY;
+import static org.apache.mailet.DsnParameters.Notify.FAILURE;
+import static org.apache.mailet.DsnParameters.Notify.NEVER;
+import static org.apache.mailet.DsnParameters.Notify.SUCCESS;
+import static org.apache.mailet.base.MailAddressFixture.RECIPIENT1;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.EnumSet;
+
+import org.apache.james.server.core.MailImpl;
+import org.apache.mailet.DsnParameters;
+import org.apache.mailet.base.MailAddressFixture;
+import org.apache.mailet.base.test.FakeMatcherConfig;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class DSNDelayRequestedTest {
+    @Nested
+    class Default {
+        DSNDelayRequested testee;
+
+        @BeforeEach
+        void setUp() throws Exception {
+            testee = new DSNDelayRequested();
+            testee.init(FakeMatcherConfig.builder()
+                .matcherName(testee.getMatcherName())
+                .build());
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNoDsnParameters() {
+            assertThat(
+                testee.match(MailImpl.builder()
+                    .name("mail")
+                    .addRecipient(RECIPIENT1)
+                    .build()))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNoDsnRcptParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .envId(DsnParameters.EnvId.of("39"))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNoDsnNotifyParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(RECIPIENT1))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNeverNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(NEVER)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNotDelayNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(SUCCESS, FAILURE)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnCollectionWhenDelayNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(DELAY)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+    }
+
+    @Nested
+    class ShouldMatchByDefault {
+        DSNDelayRequested testee;
+
+        @BeforeEach
+        void setUp() throws Exception {
+            testee = new DSNDelayRequested();
+            testee.init(FakeMatcherConfig.builder()
+                .matcherName(testee.getMatcherName())
+                .condition("shouldMatchByDefault")
+                .build());
+        }
+
+        @Test
+        void shouldReturnCollectionWhenNoDsnParameters() {
+            assertThat(
+                testee.match(MailImpl.builder()
+                    .name("mail")
+                    .addRecipient(RECIPIENT1)
+                    .build()))
+                .containsOnly(RECIPIENT1);
+        }
+
+        @Test
+        void shouldReturnCollectionWhenNoDsnRcptParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .envId(DsnParameters.EnvId.of("39"))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+
+        @Test
+        void shouldReturnCollectionWhenNoDsnNotifyParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(RECIPIENT1))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNeverNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(NEVER)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNotDelayNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(SUCCESS, FAILURE)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnCollectionWhenDelayNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(DELAY)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+    }
+
+    @Nested
+    class Configuration {
+        @Test
+        void shouldThrowOnInvalidValue() {
+            assertThatThrownBy(() -> new DSNDelayRequested()
+                .init(FakeMatcherConfig.builder()
+                    .matcherName("any")
+                    .condition("bad")
+                    .build()))
+                .isInstanceOf(IllegalStateException.class);
+        }
+
+        @Test
+        void shouldAcceptMatchByDefault() {
+            assertThatCode(() -> new DSNDelayRequested()
+                .init(FakeMatcherConfig.builder()
+                    .matcherName("any")
+                    .condition("shouldMatchByDefault")
+                    .build()))
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        void shouldAcceptNoCondition() {
+            assertThatCode(() -> new DSNDelayRequested()
+                .init(FakeMatcherConfig.builder()
+                    .matcherName("any")
+                    .build()))
+                .doesNotThrowAnyException();
+        }
+    }
+}
\ No newline at end of file
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/DSNFailureRequestedTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/DSNFailureRequestedTest.java
new file mode 100644
index 0000000..1bf8249
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/DSNFailureRequestedTest.java
@@ -0,0 +1,259 @@
+/****************************************************************
+ * 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.matchers;
+
+import static org.apache.mailet.DsnParameters.Notify.DELAY;
+import static org.apache.mailet.DsnParameters.Notify.FAILURE;
+import static org.apache.mailet.DsnParameters.Notify.NEVER;
+import static org.apache.mailet.DsnParameters.Notify.SUCCESS;
+import static org.apache.mailet.base.MailAddressFixture.RECIPIENT1;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.util.EnumSet;
+
+import org.apache.james.server.core.MailImpl;
+import org.apache.mailet.DsnParameters;
+import org.apache.mailet.base.test.FakeMatcherConfig;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class DSNFailureRequestedTest {
+    @Nested
+    class Default {
+        DSNFailureRequested testee;
+
+        @BeforeEach
+        void setUp() throws Exception {
+            testee = new DSNFailureRequested();
+            testee.init(FakeMatcherConfig.builder()
+                .matcherName(testee.getMatcherName())
+                .build());
+        }
+
+        @Test
+        void shouldReturnCollectionWhenNoDsnParameters() {
+            assertThat(
+                testee.match(MailImpl.builder()
+                    .name("mail")
+                    .addRecipient(RECIPIENT1)
+                    .build()))
+                .containsOnly(RECIPIENT1);
+        }
+
+        @Test
+        void shouldReturnCollectionWhenNoDsnRcptParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .envId(DsnParameters.EnvId.of("39"))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+
+        @Test
+        void shouldReturnCollectionWhenNoDsnNotifyParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(RECIPIENT1))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNeverNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(NEVER)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNotFailureNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(DELAY, SUCCESS)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnCollectionWhenFailureNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(FAILURE)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+    }
+
+    @Nested
+    class ShouldNotMatchByDefault {
+        DSNFailureRequested testee;
+
+        @BeforeEach
+        void setUp() throws Exception {
+            testee = new DSNFailureRequested();
+            testee.init(FakeMatcherConfig.builder()
+                .matcherName(testee.getMatcherName())
+                .condition("shouldNotMatchByDefault")
+                .build());
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNoDsnParameters() {
+            assertThat(
+                testee.match(MailImpl.builder()
+                    .name("mail")
+                    .addRecipient(RECIPIENT1)
+                    .build()))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNoDsnRcptParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .envId(DsnParameters.EnvId.of("39"))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNoDsnNotifyParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(RECIPIENT1))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNeverNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(NEVER)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNotFailureNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(DELAY, SUCCESS)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnCollectionWhenFailureNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(FAILURE)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+    }
+
+    @Nested
+    class Configuration {
+        @Test
+        void shouldThrowOnInvalidValue() {
+            assertThatThrownBy(() -> new DSNFailureRequested()
+                .init(FakeMatcherConfig.builder()
+                    .matcherName("any")
+                    .condition("bad")
+                    .build()))
+                .isInstanceOf(IllegalStateException.class);
+        }
+
+        @Test
+        void shouldAcceptNotMatchByDefault() {
+            assertThatCode(() -> new DSNFailureRequested()
+                .init(FakeMatcherConfig.builder()
+                    .matcherName("any")
+                    .condition("shouldNotMatchByDefault")
+                    .build()))
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        void shouldAcceptNoCondition() {
+            assertThatCode(() -> new DSNFailureRequested()
+                .init(FakeMatcherConfig.builder()
+                    .matcherName("any")
+                    .build()))
+                .doesNotThrowAnyException();
+        }
+    }
+}
\ No newline at end of file
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/DSNSuccessRequestedTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/DSNSuccessRequestedTest.java
new file mode 100644
index 0000000..4de2d49
--- /dev/null
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/matchers/DSNSuccessRequestedTest.java
@@ -0,0 +1,259 @@
+/****************************************************************
+ * 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.matchers;
+
+import static org.apache.mailet.DsnParameters.Notify.DELAY;
+import static org.apache.mailet.DsnParameters.Notify.FAILURE;
+import static org.apache.mailet.DsnParameters.Notify.NEVER;
+import static org.apache.mailet.DsnParameters.Notify.SUCCESS;
+import static org.apache.mailet.base.MailAddressFixture.RECIPIENT1;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.util.EnumSet;
+
+import org.apache.james.server.core.MailImpl;
+import org.apache.mailet.DsnParameters;
+import org.apache.mailet.base.test.FakeMatcherConfig;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+
+class DSNSuccessRequestedTest {
+    @Nested
+    class Default {
+        DSNSuccessRequested testee;
+
+        @BeforeEach
+        void setUp() throws Exception {
+            testee = new DSNSuccessRequested();
+            testee.init(FakeMatcherConfig.builder()
+                .matcherName(testee.getMatcherName())
+                .build());
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNoDsnParameters() {
+            assertThat(
+                testee.match(MailImpl.builder()
+                    .name("mail")
+                    .addRecipient(RECIPIENT1)
+                    .build()))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNoDsnRcptParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .envId(DsnParameters.EnvId.of("39"))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNoDsnNotifyParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(RECIPIENT1))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNeverNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(NEVER)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNotSuccessNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(DELAY, FAILURE)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnCollectionWhenSuccessNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(SUCCESS)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+    }
+
+    @Nested
+    class ShouldMatchByDefault {
+        DSNSuccessRequested testee;
+
+        @BeforeEach
+        void setUp() throws Exception {
+            testee = new DSNSuccessRequested();
+            testee.init(FakeMatcherConfig.builder()
+                .matcherName(testee.getMatcherName())
+                .condition("shouldMatchByDefault")
+                .build());
+        }
+
+        @Test
+        void shouldReturnCollectionWhenNoDsnParameters() {
+            assertThat(
+                testee.match(MailImpl.builder()
+                    .name("mail")
+                    .addRecipient(RECIPIENT1)
+                    .build()))
+                .containsOnly(RECIPIENT1);
+        }
+
+        @Test
+        void shouldReturnCollectionWhenNoDsnRcptParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .envId(DsnParameters.EnvId.of("39"))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+
+        @Test
+        void shouldReturnCollectionWhenNoDsnNotifyParameters() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(RECIPIENT1))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNeverNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(NEVER)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnEmptyWhenNotSuccessNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(DELAY, FAILURE)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .isEmpty();
+        }
+
+        @Test
+        void shouldReturnCollectionWhenSuccessNotifyParameter() {
+            MailImpl mail = MailImpl.builder()
+                .name("mail")
+                .addRecipient(RECIPIENT1)
+                .build();
+            mail.setDsnParameters(DsnParameters.builder()
+                .addRcptParameter(RECIPIENT1, DsnParameters.RecipientDsnParameters.of(EnumSet.of(SUCCESS)))
+                .build().get());
+
+            assertThat(testee.match(mail))
+                .containsOnly(RECIPIENT1);
+        }
+    }
+
+    @Nested
+    class Configuration {
+        @Test
+        void shouldThrowOnInvalidValue() {
+            assertThatThrownBy(() -> new DSNSuccessRequested()
+                .init(FakeMatcherConfig.builder()
+                    .matcherName("any")
+                    .condition("bad")
+                    .build()))
+                .isInstanceOf(IllegalStateException.class);
+        }
+
+        @Test
+        void shouldAcceptMatchByDefault() {
+            assertThatCode(() -> new DSNSuccessRequested()
+                .init(FakeMatcherConfig.builder()
+                    .matcherName("any")
+                    .condition("shouldMatchByDefault")
+                    .build()))
+                .doesNotThrowAnyException();
+        }
+
+        @Test
+        void shouldAcceptNoCondition() {
+            assertThatCode(() -> new DSNSuccessRequested()
+                .init(FakeMatcherConfig.builder()
+                    .matcherName("any")
+                    .build()))
+                .doesNotThrowAnyException();
+        }
+    }
+}
\ No newline at end of file


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