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