You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2019/08/27 07:41:53 UTC

[james-project] 05/17: JAMES-2866 JSON serialization for Condition

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 5a192d585c9a3ee26bdff2dc4a7501dbc87a77b5
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Aug 21 15:04:20 2019 +0700

    JAMES-2866 JSON serialization for Condition
---
 server/mailet/mock-smtp-server/pom.xml             |   5 +
 .../apache/james/mock/smtp/server/Condition.java   | 111 ++++++++++++++++-----
 .../apache/james/mock/smtp/server/Operator.java    |  60 ++++++++++-
 .../james/mock/smtp/server/ConditionTest.java      |  95 +++++++++++++++---
 4 files changed, 232 insertions(+), 39 deletions(-)

diff --git a/server/mailet/mock-smtp-server/pom.xml b/server/mailet/mock-smtp-server/pom.xml
index 8db2a69..bdcbea7 100644
--- a/server/mailet/mock-smtp-server/pom.xml
+++ b/server/mailet/mock-smtp-server/pom.xml
@@ -50,6 +50,11 @@
             <artifactId>guava</artifactId>
         </dependency>
         <dependency>
+            <groupId>net.javacrumbs.json-unit</groupId>
+            <artifactId>json-unit-assertj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>nl.jqno.equalsverifier</groupId>
             <artifactId>equalsverifier</artifactId>
             <scope>test</scope>
diff --git a/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/Condition.java b/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/Condition.java
index 422eea7..523794b 100644
--- a/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/Condition.java
+++ b/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/Condition.java
@@ -20,43 +20,106 @@
 package org.apache.james.mock.smtp.server;
 
 import java.util.Objects;
+import java.util.Optional;
 
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
 import com.google.common.base.Preconditions;
 
