Repository: james-project Updated Branches: refs/heads/master 5accc88a1 -> 10d76ab6e
JAMES-2110 Define keyword on JMAP Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/20beee7a Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/20beee7a Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/20beee7a Branch: refs/heads/master Commit: 20beee7a17946b42d3464269af383338d76a463f Parents: 5accc88 Author: quynhn <[email protected]> Authored: Tue Aug 15 16:31:39 2017 +0700 Committer: Raphael Ouazana <[email protected]> Committed: Thu Aug 24 15:47:26 2017 +0200 ---------------------------------------------------------------------- .../org/apache/james/jmap/model/Keyword.java | 126 +++++++ .../org/apache/james/jmap/model/Keywords.java | 202 ++++++++++++ .../org/apache/james/jmap/model/OldKeyword.java | 92 ++++++ .../apache/james/jmap/model/KeywordTest.java | 189 +++++++++++ .../apache/james/jmap/model/KeywordsTest.java | 328 +++++++++++++++++++ .../apache/james/jmap/model/OldKeywordTest.java | 31 ++ 6 files changed, 968 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keyword.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keyword.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keyword.java new file mode 100644 index 0000000..ff1a2f0 --- /dev/null +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keyword.java @@ -0,0 +1,126 @@ +/**************************************************************** + * 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 java.util.Optional; +import javax.mail.Flags; + +import org.apache.commons.lang.StringUtils; + +import com.google.common.base.CharMatcher; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableBiMap; +import com.google.common.collect.ImmutableList; + +public class Keyword { + private final static int FLAG_NAME_MIN_LENGTH = 1; + private final static int FLAG_NAME_MAX_LENGTH = 255; + private final static CharMatcher FLAG_NAME_PATTERN = CharMatcher.JAVA_LETTER_OR_DIGIT.or(CharMatcher.is('$')); + + public final static Keyword DRAFT = new Keyword("$Draft"); + public final static Keyword SEEN = new Keyword("$Seen"); + public final static Keyword FLAGGED = new Keyword("$Flagged"); + public final static Keyword ANSWERED = new Keyword("$Answered"); + public final static Keyword DELETED = new Keyword("$Deleted"); + public final static Keyword RECENT = new Keyword("$Recent"); + public final static Boolean FLAG_VALUE = true; + + private final static ImmutableList<Keyword> NON_EXPOSED_IMAP_KEYWORDS = ImmutableList.of(Keyword.RECENT, Keyword.DELETED); + private final static ImmutableBiMap<Flags.Flag, Keyword> IMAP_SYSTEM_FLAGS = ImmutableBiMap.<Flags.Flag, Keyword>builder() + .put(Flags.Flag.DRAFT, DRAFT) + .put(Flags.Flag.SEEN, SEEN) + .put(Flags.Flag.FLAGGED, FLAGGED) + .put(Flags.Flag.ANSWERED, ANSWERED) + .put(Flags.Flag.RECENT, RECENT) + .put(Flags.Flag.DELETED, DELETED) + .build(); + + private final String flagName; + + public static Keyword fromFlag(Flags.Flag flag) { + return IMAP_SYSTEM_FLAGS.get(flag); + } + + public Keyword(String flagName) { + Preconditions.checkArgument(isValid(flagName), + "Flagname must not be null or empty, must have length form 1-255, must not contain charater with hex from '\u0000' to '\u00019' or {'(' ')' '{' ']' '%' '*' '\"' '\\'} "); + this.flagName = flagName; + } + + private boolean isValid(String flagName) { + if (StringUtils.isBlank(flagName)) { + return false; + } + if (flagName.length() < FLAG_NAME_MIN_LENGTH || flagName.length() > FLAG_NAME_MAX_LENGTH) { + return false; + } + if (!FLAG_NAME_PATTERN.matchesAllOf(flagName)) { + return false; + } + return true; + } + + public String getFlagName() { + return flagName; + } + + public boolean isExposedImapKeyword() { + return !NON_EXPOSED_IMAP_KEYWORDS.contains(this); + } + + public boolean isDraft() { + return DRAFT.equals(this); + } + + public Optional<Flags.Flag> asSystemFlag() { + return Optional.ofNullable(IMAP_SYSTEM_FLAGS.inverse() + .get(this)); + } + + public Flags asFlags() { + return asSystemFlag() + .map(Flags::new) + .orElse(new Flags(flagName)); + } + + @Override + public final boolean equals(Object other) { + if (other instanceof Keyword) { + Keyword otherKeyword = (Keyword) other; + return Objects.equal(flagName, otherKeyword.flagName); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hashCode(flagName); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("flagName", flagName) + .toString(); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java new file mode 100644 index 0000000..91aff31 --- /dev/null +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java @@ -0,0 +1,202 @@ +/**************************************************************** + * 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 java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; +import javax.mail.Flags; + +import org.apache.james.mailbox.FlagsBuilder; + +import com.github.steveash.guavate.Guavate; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +public class Keywords { + public static final Keywords DEFAULT_VALUE = factory().fromSet(ImmutableSet.of()); + + public interface KeywordsValidator { + void validate(Set<Keyword> keywords); + } + + private FlagsBuilder combiner(FlagsBuilder firstBuilder, FlagsBuilder secondBuilder) { + return firstBuilder.add(secondBuilder.build()); + } + + private FlagsBuilder accumulator(FlagsBuilder accumulator, Keyword keyword) { + return accumulator.add(keyword.asFlags()); + } + + public static class KeywordsFactory { + private Optional<KeywordsValidator> validator; + private Optional<Predicate<Keyword>> filter; + + private KeywordsFactory() { + validator = Optional.empty(); + filter = Optional.empty(); + } + + public KeywordsFactory throwOnImapNonExposedKeywords() { + validator = Optional.of(keywords -> Preconditions.checkArgument( + keywords.stream().allMatch(Keyword::isExposedImapKeyword), "Does not allow to update 'Deleted' or 'Recent' flag")); + return this; + } + + public KeywordsFactory filterImapNonExposedKeywords() { + filter = Optional.of(keyword -> keyword.isExposedImapKeyword()); + return this; + } + + public Keywords fromSet(Set<Keyword> setKeywords) { + validator.orElse(keywords -> {}) + .validate(setKeywords); + + return new Keywords(setKeywords.stream() + .filter(filter.orElse(keyword -> true)) + .collect(Guavate.toImmutableSet())); + } + + public Keywords fromList(List<String> keywords) { + return fromSet(keywords.stream() + .map(Keyword::new) + .collect(Guavate.toImmutableSet())); + } + + @VisibleForTesting + Keywords fromMap(Map<String, Boolean> mapKeywords) { + Preconditions.checkArgument(mapKeywords.values() + .stream() + .noneMatch(keywordValue -> keywordValue == false), "Keyword must be true"); + Set<Keyword> setKeywords = mapKeywords.keySet() + .stream() + .map(Keyword::new) + .collect(Guavate.toImmutableSet()); + + return fromSet(setKeywords); + } + + @VisibleForTesting + Keywords fromOldKeyword(OldKeyword oldKeyword) { + ImmutableSet.Builder<Keyword> builder = ImmutableSet.builder(); + if (oldKeyword.isAnswered().orElse(false)) { + builder.add(Keyword.ANSWERED); + } + if (oldKeyword.isDraft().orElse(false)) { + builder.add(Keyword.DRAFT); + } + if (oldKeyword.isFlagged().orElse(false)) { + builder.add(Keyword.FLAGGED); + } + if (oldKeyword.isUnread().isPresent() && oldKeyword.isUnread().get() == false) { + builder.add(Keyword.SEEN); + } + return fromSet(builder.build()); + } + + public Keywords fromFlags(Flags flags) { + return fromSet(Stream.concat( + Stream.of(flags.getUserFlags()) + .map(Keyword::new), + Stream.of(flags.getSystemFlags()) + .map(Keyword::fromFlag)) + .collect(Guavate.toImmutableSet())); + } + + public Optional<Keywords> fromMapOrOldKeyword(Optional<Map<String, Boolean>> mapKeyword, Optional<OldKeyword> oldKeyword) { + Preconditions.checkArgument(!(mapKeyword.isPresent() && oldKeyword.isPresent()), "Does not support keyword and is* at the same time"); + + Keywords keywords = mapKeyword.map(this::fromMap) + .orElse(oldKeyword.map(this::fromOldKeyword) + .orElse(null)); + return Optional.ofNullable(keywords); + } + } + + public static KeywordsFactory factory() { + return new KeywordsFactory(); + } + + private final ImmutableSet<Keyword> keywords; + + private Keywords(ImmutableSet<Keyword> keywords) { + this.keywords = keywords; + } + + public Flags asFlags() { + return keywords.stream() + .reduce(FlagsBuilder.builder(), this::accumulator, this::combiner) + .build(); + } + + public Flags asFlagsWithRecentAndDeletedFrom(Flags originFlags) { + Flags flags = asFlags(); + if (originFlags.contains(Flags.Flag.DELETED)) { + flags.add(Flags.Flag.DELETED); + } + if (originFlags.contains(Flags.Flag.RECENT)) { + flags.add(Flags.Flag.RECENT); + } + + return flags; + } + + public ImmutableMap<String, Boolean> asMap() { + return keywords.stream() + .collect(Guavate.toImmutableMap(Keyword::getFlagName, keyword -> Keyword.FLAG_VALUE)); + } + + public ImmutableSet<Keyword> getKeywords() { + return keywords; + } + + public boolean contains(Keyword keyword) { + return keywords.contains(keyword); + } + + @Override + public final boolean equals(Object other) { + if (other instanceof Keywords) { + Keywords otherKeyword = (Keywords) other; + return Objects.equal(keywords, otherKeyword.keywords); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hashCode(keywords); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("keywords", keywords) + .toString(); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java new file mode 100644 index 0000000..e2cb2b3 --- /dev/null +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.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 java.util.Optional; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.MoreObjects; +import com.google.common.base.Objects; + +public class OldKeyword { + private final Optional<Boolean> isUnread; + private final Optional<Boolean> isFlagged; + private final Optional<Boolean> isAnswered; + private final Optional<Boolean> isDraft; + + @VisibleForTesting + OldKeyword(boolean isUnread, boolean isFlagged, boolean isAnswered, boolean isDraft) { + this.isUnread = Optional.of(isUnread); + this.isFlagged = Optional.of(isFlagged); + this.isAnswered = Optional.of(isAnswered); + this.isDraft = Optional.of(isDraft); + } + + public OldKeyword(Optional<Boolean> isUnread, Optional<Boolean> isFlagged, Optional<Boolean> isAnswered, Optional<Boolean> isDraft) { + this.isUnread = isUnread; + this.isFlagged = isFlagged; + this.isAnswered = isAnswered; + this.isDraft = isDraft; + } + + public Optional<Boolean> isUnread() { + return isUnread; + } + + public Optional<Boolean> isFlagged() { + return isFlagged; + } + + public Optional<Boolean> isAnswered() { + return isAnswered; + } + + public Optional<Boolean> isDraft() { + return isDraft; + } + + @Override + public final boolean equals(Object other) { + if (other instanceof OldKeyword) { + OldKeyword oldKeyword = (OldKeyword) other; + return Objects.equal(isUnread, oldKeyword.isUnread) + && Objects.equal(isFlagged, oldKeyword.isFlagged) + && Objects.equal(isAnswered, oldKeyword.isAnswered) + && Objects.equal(isDraft, oldKeyword.isDraft); + } + return false; + } + + @Override + public final int hashCode() { + return Objects.hashCode(isUnread, isFlagged, isAnswered, isDraft); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("isUnread", isUnread) + .add("isFlagged", isFlagged) + .add("isAnswered", isAnswered) + .add("isDraft", isDraft) + .toString(); + } + +} http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordTest.java new file mode 100644 index 0000000..12fcf0a --- /dev/null +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordTest.java @@ -0,0 +1,189 @@ +/**************************************************************** + * 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 static org.assertj.core.api.Assertions.assertThat; +import java.util.Optional; +import javax.mail.Flags; + +import org.apache.commons.lang3.StringUtils; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class KeywordTest { + private final static String FORWARDED = "forwarded"; + private final static int FLAG_NAME_MAX_LENTH = 255; + private final static String ANY_KEYWORD = "AnyKeyword"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void shouldRespectBeanContract() { + EqualsVerifier.forClass(Keyword.class).verify(); + } + + @Test + public void keywordShouldThrowWhenFlagNameLengthLessThanMinLength() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword(""); + } + + @Test + public void keywordShouldThrowWhenFlagNameLengthMoreThanMaxLength() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword(StringUtils.repeat("a", FLAG_NAME_MAX_LENTH + 1)); + } + + @Test + public void keywordShouldCreateNewOneWhenFlagNameLengthEqualsMaxLength() throws Exception { + String maxLengthFlagName = StringUtils.repeat("a", FLAG_NAME_MAX_LENTH); + Keyword keyword = new Keyword(maxLengthFlagName); + + assertThat(keyword.getFlagName()).isEqualTo(maxLengthFlagName); + } + + @Test + public void keywordShouldCreateNewOneWhenFlagNameLengthEqualsMinLength() throws Exception { + String minLengthFlagName = "a"; + Keyword keyword = new Keyword(minLengthFlagName); + + assertThat(keyword.getFlagName()).isEqualTo(minLengthFlagName); + } + + @Test + public void keywordShouldThrowWhenFlagNameContainsPercentageCharacter() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword("a%"); + } + + @Test + public void keywordShouldThrowWhenFlagNameContainsLeftBracket() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword("a["); + } + + @Test + public void keywordShouldThrowWhenFlagNameContainsRightBracket() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword("a]"); + } + + @Test + public void keywordShouldThrowWhenFlagNameContainsLeftBrace() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword("a{"); + } + + @Test + public void keywordShouldThrowWhenFlagNameContainsSlash() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword("a\\"); + } + + @Test + public void keywordShouldThrowWhenFlagNameContainsStar() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword("a*"); + } + + @Test + public void keywordShouldThrowWhenFlagNameContainsQuote() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword("a\""); + } + + @Test + public void keywordShouldThrowWhenFlagNameContainsOpeningParenthesis() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword("a("); + } + + @Test + public void keywordShouldThrowWhenFlagNameContainsClosingParenthesis() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword("a)"); + } + + @Test + public void keywordShouldThrowWhenFlagNameContainsSpaceCharacter() throws Exception { + expectedException.expect(IllegalArgumentException.class); + new Keyword("a b"); + } + + @Test + public void isNotNonExposedImapKeywordShouldReturnFalseWhenDeleted() throws Exception { + assertThat(Keyword.DELETED.isExposedImapKeyword()).isFalse(); + } + + @Test + public void isNotNonExposedImapKeywordShouldReturnFalseWhenRecent() throws Exception { + assertThat(Keyword.RECENT.isExposedImapKeyword()).isFalse(); + } + + @Test + public void isNotNonExposedImapKeywordShouldReturnTrueWhenOtherSystemFlag() throws Exception { + assertThat(Keyword.DRAFT.isExposedImapKeyword()).isTrue(); + } + + @Test + public void isNotNonExposedImapKeywordShouldReturnTrueWhenAnyUserFlag() throws Exception { + Keyword keyword = new Keyword(ANY_KEYWORD); + assertThat(keyword.isExposedImapKeyword()).isTrue(); + } + + @Test + public void isDraftShouldReturnTrueWhenDraft() throws Exception { + assertThat(Keyword.DRAFT.isDraft()).isTrue(); + } + + @Test + public void isDraftShouldReturnFalseWhenNonDraft() throws Exception { + assertThat(Keyword.DELETED.isDraft()).isFalse(); + } + + @Test + public void asSystemFlagShouldReturnSystemFlag() throws Exception { + assertThat(new Keyword("$Draft").asSystemFlag()) + .isEqualTo(Optional.of(Flags.Flag.DRAFT)); + } + + @Test + public void asSystemFlagShouldReturnEmptyWhenNonSystemFlag() throws Exception { + assertThat(new Keyword(ANY_KEYWORD).asSystemFlag().isPresent()) + .isFalse(); + } + + @Test + public void asFlagsShouldReturnFlagsWhenSystemFlag() throws Exception { + assertThat(Keyword.DELETED.asFlags()) + .isEqualTo(new Flags(Flags.Flag.DELETED)); + } + + @Test + public void asFlagsShouldReturnFlagsWhenUserFlag() throws Exception { + Keyword keyword = new Keyword(ANY_KEYWORD); + assertThat(keyword.asFlags()) + .isEqualTo(new Flags(ANY_KEYWORD)); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java new file mode 100644 index 0000000..e992c59 --- /dev/null +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java @@ -0,0 +1,328 @@ +/**************************************************************** + * 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 static org.assertj.core.api.Assertions.assertThat; +import java.util.Map; +import java.util.Optional; +import javax.mail.Flags; +import javax.mail.Flags.Flag; + +import org.apache.james.mailbox.FlagsBuilder; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; + +import nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class KeywordsTest { + public static final String ANY_KEYWORD = "AnyKeyword"; + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void shouldRespectBeanContract() { + EqualsVerifier.forClass(Keywords.class).verify(); + } + + @Test + public void fromMapShouldThrowWhenWrongKeywordValue() throws Exception { + expectedException.expect(IllegalArgumentException.class); + + Keywords.factory() + .fromMap(ImmutableMap.of(ANY_KEYWORD, false)); + } + + @Test + public void fromMapShouldReturnKeywordsFromMapStringAndBoolean() throws Exception { + Keywords keywords = Keywords.factory() + .fromMap(ImmutableMap.of(ANY_KEYWORD, Keyword.FLAG_VALUE)); + + assertThat(keywords.getKeywords()) + .containsOnly(new Keyword(ANY_KEYWORD)); + } + + @Test + public void fromFlagsShouldReturnKeywordsFromAllFlag() throws Exception { + Keywords keywords = Keywords.factory() + .fromFlags(new Flags(Flags.Flag.ANSWERED)); + + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.ANSWERED); + } + + @Test + public void fromSetShouldReturnKeywordsFromSetOfKeywords() throws Exception { + Keywords keywords = Keywords.factory() + .fromSet(ImmutableSet.of(Keyword.ANSWERED)); + + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.ANSWERED); + } + + @Test + public void fromOldKeywordsShouldReturnKeywordsFromIsAnswered() throws Exception { + Optional<Boolean> isAnswered = Optional.of(true); + Optional<Boolean> isUnread = Optional.empty(); + Optional<Boolean> isFlagged = Optional.empty(); + Optional<Boolean> isDraft = Optional.empty(); + Keywords keywords = Keywords.factory() + .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft)); + + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.ANSWERED); + } + + @Test + public void fromOldKeywordsShouldReturnKeywordsFromIsDraft() throws Exception { + Optional<Boolean> isAnswered = Optional.empty(); + Optional<Boolean> isUnread = Optional.empty(); + Optional<Boolean> isFlagged = Optional.empty(); + Optional<Boolean> isDraft = Optional.of(true); + Keywords keywords = Keywords.factory() + .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft)); + + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.DRAFT); + } + + @Test + public void fromOldKeywordsShouldReturnKeywordsFromIsFlagged() throws Exception { + Optional<Boolean> isAnswered = Optional.empty(); + Optional<Boolean> isUnread = Optional.empty(); + Optional<Boolean> isFlagged = Optional.of(true); + Optional<Boolean> isDraft = Optional.empty(); + Keywords keywords = Keywords.factory() + .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft)); + + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.FLAGGED); + } + + @Test + public void fromOldKeywordsShouldReturnKeywordsFromIsUnread() throws Exception { + Optional<Boolean> isAnswered = Optional.empty(); + Optional<Boolean> isUnread = Optional.of(false); + Optional<Boolean> isFlagged = Optional.empty(); + Optional<Boolean> isDraft = Optional.empty(); + Keywords keywords = Keywords.factory() + .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft)); + + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.SEEN); + } + + @Test + public void fromOldKeywordsShouldReturnKeywordsFromIsFlag() throws Exception { + Optional<Boolean> isAnswered = Optional.of(true); + Optional<Boolean> isUnread = Optional.of(true); + Optional<Boolean> isFlagged = Optional.of(true); + Optional<Boolean> isDraft = Optional.of(true); + Keywords keywords = Keywords.factory() + .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft)); + + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.ANSWERED, Keyword.DRAFT, Keyword.FLAGGED); + } + + @Test + public void fromMapOrOldKeywordShouldReturnEmptyWhenBothAreEmpty() throws Exception { + assertThat(Keywords.factory() + .fromMapOrOldKeyword(Optional.empty(), Optional.empty())) + .isEmpty(); + } + + @Test + public void fromMapOrOldKeywordShouldReturnKeywordsWhenMap() throws Exception { + assertThat(Keywords.factory() + .fromMapOrOldKeyword(Optional.of(ImmutableMap.of()), Optional.empty())) + .isPresent(); + } + + @Test + public void fromMapOrOldKeywordShouldReturnKeywordsWhenOldKeyword() throws Exception { + Optional<Boolean> isAnswered = Optional.of(true); + Optional<Boolean> isUnread = Optional.of(true); + Optional<Boolean> isFlagged = Optional.of(true); + Optional<Boolean> isDraft = Optional.of(true); + + OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft); + + assertThat(Keywords.factory() + .fromMapOrOldKeyword(Optional.empty(), Optional.of(oldKeyword))) + .isPresent(); + } + + @Test + public void fromMapOrOldKeywordShouldThrownWhenBothMapAndOldKeyword() throws Exception { + expectedException.expect(IllegalArgumentException.class); + Optional<Boolean> isAnswered = Optional.of(true); + Optional<Boolean> isUnread = Optional.of(true); + Optional<Boolean> isFlagged = Optional.of(true); + Optional<Boolean> isDraft = Optional.of(true); + + OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft); + + Keywords.factory() + .fromMapOrOldKeyword(Optional.of(ImmutableMap.of()), Optional.of(oldKeyword)); + } + + @Test + public void fromMapOrOldKeywordShouldReturnKeywordsFromMap() throws Exception { + ImmutableMap<String, Boolean> mapKeywords = ImmutableMap.of(ANY_KEYWORD, Keyword.FLAG_VALUE); + + assertThat(Keywords.factory() + .fromMapOrOldKeyword(Optional.of(mapKeywords), Optional.empty()) + .get() + .getKeywords()) + .containsOnly(new Keyword(ANY_KEYWORD)); + } + + @Test + public void fromMapOrOldKeywordShouldReturnKeywordsFromOldKeyword() throws Exception { + Optional<Boolean> isAnswered = Optional.of(true); + Optional<Boolean> isUnread = Optional.of(true); + Optional<Boolean> isFlagged = Optional.of(true); + Optional<Boolean> isDraft = Optional.of(true); + + OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft); + + Keywords keywords = Keywords.factory() + .fromMapOrOldKeyword(Optional.empty(), Optional.of(oldKeyword)) + .get(); + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.ANSWERED, Keyword.DRAFT, Keyword.FLAGGED); + } + + @Test + public void asFlagsShouldBuildFlagsFromKeywords() throws Exception { + assertThat(Keywords.factory() + .fromSet(ImmutableSet.of(Keyword.ANSWERED)) + .asFlags()) + .isEqualTo(new Flags(Flags.Flag.ANSWERED)); + } + + @Test + public void asFlagsWithRecentAndDeletedFromShouldBuildFlagsFromKeywordsAndRecentOriginFlags() throws Exception { + Flags originFlags = FlagsBuilder.builder() + .add(Flag.RECENT, Flag.DRAFT) + .build(); + + Flags expectedFlags = FlagsBuilder.builder() + .add(Flag.ANSWERED, Flag.RECENT) + .build(); + + assertThat(Keywords.factory() + .fromSet(ImmutableSet.of(Keyword.ANSWERED)) + .asFlagsWithRecentAndDeletedFrom(originFlags)) + .isEqualTo(expectedFlags); + } + + @Test + public void asFlagsWithRecentAndDeletedFromShouldBuildFlagsFromKeywordsWithDeletedAndRecentOriginFlags() throws Exception { + Flags originFlags = FlagsBuilder.builder() + .add(Flag.RECENT, Flag.DELETED, Flag.DRAFT) + .build(); + + Flags expectedFlags = FlagsBuilder.builder() + .add(Flag.ANSWERED, Flag.RECENT, Flag.DELETED) + .build(); + + assertThat(Keywords.factory() + .fromSet(ImmutableSet.of(Keyword.ANSWERED)) + .asFlagsWithRecentAndDeletedFrom(originFlags)) + .isEqualTo(expectedFlags); + } + + @Test + public void asMapShouldReturnEmptyWhenEmptyMapOfStringAndBoolean() throws Exception { + assertThat(Keywords.factory() + .fromSet(ImmutableSet.of()) + .asMap()) + .isEmpty();; + } + + @Test + public void asMapShouldReturnMapOfStringAndBoolean() throws Exception { + Map<String, Boolean> expectedMap = ImmutableMap.of("$Answered", Keyword.FLAG_VALUE); + assertThat(Keywords.factory() + .fromSet(ImmutableSet.of(Keyword.ANSWERED)) + .asMap()) + .isEqualTo(expectedMap); + } + + @Test + public void throwWhenUnsupportedKeywordShouldThrowWhenHaveUnsupportedKeywords() throws Exception { + expectedException.expect(IllegalArgumentException.class); + + Keywords.factory() + .throwOnImapNonExposedKeywords() + .fromSet(ImmutableSet.of(Keyword.DRAFT, Keyword.DELETED)); + } + + @Test + public void throwWhenUnsupportedKeywordShouldNotThrowWhenHaveDraft() throws Exception { + Keywords keywords = Keywords.factory() + .throwOnImapNonExposedKeywords() + .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DRAFT)); + + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.ANSWERED, Keyword.DRAFT); + } + + @Test + public void filterUnsupportedShouldFilter() throws Exception { + Keywords keywords = Keywords.factory() + .filterImapNonExposedKeywords() + .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DELETED, Keyword.RECENT, Keyword.DRAFT)); + + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.ANSWERED, Keyword.DRAFT); + } + + @Test + public void containsShouldReturnTrueWhenKeywordsContainKeyword() { + Keywords keywords = Keywords.factory() + .fromSet(ImmutableSet.of(Keyword.SEEN)); + + assertThat(keywords.contains(Keyword.SEEN)).isTrue(); + } + + @Test + public void containsShouldReturnFalseWhenKeywordsDoNotContainKeyword() { + Keywords keywords = Keywords.factory() + .fromSet(ImmutableSet.of()); + + assertThat(keywords.contains(Keyword.SEEN)).isFalse(); + } + + @Test + public void fromListShouldReturnKeywordsFromListOfStrings() throws Exception { + Keywords keywords = Keywords.factory() + .fromList(ImmutableList.of("$Answered", "$Flagged")); + + assertThat(keywords.getKeywords()) + .containsOnly(Keyword.ANSWERED, Keyword.FLAGGED); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/20beee7a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java new file mode 100644 index 0000000..024271a --- /dev/null +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java @@ -0,0 +1,31 @@ +/**************************************************************** + * 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 nl.jqno.equalsverifier.EqualsVerifier; +import org.junit.Test; + +public class OldKeywordTest { + @Test + public void shouldRespectBeanContract() { + EqualsVerifier.forClass(OldKeyword.class).verify(); + } + +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
