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 ro...@apache.org on 2016/07/19 08:04:25 UTC

[3/4] james-project git commit: JAMES-1800 Handle filters with operators

JAMES-1800 Handle filters with operators


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/cabe4358
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/cabe4358
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/cabe4358

Branch: refs/heads/master
Commit: cabe4358397c778d6edf992581edc3efad633b03
Parents: 83da2c0
Author: Raphael Ouazana <ra...@linagora.com>
Authored: Wed Jul 13 14:56:45 2016 +0200
Committer: Matthieu Baechler <ma...@linagora.com>
Committed: Tue Jul 19 09:58:58 2016 +0200

----------------------------------------------------------------------
 .../integration/GetMessageListMethodTest.java   |  4 +-
 .../james/jmap/json/FilterDeserializer.java     | 62 +++++++++++++
 .../org/apache/james/jmap/model/Filter.java     | 15 +---
 .../james/jmap/model/FilterCondition.java       | 12 +++
 .../apache/james/jmap/model/FilterOperator.java | 16 ++++
 .../james/jmap/model/FilterConditionTest.java   | 22 ++++-
 .../james/jmap/model/FilterOperatorTest.java    | 40 +++++++++
 .../org/apache/james/jmap/model/FilterTest.java | 92 ++++++++++++++++++++
 8 files changed, 246 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/cabe4358/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessageListMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessageListMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessageListMethodTest.java
