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 2017/08/25 08:32:40 UTC
[02/13] james-project git commit: JAMES-2110 GetMessageList should
allow filtering by keywords
JAMES-2110 GetMessageList should allow filtering by keywords
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/a6f1c105
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/a6f1c105
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/a6f1c105
Branch: refs/heads/master
Commit: a6f1c1059d20dd179e9f5feb2e3374c38869cfed
Parents: 92365a2
Author: quynhn <qn...@linagora.com>
Authored: Tue Aug 15 16:45:26 2017 +0700
Committer: Raphael Ouazana <ra...@linagora.com>
Committed: Thu Aug 24 15:47:27 2017 +0200
----------------------------------------------------------------------
.../james/jmap/model/FilterCondition.java | 48 ++++++++++--
.../james/jmap/utils/FilterToSearchQuery.java | 27 ++++++-
.../james/jmap/model/FilterConditionTest.java | 77 +++++++++++++++++++-
.../jmap/utils/FilterToSearchQueryTest.java | 52 +++++++++++++
4 files changed, 196 insertions(+), 8 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/a6f1c105/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 63663c5..334667d 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
@@ -29,6 +29,7 @@ import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@JsonDeserialize(builder = FilterCondition.Builder.class)
@@ -60,10 +61,14 @@ public class FilterCondition implements Filter {
private String subject;
private String body;
private Header header;
+ private Optional<String> hasKeyword;
+ private Optional<String> notKeyword;
private Builder() {
inMailboxes = Optional.empty();
notInMailboxes = Optional.empty();
+ hasKeyword = Optional.empty();
+ notKeyword = Optional.empty();
}
public Builder inMailboxes(String... inMailboxes) {
@@ -88,6 +93,18 @@ public class FilterCondition implements Filter {
return this;
}
+ @JsonDeserialize
+ public Builder hasKeyword(Optional<String> hasKeyword) {
+ this.hasKeyword = hasKeyword;
+ return this;
+ }
+
+ @JsonDeserialize
+ public Builder notKeyword(Optional<String> notKeyword) {
+ this.notKeyword = notKeyword;
+ return this;
+ }
+
public Builder before(ZonedDateTime before) {
this.before = before;
return this;
@@ -174,9 +191,12 @@ public class FilterCondition implements Filter {
}
public FilterCondition build() {
+ Preconditions.checkArgument(!hasKeyword.isPresent() || (new Keyword(hasKeyword.get()) != null), "hasKeyword is not valid");
+ Preconditions.checkArgument(!notKeyword.isPresent() || (new Keyword(notKeyword.get()) != null), "notKeyword is not valid");
return new FilterCondition(inMailboxes, notInMailboxes, Optional.ofNullable(before), Optional.ofNullable(after), Optional.ofNullable(minSize), Optional.ofNullable(maxSize),
Optional.ofNullable(isFlagged), Optional.ofNullable(isUnread), Optional.ofNullable(isAnswered), Optional.ofNullable(isDraft), Optional.ofNullable(hasAttachment),
- Optional.ofNullable(text), Optional.ofNullable(from), Optional.ofNullable(to), Optional.ofNullable(cc), Optional.ofNullable(bcc), Optional.ofNullable(subject), Optional.ofNullable(body), Optional.ofNullable(header));
+ Optional.ofNullable(text), Optional.ofNullable(from), Optional.ofNullable(to), Optional.ofNullable(cc), Optional.ofNullable(bcc), Optional.ofNullable(subject),
+ Optional.ofNullable(body), Optional.ofNullable(header), hasKeyword, notKeyword);
}
}
@@ -199,10 +219,13 @@ public class FilterCondition implements Filter {
private final Optional<String> subject;
private final Optional<String> body;
private final Optional<Header> header;
+ private final Optional<String> hasKeyword;
+ private final Optional<String> notKeyword;
@VisibleForTesting FilterCondition(Optional<List<String>> inMailboxes, Optional<List<String>> notInMailboxes, Optional<ZonedDateTime> before, Optional<ZonedDateTime> after, Optional<Integer> minSize, Optional<Integer> maxSize,
- Optional<Boolean> isFlagged, Optional<Boolean> isUnread, Optional<Boolean> isAnswered, Optional<Boolean> isDraft, Optional<Boolean> hasAttachment,
- Optional<String> text, Optional<String> from, Optional<String> to, Optional<String> cc, Optional<String> bcc, Optional<String> subject, Optional<String> body, Optional<Header> header) {
+ Optional<Boolean> isFlagged, Optional<Boolean> isUnread, Optional<Boolean> isAnswered, Optional<Boolean> isDraft, Optional<Boolean> hasAttachment,
+ Optional<String> text, Optional<String> from, Optional<String> to, Optional<String> cc, Optional<String> bcc, Optional<String> subject,
+ Optional<String> body, Optional<Header> header, Optional<String> hasKeyword, Optional<String> notKeyword) {
this.inMailboxes = inMailboxes;
this.notInMailboxes = notInMailboxes;
@@ -223,6 +246,8 @@ public class FilterCondition implements Filter {
this.subject = subject;
this.body = body;
this.header = header;
+ this.hasKeyword = hasKeyword;
+ this.notKeyword = notKeyword;
}
public Optional<List<String>> getInMailboxes() {
@@ -301,6 +326,14 @@ public class FilterCondition implements Filter {
return header;
}
+ public Optional<String> getHasKeyword() {
+ return hasKeyword;
+ }
+
+ public Optional<String> getNotKeyword() {
+ return notKeyword;
+ }
+
@Override
public final boolean equals(Object obj) {
if (obj instanceof FilterCondition) {
@@ -323,14 +356,17 @@ public class FilterCondition implements Filter {
&& Objects.equals(this.bcc, other.bcc)
&& Objects.equals(this.subject, other.subject)
&& Objects.equals(this.body, other.body)
- && Objects.equals(this.header, other.header);
+ && Objects.equals(this.header, other.header)
+ && Objects.equals(this.hasKeyword, other.hasKeyword)
+ && Objects.equals(this.notKeyword, other.notKeyword);
}
return false;
}
@Override
public final int hashCode() {
- return Objects.hash(inMailboxes, notInMailboxes, before, after, minSize, maxSize, isFlagged, isUnread, isAnswered, isDraft, hasAttachment, text, from, to, cc, bcc, subject, body, header);
+ return Objects.hash(inMailboxes, notInMailboxes, before, after, minSize, maxSize, isFlagged, isUnread, isAnswered, isDraft, hasAttachment,
+ text, from, to, cc, bcc, subject, body, header, hasKeyword, notKeyword);
}
@Override
@@ -355,6 +391,8 @@ public class FilterCondition implements Filter {
subject.ifPresent(x -> helper.add("subject", x));
body.ifPresent(x -> helper.add("body", x));
header.ifPresent(x -> helper.add("header", x));
+ hasKeyword.ifPresent(x -> helper.add("hasKeyword", x));
+ notKeyword.ifPresent(x -> helper.add("notKeyword", x));
return helper.toString();
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/a6f1c105/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/FilterToSearchQuery.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/FilterToSearchQuery.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/FilterToSearchQuery.java
index 95799e4..f619357 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/FilterToSearchQuery.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/FilterToSearchQuery.java
@@ -20,12 +20,13 @@
package org.apache.james.jmap.utils;
import java.util.Date;
-
+import java.util.Optional;
import javax.mail.Flags.Flag;
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.jmap.model.Keyword;
import org.apache.james.mailbox.model.SearchQuery;
import org.apache.james.mailbox.model.SearchQuery.AddressType;
import org.apache.james.mailbox.model.SearchQuery.Criterion;
@@ -75,9 +76,33 @@ public class FilterToSearchQuery {
filter.getMaxSize().ifPresent(maxSize -> searchQuery.andCriteria(SearchQuery.sizeLessThan(maxSize)));
filter.getMinSize().ifPresent(minSize -> searchQuery.andCriteria(SearchQuery.sizeGreaterThan(minSize)));
filter.getHasAttachment().ifPresent(hasAttachment -> searchQuery.andCriteria(SearchQuery.hasAttachment(hasAttachment)));
+ filter.getHasKeyword().ifPresent(hasKeyword -> {
+ keywordQuery(hasKeyword, true).ifPresent(hasKeywordCriterion
+ -> searchQuery.andCriteria(hasKeywordCriterion));
+ });
+ filter.getNotKeyword().ifPresent(notKeyword -> {
+ keywordQuery(notKeyword, false).ifPresent(notKeywordCriterion
+ -> searchQuery.andCriteria(notKeywordCriterion));
+ });
+
return searchQuery;
}
+ private Optional<Criterion> keywordQuery(String stringKeyword, boolean isSet) {
+ Keyword keyword = new Keyword(stringKeyword);
+ if (keyword.isExposedImapKeyword()) {
+ return Optional.of(getFlagCriterion(keyword, isSet));
+ }
+
+ return Optional.empty();
+ }
+
+ private Criterion getFlagCriterion(Keyword keyword, boolean isSet) {
+ return keyword.asSystemFlag()
+ .map(flag -> SearchQuery.flagSet(flag, isSet))
+ .orElse(SearchQuery.flagSet(keyword.getFlagName(), isSet));
+ }
+
private Criterion convertOperator(FilterOperator filter) {
switch (filter.getOperator()) {
case AND:
http://git-wip-us.apache.org/repos/asf/james-project/blob/a6f1c105/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 5a518f0..5efaecc 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
@@ -23,15 +23,22 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.time.ZonedDateTime;
import java.util.Optional;
+import java.util.Set;
+import org.junit.Rule;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import nl.jqno.equalsverifier.EqualsVerifier;
+import org.junit.rules.ExpectedException;
public class FilterConditionTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
@Test
public void buildShouldWorkWhenNoInMailboxes() {
FilterCondition filterCondition = FilterCondition.builder().build();
@@ -89,9 +96,13 @@ public class FilterConditionTest {
String subject = "subject";
String body = "body";
Header header = Header.from(ImmutableList.of("name", "value"));
- FilterCondition expectedFilterCondition = new FilterCondition(Optional.of(ImmutableList.of("1")), Optional.of(ImmutableList.of("2")), Optional.of(before), Optional.of(after), Optional.of(minSize), Optional.of(maxSize),
+ Optional<String> hasKeyword = Optional.of("$Draft");
+ Optional<String> notKeyword = Optional.of("$Flagged");
+
+ FilterCondition expectedFilterCondition = new FilterCondition(Optional.of(ImmutableList.of("1")), Optional.of(ImmutableList.of("2")), Optional.of(before), Optional.of(after), Optional.of(minSize), Optional.of(maxSize),
Optional.of(isFlagged), Optional.of(isUnread), Optional.of(isAnswered), Optional.of(isDraft), Optional.of(hasAttachment), Optional.of(text), Optional.of(from),
- Optional.of(to), Optional.of(cc), Optional.of(bcc), Optional.of(subject), Optional.of(body), Optional.of(header));
+ Optional.of(to), Optional.of(cc), Optional.of(bcc), Optional.of(subject), Optional.of(body), Optional.of(header),
+ hasKeyword, notKeyword);
FilterCondition filterCondition = FilterCondition.builder()
.inMailboxes(Optional.of(ImmutableList.of("1")))
@@ -113,6 +124,8 @@ public class FilterConditionTest {
.subject(subject)
.body(body)
.header(header)
+ .hasKeyword(hasKeyword)
+ .notKeyword(notKeyword)
.build();
assertThat(filterCondition).isEqualToComparingFieldByField(expectedFilterCondition);
@@ -122,4 +135,64 @@ public class FilterConditionTest {
public void shouldRespectJavaBeanContract() {
EqualsVerifier.forClass(FilterCondition.class).verify();
}
+
+ @Test
+ public void buildShouldBuildFilterConditionWithHasKeywordWhenGivenHasKeyword() {
+ String hasKeyword = "$Draft";
+
+ FilterCondition filterCondition = FilterCondition.builder()
+ .hasKeyword(Optional.of(hasKeyword))
+ .build();
+
+ assertThat(filterCondition.getHasKeyword().get())
+ .isEqualTo(hasKeyword);
+ }
+
+ @Test
+ public void buildShouldBuildFilterConditionWithoutHasKeywordWhenDoNotGivenHasKeyword() {
+ FilterCondition filterCondition = FilterCondition.builder()
+ .hasKeyword(Optional.empty())
+ .build();
+
+ assertThat(filterCondition.getHasKeyword().isPresent())
+ .isFalse();
+ }
+
+ @Test
+ public void buildShouldThrowWhenGivenInvalidKeywordAsHasKeyword() {
+ expectedException.expect(IllegalArgumentException.class);
+
+ FilterCondition.builder()
+ .hasKeyword(Optional.of("$Draft%"))
+ .build();
+ }
+
+ @Test
+ public void buildShouldBuildFilterConditionWithNotKeywordWhenGivenNotKeyword() {
+ String notKeyword = "$Draft";
+
+ FilterCondition filterCondition = FilterCondition.builder()
+ .notKeyword(Optional.of(notKeyword))
+ .build();
+ assertThat(filterCondition.getNotKeyword().get()).isEqualTo(notKeyword);
+ }
+
+ @Test
+ public void buildShouldBuildFilterConditionWithoutNotKeywordWhenDoNotGivenNotKeyword() {
+ FilterCondition filterCondition = FilterCondition.builder()
+ .notKeyword(Optional.empty())
+ .build();
+
+ assertThat(filterCondition.getNotKeyword().isPresent())
+ .isFalse();
+ }
+
+ @Test
+ public void buildShouldThrowWhenGivenInvalidKeywordAsNotKeyword() {
+ expectedException.expect(IllegalArgumentException.class);
+
+ FilterCondition.builder()
+ .notKeyword(Optional.of("$Draft%"))
+ .build();
+ }
}
http://git-wip-us.apache.org/repos/asf/james-project/blob/a6f1c105/server/protocols/jmap/src/test/java/org/apache/james/jmap/utils/FilterToSearchQueryTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/utils/FilterToSearchQueryTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/utils/FilterToSearchQueryTest.java
index eb05436..aca9757 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/utils/FilterToSearchQueryTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/utils/FilterToSearchQueryTest.java
@@ -24,6 +24,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import java.time.ZonedDateTime;
import java.util.Date;
+import java.util.Optional;
import javax.mail.Flags.Flag;
@@ -31,14 +32,17 @@ 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.jmap.model.Header;
+import org.apache.james.jmap.model.Keyword;
import org.apache.james.mailbox.model.SearchQuery;
import org.apache.james.mailbox.model.SearchQuery.AddressType;
import org.apache.james.mailbox.model.SearchQuery.DateResolution;
import org.junit.Test;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
public class FilterToSearchQueryTest {
+ private final static String FORWARDED = "forwarded";
@Test
public void filterConditionShouldThrowWhenUnknownFilter() {
@@ -476,4 +480,52 @@ public class FilterToSearchQueryTest {
assertThat(searchQuery).isEqualTo(expectedSearchQuery);
}
+
+ @Test
+ public void filterConditionShouldMapWhenHasKeyword() {
+ SearchQuery expectedSearchQuery = new SearchQuery();
+ expectedSearchQuery.andCriteria(SearchQuery.flagIsSet(Flag.FLAGGED));
+
+ SearchQuery searchQuery = new FilterToSearchQuery().convert(FilterCondition.builder()
+ .hasKeyword(Optional.of("$Flagged"))
+ .build());
+
+ assertThat(searchQuery).isEqualTo(expectedSearchQuery);
+ }
+
+ @Test
+ public void filterConditionShouldMapWhenHasKeywordWithUserFlag() {
+ SearchQuery expectedSearchQuery = new SearchQuery();
+ expectedSearchQuery.andCriteria(SearchQuery.flagIsSet(FORWARDED));
+
+ SearchQuery searchQuery = new FilterToSearchQuery().convert(FilterCondition.builder()
+ .hasKeyword(Optional.of(FORWARDED))
+ .build());
+
+ assertThat(searchQuery).isEqualTo(expectedSearchQuery);
+ }
+
+ @Test
+ public void filterConditionShouldMapWhenNotKeyword() {
+ SearchQuery expectedSearchQuery = new SearchQuery();
+ expectedSearchQuery.andCriteria(SearchQuery.flagIsUnSet(Flag.FLAGGED));
+
+ SearchQuery searchQuery = new FilterToSearchQuery().convert(FilterCondition.builder()
+ .notKeyword(Optional.of("$Flagged"))
+ .build());
+
+ assertThat(searchQuery).isEqualTo(expectedSearchQuery);
+ }
+
+ @Test
+ public void filterConditionShouldMapWhenNotKeywordWithUserFlag() {
+ SearchQuery expectedSearchQuery = new SearchQuery();
+ expectedSearchQuery.andCriteria(SearchQuery.flagIsUnSet(FORWARDED));
+
+ SearchQuery searchQuery = new FilterToSearchQuery().convert(FilterCondition.builder()
+ .notKeyword(Optional.of(FORWARDED))
+ .build());
+
+ assertThat(searchQuery).isEqualTo(expectedSearchQuery);
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org