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/05 07:10:16 UTC

[james-project] 09/17: JAMES-343 Mail should carry DsnParameters

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 bc826e7722fcae2f3294ce1e0af3f79ff2b4bcd5
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Dec 3 10:54:23 2020 +0700

    JAMES-343 Mail should carry DsnParameters
    
    Leverage attributes by default in order to do so.
    
    This prevents retro-compatibility breakage of the mailet-api.
---
 .../java/org/apache/mailet/AttributeValue.java     |  20 ++-
 .../main/java/org/apache/mailet/DsnParameters.java | 158 +++++++++++++++++++++
 .../api/src/main/java/org/apache/mailet/Mail.java  |  10 ++
 3 files changed, 185 insertions(+), 3 deletions(-)

diff --git a/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java b/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java
index d935d46..9bb6d26 100644
--- a/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java
+++ b/mailet/api/src/main/java/org/apache/mailet/AttributeValue.java
@@ -27,6 +27,7 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.james.mailbox.model.MessageIdDto;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,6 +36,7 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.github.steveash.guavate.Guavate;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
 
@@ -209,12 +211,24 @@ public class AttributeValue<T> {
     }
 
     public <U> Optional<U> valueAs(Class<U> type) {
-        return tryToCast(type, value);
+        return asAttributeValueOf(type).map(AttributeValue::value);
     }
 
-    private static <U> Optional<U> tryToCast(Class<U> type, Object value) {
+    public <U> Optional<AttributeValue<U>> asAttributeValueOf(Class<U> type) {
         if (type.isInstance(value)) {
-            return Optional.of(type.cast(value));
+            return Optional.of((AttributeValue<U>) this);
+        } else {
+            return Optional.empty();
+        }
+    }
+    public <U> Optional<AttributeValue<Map<String, AttributeValue<U>>>> asMapAttributeValueOf(Class<U> type) {
+        if (Map.class.isInstance(value)) {
+            Map<String, AttributeValue<?>> aMap = (Map<String, AttributeValue<?>>) value;
+            Map<String, AttributeValue<U>> castedMap = aMap.entrySet()
+                .stream()
+                .flatMap(entry -> entry.getValue().asAttributeValueOf(type).stream().map(castedValue -> Pair.of(entry.getKey(), castedValue)))
+                .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue));
+            return Optional.of(new AttributeValue<>(castedMap, new Serializer.MapSerializer()));
         } else {
             return Optional.empty();
         }
diff --git a/mailet/api/src/main/java/org/apache/mailet/DsnParameters.java b/mailet/api/src/main/java/org/apache/mailet/DsnParameters.java
index 03f7479..a18ea88 100644
--- a/mailet/api/src/main/java/org/apache/mailet/DsnParameters.java
+++ b/mailet/api/src/main/java/org/apache/mailet/DsnParameters.java
@@ -21,19 +21,25 @@ package org.apache.mailet;
 
 import java.util.Arrays;
 import java.util.EnumSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 
 import javax.mail.internet.AddressException;
 
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.james.core.MailAddress;
 