-class Condition {
+@JsonDeserialize(builder = Condition.Builder.class)
+interface Condition {
+    @JsonPOJOBuilder(withPrefix = "")
+    class Builder {
+        private Operator.OperatorName operator;
+        private Optional<String> matchingValue;
 
-    static final Condition MATCH_ALL = new Condition(Operator.MATCH_ALL, "any");
+        public Builder() {
+            this.matchingValue = Optional.empty();
+        }
 
-    private final Operator operator;
-    private final String matchingValue;
+        public Builder operator(Operator.OperatorName operator) {
+            this.operator = operator;
+            return this;
+        }
 
-    Condition(Operator operator, String matchingValue) {
-        Preconditions.checkNotNull(operator);
-        Preconditions.checkNotNull(matchingValue);
+        public Builder matchingValue(String matchingValue) {
+            this.matchingValue = Optional.of(matchingValue);
+            return this;
+        }
 
-        this.operator = operator;
-        this.matchingValue = matchingValue;
-    }
+        public Condition build() {
+            Preconditions.checkState(operator != null, "You need to specify an operator");
 
-    boolean matches(String line) {
-        return operator.actual(line)
-            .expected(matchingValue)
-            .matches();
+            return operator.getConditionFactory().apply(matchingValue);
+        }
     }
 
-    @Override
-    public final boolean equals(Object o) {
-        if (o instanceof Condition) {
-            Condition condition = (Condition) o;
+    class MatchAllCondition implements Condition {
+        public Operator.OperatorName getOperator() {
+            return Operator.OperatorName.MATCH_ALL;
+        }
 
-            return Objects.equals(this.operator, condition.operator)
-                && Objects.equals(this.matchingValue, condition.matchingValue);
+        @Override
+        public boolean matches(String line) {
+            return true;
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            return o instanceof MatchAllCondition;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(MatchAllCondition.class);
         }
-        return false;
     }
 
-    @Override
-    public final int hashCode() {
-        return Objects.hash(operator, matchingValue);
+    class OperatorCondition implements Condition {
+        private final Operator operator;
+        private final String matchingValue;
+
+        OperatorCondition(Operator operator, String matchingValue) {
+            Preconditions.checkNotNull(operator);
+            Preconditions.checkNotNull(matchingValue);
+
+            this.operator = operator;
+            this.matchingValue = matchingValue;
+        }
+
+        public Operator.OperatorName getOperator() {
+            return operator.getOperatorName();
+        }
+
+        public String getMatchingValue() {
+            return matchingValue;
+        }
+
+        @Override
+        public boolean matches(String line) {
+            return operator.actual(line)
+                .expected(matchingValue)
+                .matches();
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof OperatorCondition) {
+                OperatorCondition condition = (OperatorCondition) o;
+
+                return Objects.equals(this.operator, condition.operator)
+                    && Objects.equals(this.matchingValue, condition.matchingValue);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(operator, matchingValue);
+        }
     }
+
+    Condition MATCH_ALL = new MatchAllCondition();
+
+    boolean matches(String line);
 }
diff --git a/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/Operator.java b/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/Operator.java
index 26947c0..d56e04c 100644
--- a/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/Operator.java
+++ b/server/mailet/mock-smtp-server/src/main/java/org/apache/james/mock/smtp/server/Operator.java
@@ -19,7 +19,52 @@
 
 package org.apache.james.mock.smtp.server;
 
+import java.util.Arrays;
+import java.util.Optional;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+
 public interface Operator {
+    enum OperatorName {
+        CONTAINS("contains", maybeMatchingValue -> {
+            Preconditions.checkState(maybeMatchingValue.isPresent(), "You need to specify a matchingValue with the contains operator");
+
+            return new Condition.OperatorCondition(Operator.CONTAINS, maybeMatchingValue.get());
+        }),
+        MATCH_ALL("matchAll", maybeMatchingValue -> {
+            Preconditions.checkState(!maybeMatchingValue.isPresent(), "You should not specify a matchingValue with the matchAll operator");
+
+            return new Condition.MatchAllCondition();
+        });
+
+        private final String name;
+        private final Function<Optional<String>, Condition> conditionFactory;
+
+        @JsonCreator
+        public static OperatorName from(String name) {
+            return Arrays.stream(values())
+                .filter(value -> value.name.equals(name))
+                .findFirst()
+                .orElseThrow(() -> new IllegalArgumentException("Unsuported " + name + " operator"));
+        }
+
+        OperatorName(String name, Function<Optional<String>, Condition> conditionFactory) {
+            this.name = name;
+            this.conditionFactory = conditionFactory;
+        }
+
+        @JsonValue
+        public String getName() {
+            return name;
+        }
+
+        public Function<Optional<String>, Condition> getConditionFactory() {
+            return conditionFactory;
+        }
+    }
 
     @FunctionalInterface
     interface Expected {
@@ -31,8 +76,19 @@ public interface Operator {
         boolean matches();
     }
 
-    Operator CONTAINS = actual -> expected -> () -> actual.contains(expected);
-    Operator MATCH_ALL = actual -> expected -> () -> true;
+    Operator CONTAINS = new Operator() {
+        @Override
+        public Expected actual(String actual) {
+            return expected -> () -> actual.contains(expected);
+        }
+
+        @Override
+        public OperatorName getOperatorName() {
+            return OperatorName.CONTAINS;
+        }
+    };
 
     Expected actual(String actual);
+
+    OperatorName getOperatorName();
 }
diff --git a/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/ConditionTest.java b/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/ConditionTest.java
index e5fe70a..eefbe1c 100644
--- a/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/ConditionTest.java
+++ b/server/mailet/mock-smtp-server/src/test/java/org/apache/james/mock/smtp/server/ConditionTest.java
@@ -19,35 +19,54 @@
 
 package org.apache.james.mock.smtp.server;
 
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
+import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
+
 import nl.jqno.equalsverifier.EqualsVerifier;
 
 class ConditionTest {
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    @Test
+    void operatorConditionShouldMatchBeanContract() {
+        EqualsVerifier.forClass(Condition.OperatorCondition.class)
+            .verify();
+    }
+
     @Test
-    void shouldMatchBeanContract() {
-        EqualsVerifier.forClass(Condition.class)
+    void matchAllShouldMatchBeanContract() {
+        EqualsVerifier.forClass(Condition.MatchAllCondition.class)
             .verify();
     }
 
     @Test
+    void differentConditionTypesShouldNotBeEqual() {
+        assertThat(Condition.MATCH_ALL)
+            .isNotEqualTo(new Condition.OperatorCondition(Operator.CONTAINS, "any"));
+    }
+
+    @Test
     void constructorShouldThrowWhenNullOperator() {
-        assertThatThrownBy(() -> new Condition(null, "matchingValue"))
+        assertThatThrownBy(() -> new Condition.OperatorCondition(null, "matchingValue"))
             .isInstanceOf(NullPointerException.class);
     }
 
     @Test
     void constructorShouldThrowWhenNullMatchingValue() {
-        assertThatThrownBy(() -> new Condition(Operator.CONTAINS, null))
+        assertThatThrownBy(() -> new Condition.OperatorCondition(Operator.CONTAINS, null))
             .isInstanceOf(NullPointerException.class);
     }
 
     @Test
     void matchesShouldReturnTrueWhenOperatorMatches() {
-        Condition condition = new Condition(Operator.CONTAINS, "match me");
+        Condition condition = new Condition.OperatorCondition(Operator.CONTAINS, "match me");
 
         assertThat(condition.matches("this contains match me string"))
             .isTrue();
@@ -55,7 +74,7 @@ class ConditionTest {
 
     @Test
     void matchesShouldReturnFalseWhenOperatorDoesNotMatch() {
-        Condition condition = new Condition(Operator.CONTAINS, "match me");
+        Condition condition = new Condition.OperatorCondition(Operator.CONTAINS, "match me");
 
         assertThat(condition.matches("this contains another string"))
             .isFalse();
@@ -63,7 +82,7 @@ class ConditionTest {
 
     @Test
     void matchesShouldThrowWhenNullLine() {
-        Condition condition = new Condition(Operator.CONTAINS, "match me");
+        Condition condition = new Condition.OperatorCondition(Operator.CONTAINS, "match me");
 
         assertThatThrownBy(() -> condition.matches(null))
             .isInstanceOf(NullPointerException.class);
@@ -71,22 +90,72 @@ class ConditionTest {
 
     @Test
     void matchAllShouldReturnTrue() {
-        assertThat(Condition.MATCH_ALL
-                .matches("this contains another string"))
+        assertThat(Condition.MATCH_ALL.matches("this contains another string"))
             .isTrue();
     }
 
     @Test
     void matchAllShouldReturnTrueEvenWhenLineIsNull() {
-        assertThat(Condition.MATCH_ALL
-                .matches(null))
+        assertThat(Condition.MATCH_ALL.matches(null))
             .isTrue();
     }
 
     @Test
     void matchAllShouldReturnTrueEvenWhenLineIsEmpty() {
-        assertThat(Condition.MATCH_ALL
-                .matches(""))
+        assertThat(Condition.MATCH_ALL.matches(""))
             .isTrue();
     }
+
+    @Nested
+    class JSONTest {
+        @Test
+        void jacksonShouldDeserializeCondition() throws Exception {
+            Condition condition = OBJECT_MAPPER.readValue(
+                "{\"operator\":\"contains\", \"matchingValue\":\"matchme\"}",
+                Condition.class);
+
+            assertThat(condition).isEqualTo(new Condition.OperatorCondition(Operator.CONTAINS, "matchme"));
+        }
+
+        @Test
+        void jacksonShouldDeserializeMatchAllCondition() throws Exception {
+            Condition condition = OBJECT_MAPPER.readValue(
+                "{\"operator\":\"matchAll\"}",
+                Condition.class);
+
+            assertThat(condition).isEqualTo(Condition.MATCH_ALL);
+        }
+
+        @Test
+        void jacksonShouldSerializeCondition() throws Exception {
+            String json = OBJECT_MAPPER.writeValueAsString(new Condition.OperatorCondition(Operator.CONTAINS, "matchme"));
+
+            assertThatJson(json).isEqualTo("{\"operator\":\"contains\", \"matchingValue\":\"matchme\"}");
+        }
+
+        @Test
+        void jacksonShouldSerializeMatchAllCondition() throws Exception {
+            String json = OBJECT_MAPPER.writeValueAsString(Condition.MATCH_ALL);
+
+            assertThatJson(json).isEqualTo("{\"operator\":\"matchAll\"}");
+        }
+
+        @Test
+        void jacksonShouldThrowWhenDeserializeMatchAllConditionWithMatchingValue(){
+            String json = "{\"operator\":\"matchAll\", \"matchingValue\":\"matchme\"}";
+
+            assertThatThrownBy(() -> OBJECT_MAPPER.readValue(json, Condition.class))
+                .isInstanceOf(InvalidDefinitionException.class)
+                .hasMessageContaining("You should not specify a matchingValue with the matchAll operator");
+        }
+
+        @Test
+        void jacksonShouldThrowWhenDeserializeContainsConditionWithoutMatchingValue(){
+            String json = "{\"operator\":\"contains\"}";
+
+            assertThatThrownBy(() -> OBJECT_MAPPER.readValue(json, Condition.class))
+                .isInstanceOf(InvalidDefinitionException.class)
+                .hasMessageContaining("You need to specify a matchingValue with the contains operator");
+        }
+    }
 }


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