index 3ade45f..161010f 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessageListMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/GetMessageListMethodTest.java
@@ -98,9 +98,7 @@ public abstract class GetMessageListMethodTest {
         .then()
             .statusCode(200)
             .body(NAME, equalTo("error"))
-            .body(ARGUMENTS + ".type", equalTo("invalidArguments"))
-            .body(ARGUMENTS + ".description", equalTo("Can not instantiate value of type [simple type, class org.apache.james.jmap.model.FilterCondition$Builder] from Boolean value (true); no single-boolean/Boolean-arg constructor/factory method\n" + 
-                    " at [Source: {\"filter\":true}; line: 1, column: 2] (through reference chain: org.apache.james.jmap.model.Builder[\"filter\"])"));
+            .body(ARGUMENTS + ".type", equalTo("invalidArguments"));
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/james-project/blob/cabe4358/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/FilterDeserializer.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/FilterDeserializer.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/FilterDeserializer.java
new file mode 100644
index 0000000..e4d0ca6
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/json/FilterDeserializer.java
@@ -0,0 +1,62 @@
+/****************************************************************
+ * 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.jmap.json;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.james.jmap.model.Filter;
+import org.apache.james.jmap.model.FilterCondition;
+import org.apache.james.jmap.model.FilterOperator;
+import org.apache.james.util.streams.Iterators;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+public class FilterDeserializer extends StdDeserializer<Filter> {
+
+    public FilterDeserializer() {
+        super(Filter.class);
+    }
+
+    @Override
+    public Filter deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+        ObjectMapper mapper = (ObjectMapper) p.getCodec();
+        ObjectNode obj = (ObjectNode) mapper.readTree(p);
+
+        return mapper.treeToValue(obj, detectClass(obj.fields()));
+    }
+
+    private Class<? extends Filter> detectClass(Iterator<Map.Entry<String, JsonNode>> elements) {
+        Optional<Class<? extends Filter>> maybeFilterOperator = Iterators.toStream(elements)
+                .map(Map.Entry::getKey)
+                .filter(name -> name.equals("operator"))
+                .findFirst()
+                .map(x -> FilterOperator.class);
+
+        return maybeFilterOperator.orElse(FilterCondition.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/cabe4358/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Filter.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Filter.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Filter.java
index d262561..7509fba 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Filter.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Filter.java
@@ -19,18 +19,11 @@
 
 package org.apache.james.jmap.model;
 
-import com.fasterxml.jackson.annotation.JsonSubTypes;
-import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import org.apache.james.jmap.json.FilterDeserializer;
 
-@JsonTypeInfo(
-        use = JsonTypeInfo.Id.NAME,
-        include = JsonTypeInfo.As.PROPERTY,
-        property = "filter",
-        defaultImpl = FilterCondition.class
-)
-@JsonSubTypes({
-    @JsonSubTypes.Type(value = FilterOperator.class, name = "operator")
-})
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+@JsonDeserialize(using = FilterDeserializer.class)
 public interface Filter {
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/cabe4358/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/FilterCondition.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/FilterCondition.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/FilterCondition.java
index 76efb8a..7185cb0 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/FilterCondition.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/FilterCondition.java
@@ -69,11 +69,23 @@ public class FilterCondition implements Filter {
             header = Optional.empty();
         }
 
+        public Builder inMailboxes(String... inMailboxes) {
+            this.inMailboxes = Optional.of(ImmutableList.copyOf(inMailboxes));
+            return this;
+        }
+
+        @JsonDeserialize
         public Builder inMailboxes(Optional<List<String>> inMailboxes) {
             this.inMailboxes = inMailboxes.map(ImmutableList::copyOf);
             return this;
         }
 
+        public Builder notInMailboxes(String... notInMailboxes) {
+            this.notInMailboxes = Optional.of(ImmutableList.copyOf(notInMailboxes));
+            return this;
+        }
+
+        @JsonDeserialize
         public Builder notInMailboxes(Optional<List<String>> notInMailboxes) {
             this.notInMailboxes = notInMailboxes.map(ImmutableList::copyOf);
             return this;

http://git-wip-us.apache.org/repos/asf/james-project/blob/cabe4358/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/FilterOperator.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/FilterOperator.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/FilterOperator.java
index 3a31228..f8ba52c 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/FilterOperator.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/FilterOperator.java
@@ -65,6 +65,22 @@ public class FilterOperator implements Filter {
         }
     }
 
+    public static FilterOperator or(Filter... filters) {
+        Preconditions.checkArgument(filters.length > 0);
+        return builder().operator(Operator.OR).conditions(ImmutableList.copyOf(filters)).build();
+    }
+
+    public static FilterOperator and(Filter... filters) {
+        Preconditions.checkArgument(filters.length > 0);
+        return builder().operator(Operator.AND).conditions(ImmutableList.copyOf(filters)).build();
+    }
+
+
+    public static FilterOperator not(Filter... filters) {
+        Preconditions.checkArgument(filters.length > 0);
+        return builder().operator(Operator.NOT).conditions(ImmutableList.copyOf(filters)).build();
+    }
+    
     private final Operator operator;
     private final List<Filter> conditions;
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/cabe4358/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterConditionTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterConditionTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterConditionTest.java
index b60a935..a4707e5 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterConditionTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterConditionTest.java
@@ -43,7 +43,15 @@ public class FilterConditionTest {
         FilterCondition filterCondition = FilterCondition.builder()
                 .inMailboxes(Optional.of(ImmutableList.of("1", "2")))
                 .build();
-        assertThat(filterCondition.getInMailboxes()).isEqualTo(Optional.of(ImmutableList.of("1", "2")));
+        assertThat(filterCondition.getInMailboxes()).contains(ImmutableList.of("1", "2"));
+    }
+
+    @Test
+    public void buildShouldWorkWhenGivenInMailboxesAsEllipsis() {
+        FilterCondition filterCondition = FilterCondition.builder()
+                .inMailboxes("1", "2")
+                .build();
+        assertThat(filterCondition.getInMailboxes()).contains(ImmutableList.of("1", "2"));
     }
 
     @Test
@@ -51,9 +59,17 @@ public class FilterConditionTest {
         FilterCondition filterCondition = FilterCondition.builder()
                 .notInMailboxes(Optional.of(ImmutableList.of("1", "2")))
                 .build();
-        assertThat(filterCondition.getNotInMailboxes()).isEqualTo(Optional.of(ImmutableList.of("1", "2")));
+        assertThat(filterCondition.getNotInMailboxes()).contains(ImmutableList.of("1", "2"));
     }
 
+    @Test
+    public void builderShouldBuildWhenGivenNotInMailboxesAsEllipsis() {
+        FilterCondition filterCondition = FilterCondition.builder()
+                .notInMailboxes("1", "2")
+                .build();
+        assertThat(filterCondition.getNotInMailboxes()).contains(ImmutableList.of("1", "2"));
+    }
+    
     @Test(expected=NotImplementedException.class)
     public void builderShouldThrowWhenBefore() {
         FilterCondition.builder().before(null);
@@ -147,7 +163,7 @@ public class FilterConditionTest {
 
         FilterCondition filterCondition = FilterCondition.builder()
                 .inMailboxes(Optional.of(ImmutableList.of("1")))
-                .notInMailboxes(Optional.of(ImmutableList.of("2")))
+                .notInMailboxes("2")
                 .build();
 
         assertThat(filterCondition).isEqualToComparingFieldByField(expectedFilterCondition);

http://git-wip-us.apache.org/repos/asf/james-project/blob/cabe4358/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterOperatorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterOperatorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterOperatorTest.java
index 920d2c6..b2b1638 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterOperatorTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterOperatorTest.java
@@ -20,6 +20,7 @@
 package org.apache.james.jmap.model;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import org.junit.Test;
 
@@ -58,6 +59,45 @@ public class FilterOperatorTest {
     }
 
     @Test
+    public void andFactoryMethodShouldThrowWhenNoArgument() {
+        assertThatThrownBy(() -> FilterOperator.and()).isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void andFactoryMethodShouldReturnRightOperator() {
+        FilterCondition condition = FilterCondition.builder().inMailboxes("12").build();
+        ImmutableList<Filter> conditions = ImmutableList.of(condition);
+        FilterOperator expectedFilterOperator = new FilterOperator(Operator.AND, conditions);
+        assertThat(FilterOperator.and(condition)).isEqualTo(expectedFilterOperator);
+    }
+
+    @Test
+    public void orFactoryMethodShouldThrowWhenNoArgument() {
+        assertThatThrownBy(() -> FilterOperator.or()).isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void orFactoryMethodShouldReturnRightOperator() {
+        FilterCondition condition = FilterCondition.builder().inMailboxes("12").build();
+        ImmutableList<Filter> conditions = ImmutableList.of(condition);
+        FilterOperator expectedFilterOperator = new FilterOperator(Operator.OR, conditions);
+        assertThat(FilterOperator.or(condition)).isEqualTo(expectedFilterOperator);
+    }
+
+    @Test
+    public void notFactoryMethodShouldThrowWhenNoArgument() {
+        assertThatThrownBy(() -> FilterOperator.not()).isInstanceOf(IllegalArgumentException.class);
+    }
+
+    @Test
+    public void notFactoryMethodShouldReturnRightOperator() {
+        FilterCondition condition = FilterCondition.builder().inMailboxes("12").build();
+        ImmutableList<Filter> conditions = ImmutableList.of(condition);
+        FilterOperator expectedFilterOperator = new FilterOperator(Operator.NOT, conditions);
+        assertThat(FilterOperator.not(condition)).isEqualTo(expectedFilterOperator);
+    }
+
+    @Test
     public void shouldRespectJavaBeanContract() {
         EqualsVerifier.forClass(FilterOperator.class).verify();
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/cabe4358/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterTest.java
new file mode 100644
index 0000000..7588bad
--- /dev/null
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/FilterTest.java
@@ -0,0 +1,92 @@
+/****************************************************************
+ * 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.jmap.model;
+
+import org.apache.james.jmap.json.ObjectMapperFactory;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class FilterTest {
+
+    private ObjectMapper parser;
+
+    @Before
+    public void setup() {
+        parser = new ObjectMapperFactory().forParsing();
+    }
+
+    @Test
+    public void emptyFilterConditionShouldBeDeserialized() throws Exception {
+        String json = "{}";
+        Filter expected = FilterCondition.builder()
+                .build();
+        Filter actual = parser.readValue(json, Filter.class);
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void singleFilterConditionShouldBeDeserialized() throws Exception {
+        String json = "{\"inMailboxes\": [\"12\",\"34\"]}";
+        Filter expected = FilterCondition.builder().inMailboxes("12","34").build();
+        Filter actual = parser.readValue(json, Filter.class);
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void doubleFilterConditionShouldBeDeserialized() throws Exception {
+        String json = "{\"inMailboxes\": [\"12\",\"34\"], \"notInMailboxes\": [\"45\",\"67\"]}";
+        Filter expected = FilterCondition.builder()
+                .inMailboxes("12","34")
+                .notInMailboxes("45","67")
+                .build();
+        Filter actual = parser.readValue(json, Filter.class);
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void operatorWithSingleConditionShouldBeDeserialized() throws Exception {
+        String json = "{\"operator\": \"AND\", \"conditions\": [{\"inMailboxes\": [\"12\",\"34\"]}]}";
+        Filter expected = FilterOperator.and(FilterCondition.builder().inMailboxes("12","34").build());
+        Filter actual = parser.readValue(json, Filter.class);
+        assertThat(actual).isEqualTo(expected);
+    }
+
+    @Test
+    public void complexFilterShouldBeDeserialized() throws Exception {
+        String json = "{\"operator\": \"AND\", \"conditions\": ["
+                + "         {\"inMailboxes\": [\"12\",\"34\"]},"
+                + "         {\"operator\": \"OR\", \"conditions\": ["
+                + "                 {\"operator\": \"NOT\", \"conditions\": ["
+                + "                         {\"notInMailboxes\": [\"45\"]}]},"
+                + "                 {}]}]}";
+        Filter expected = 
+                FilterOperator.and(
+                        FilterCondition.builder().inMailboxes("12","34").build(),
+                        FilterOperator.or(
+                                FilterOperator.not(
+                                        FilterCondition.builder().notInMailboxes("45").build()),
+                                FilterCondition.builder().build()));
+        Filter actual = parser.readValue(json, Filter.class);
+        assertThat(actual).isEqualTo(expected);
+    }
+}


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