+import com.github.fge.lambdas.Throwing;
 import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Joiner;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 
 /**
  * Represents DSN parameters attached to the envelope of an Email transiting over SMTP
@@ -64,6 +70,15 @@ public class DsnParameters {
                     .orElseThrow(() -> new IllegalArgumentException(input + " is not a supported value for RET DSN parameter")));
         }
 
+        public static Ret fromAttributeValue(AttributeValue<String> attributeValue) {
+            return parse(attributeValue.value())
+                .orElseThrow(() -> new IllegalArgumentException(attributeValue.value() + " is not a supported value for RET DSN parameter"));
+        }
+
+        public static AttributeValue<String> toAttributeValue(Ret value) {
+            return AttributeValue.of(value.toString());
+        }
+
         public static Optional<Ret> parse(String string) {
             Preconditions.checkNotNull(string);
 
@@ -84,6 +99,10 @@ public class DsnParameters {
                 .map(EnvId::of);
         }
 
+        public static EnvId fromAttributeValue(AttributeValue<String> attributeValue) {
+            return of(attributeValue.value());
+        }
+
         public static EnvId of(String value) {
             Preconditions.checkNotNull(value);
 
@@ -100,6 +119,10 @@ public class DsnParameters {
             return value;
         }
 
+        public AttributeValue<String> toAttributeValue() {
+            return AttributeValue.of(value);
+        }
+
         @Override
         public final boolean equals(Object o) {
             if (o instanceof EnvId) {
@@ -134,6 +157,14 @@ public class DsnParameters {
         FAILURE,
         DELAY;
 
+        public static EnumSet<Notify> fromAttributeValue(AttributeValue<String> attributeValue) {
+            return parse(attributeValue.value());
+        }
+
+        public static AttributeValue<String> toAttributeValue(EnumSet<Notify> value) {
+            return AttributeValue.of(Joiner.on(',').join(value));
+        }
+
         public static EnumSet<Notify> parse(String input) {
             Preconditions.checkNotNull(input);
 
@@ -171,12 +202,28 @@ public class DsnParameters {
             Optional<MailAddress> orcptParameter = Optional.ofNullable(rcptToArgLine.get(ORCPT_PARAMETER))
                 .map(RecipientDsnParameters::parseOrcpt);
 
+            return of(notifyParameter, orcptParameter);
+        }
+
+        public static Optional<RecipientDsnParameters> of(Optional<EnumSet<Notify>> notifyParameter, Optional<MailAddress> orcptParameter) {
             if (notifyParameter.isEmpty() && orcptParameter.isEmpty()) {
                 return Optional.empty();
             }
             return Optional.of(new RecipientDsnParameters(notifyParameter, orcptParameter));
         }
 
+        public static RecipientDsnParameters of(EnumSet<Notify> notifyParameter, MailAddress orcptParameter) {
+            return new RecipientDsnParameters(Optional.of(notifyParameter), Optional.of(orcptParameter));
+        }
+
+        public static RecipientDsnParameters of(MailAddress orcptParameter) {
+            return new RecipientDsnParameters(Optional.empty(), Optional.of(orcptParameter));
+        }
+
+        public static RecipientDsnParameters of(EnumSet<Notify> notifyParameter) {
+            return new RecipientDsnParameters(Optional.of(notifyParameter), Optional.empty());
+        }
+
         private static MailAddress parseOrcpt(String input) {
             Preconditions.checkArgument(input.startsWith(RFC_822_PREFIX), "ORCPT must start with the rfc822 prefix");
             String addressPart = input.substring(RFC_822_PREFIX.length());
@@ -228,6 +275,65 @@ public class DsnParameters {
         }
     }
 
+    public static class DsnAttributeValues {
+        private static final AttributeName ENVID_ATTRIBUTE_NAME = AttributeName.of("dsn-envid");
+        private static final AttributeName RET_ATTRIBUTE_NAME = AttributeName.of("dsn-ret");
+        private static final AttributeName NOTIFY_ATTRIBUTE_NAME = AttributeName.of("dsn-notify");
+        private static final AttributeName ORCPT_ATTRIBUTE_NAME = AttributeName.of("dsn-orcpt");
+
+        public static DsnAttributeValues extract(Map<AttributeName, Attribute> attributesMap) {
+            Optional<AttributeValue<String>> envId = Optional.ofNullable(attributesMap.get(ENVID_ATTRIBUTE_NAME))
+                .flatMap(attribute -> attribute.getValue().asAttributeValueOf(String.class));
+            Optional<AttributeValue<String>> ret = Optional.ofNullable(attributesMap.get(RET_ATTRIBUTE_NAME))
+                .flatMap(attribute -> attribute.getValue().asAttributeValueOf(String.class));
+            Optional<AttributeValue<Map<String, AttributeValue<String>>>> notify =
+                Optional.ofNullable(attributesMap.get(NOTIFY_ATTRIBUTE_NAME))
+                    .flatMap(attribute -> attribute.getValue().asMapAttributeValueOf(String.class));
+            Optional<AttributeValue<Map<String, AttributeValue<String>>>> orcpt =
+                Optional.ofNullable(attributesMap.get(ORCPT_ATTRIBUTE_NAME))
+                    .flatMap(attribute -> attribute.getValue().asMapAttributeValueOf(String.class));
+
+            return new DsnAttributeValues(notify, orcpt, envId, ret);
+        }
+
+        private final Optional<AttributeValue<Map<String, AttributeValue<String>>>> notifyAttributeValue;
+        private final Optional<AttributeValue<Map<String, AttributeValue<String>>>> orcptAttributeValue;
+        private final Optional<AttributeValue<String>> envIdAttributeValue;
+        private final Optional<AttributeValue<String>> retAttributeValue;
+
+        public DsnAttributeValues(Optional<AttributeValue<Map<String, AttributeValue<String>>>> notifyAttributeValue, Optional<AttributeValue<Map<String, AttributeValue<String>>>> orcptAttributeValue, Optional<AttributeValue<String>> envIdAttributeValue, Optional<AttributeValue<String>> retAttributeValue) {
+            this.notifyAttributeValue = notifyAttributeValue;
+            this.orcptAttributeValue = orcptAttributeValue;
+            this.envIdAttributeValue = envIdAttributeValue;
+            this.retAttributeValue = retAttributeValue;
+        }
+
+        public Optional<AttributeValue<Map<String, AttributeValue<String>>>> getNotifyAttributeValue() {
+            return notifyAttributeValue;
+        }
+
+        public Optional<AttributeValue<Map<String, AttributeValue<String>>>> getOrcptAttributeValue() {
+            return orcptAttributeValue;
+        }
+
+        public Optional<AttributeValue<String>> getEnvIdAttributeValue() {
+            return envIdAttributeValue;
+        }
+
+        public Optional<AttributeValue<String>> getRetAttributeValue() {
+            return retAttributeValue;
+        }
+
+        public List<Attribute> asAttributes() {
+            ImmutableList.Builder<Attribute> result = ImmutableList.builder();
+            envIdAttributeValue.map(value -> new Attribute(ENVID_ATTRIBUTE_NAME, value)).ifPresent(result::add);
+            retAttributeValue.map(value -> new Attribute(RET_ATTRIBUTE_NAME, value)).ifPresent(result::add);
+            notifyAttributeValue.map(value -> new Attribute(NOTIFY_ATTRIBUTE_NAME, value)).ifPresent(result::add);
+            orcptAttributeValue.map(value -> new Attribute(ORCPT_ATTRIBUTE_NAME, value)).ifPresent(result::add);
+            return result.build();
+        }
+    }
+
     public static Optional<DsnParameters> of(Optional<EnvId> envIdParameter, Optional<Ret> retParameter, ImmutableMap<MailAddress, RecipientDsnParameters> rcptParameters) {
         if (envIdParameter.isEmpty() && retParameter.isEmpty() && rcptParameters.isEmpty()) {
             return Optional.empty();
@@ -235,6 +341,39 @@ public class DsnParameters {
         return Optional.of(new DsnParameters(envIdParameter, retParameter, rcptParameters));
     }
 
+    public static Optional<DsnParameters> fromAttributeValue(DsnAttributeValues dsnAttributeValues) {
+        Optional<EnvId> envId = dsnAttributeValues.getEnvIdAttributeValue().map(EnvId::fromAttributeValue);
+        Optional<Ret> ret = dsnAttributeValues.getRetAttributeValue().map(Ret::fromAttributeValue);
+        Map<MailAddress, EnumSet<Notify>> notify = dsnAttributeValues.getNotifyAttributeValue()
+            .map(mapAttributeValue -> mapAttributeValue.value()
+                .entrySet()
+                .stream()
+                .map(Throwing.function(entry -> Pair.of(new MailAddress(entry.getKey()), Notify.fromAttributeValue(entry.getValue()))))
+                .collect(Guavate.entriesToMap()))
+            .orElse(ImmutableMap.of());
+        Map<MailAddress, MailAddress> orcpt = dsnAttributeValues.getOrcptAttributeValue()
+            .map(mapAttributeValue -> mapAttributeValue.value()
+                .entrySet()
+                .stream()
+                .map(Throwing.function(entry -> Pair.of(new MailAddress(entry.getKey()), new MailAddress(entry.getValue().value()))))
+                .collect(Guavate.toImmutableMap(
+                    Pair::getKey,
+                    Pair::getValue)))
+            .orElse(ImmutableMap.of());
+        ImmutableSet<MailAddress> rcpts = ImmutableSet.<MailAddress>builder()
+            .addAll(notify.keySet())
+            .addAll(orcpt.keySet())
+            .build();
+        ImmutableMap<MailAddress, RecipientDsnParameters> recipientDsnParameters = rcpts.stream()
+            .map(rcpt -> Pair.of(rcpt, new RecipientDsnParameters(
+                Optional.ofNullable(notify.get(rcpt)),
+                Optional.ofNullable(orcpt.get(rcpt)))))
+            .collect(Guavate.toImmutableMap(
+                Pair::getKey,
+                Pair::getValue));
+        return of(envId, ret, recipientDsnParameters);
+    }
+
     private final Optional<EnvId> envIdParameter;
     private final Optional<Ret> retParameter;
     private final ImmutableMap<MailAddress, RecipientDsnParameters> rcptParameters;
@@ -257,6 +396,25 @@ public class DsnParameters {
         return rcptParameters;
     }
 
+    public DsnAttributeValues toAttributes() {
+        Optional<AttributeValue<String>> envIdAttributeValue = envIdParameter.map(EnvId::asString).map(AttributeValue::of);
+        Optional<AttributeValue<String>> retAttributeValue = retParameter.map(Ret::toString).map(AttributeValue::of);
+        Optional<AttributeValue<Map<String, AttributeValue<String>>>> notifyAttributeValue = AttributeValue.of(rcptParameters.entrySet().stream()
+            .filter(entry -> entry.getValue().getNotifyParameter().isPresent())
+            .map(entry -> Pair.of(entry.getKey().asString(),
+                Notify.toAttributeValue(entry.getValue().getNotifyParameter().get())))
+            .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue)))
+            .asMapAttributeValueOf(String.class);
+        Optional<AttributeValue<Map<String, AttributeValue<String>>>> orcptAttributeValue = AttributeValue.of(rcptParameters.entrySet().stream()
+            .filter(entry -> entry.getValue().getOrcptParameter().isPresent())
+            .map(entry -> Pair.of(entry.getKey().asString(),
+                AttributeValue.of(entry.getValue().getOrcptParameter().get().asString())))
+            .collect(Guavate.toImmutableMap(Pair::getKey, Pair::getValue)))
+            .asMapAttributeValueOf(String.class);
+
+        return new DsnAttributeValues(notifyAttributeValue, orcptAttributeValue, envIdAttributeValue, retAttributeValue);
+    }
+
     @Override
     public final boolean equals(Object o) {
         if (o instanceof DsnParameters) {
diff --git a/mailet/api/src/main/java/org/apache/mailet/Mail.java b/mailet/api/src/main/java/org/apache/mailet/Mail.java
index 3b37521..fe7ca1a 100644
--- a/mailet/api/src/main/java/org/apache/mailet/Mail.java
+++ b/mailet/api/src/main/java/org/apache/mailet/Mail.java
@@ -438,4 +438,14 @@ public interface Mail extends Serializable, Cloneable {
             .flatMap(Optional::stream)
             .collect(ImmutableMap.toImmutableMap(Pair::getKey, Pair::getValue));
     }
+
+    default Optional<DsnParameters> dsnParameters() {
+        return DsnParameters.fromAttributeValue(DsnParameters.DsnAttributeValues.extract(attributesMap()));
+    }
+
+    default void setDsnParameters(DsnParameters dsnParameters) {
+        dsnParameters.toAttributes()
+            .asAttributes()
+            .forEach(this::setAttribute);
+    }
 }


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