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:42 UTC

[04/13] james-project git commit: JAMES-2110 SetMessage with update should support keywords for flags

JAMES-2110 SetMessage with update should support keywords for flags


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

Branch: refs/heads/master
Commit: 92365a2ee4813062a37f113a308ea172bc84025e
Parents: 78a4a57
Author: quynhn <qn...@linagora.com>
Authored: Tue Aug 15 16:43:27 2017 +0700
Committer: Raphael Ouazana <ra...@linagora.com>
Committed: Thu Aug 24 15:47:27 2017 +0200

----------------------------------------------------------------------
 .../methods/SetMessagesUpdateProcessor.java     |   7 +
 .../james/jmap/model/UpdateMessagePatch.java    |  72 ++++----
 .../jmap/model/UpdateMessagePatchTest.java      | 184 +++++++++++++++++--
 3 files changed, 206 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/92365a2e/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
index c303359..da7dceb 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java
@@ -111,6 +111,13 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
             }
         } catch (MailboxException e) {
             handleMessageUpdateException(messageId, builder, e);
+        } catch (IllegalArgumentException e) {
+            ValidationResult invalidPropertyKeywords = ValidationResult.builder()
+                    .property(MessageProperties.MessageProperty.keywords.asFieldName())
+                    .message(e.getMessage())
+                    .build();
+
+            handleInvalidRequest(builder, messageId, ImmutableList.of(invalidPropertyKeywords));
         }
 
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/92365a2e/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java
index 32e3fe2..6a46bf8 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java
@@ -20,9 +20,9 @@
 package org.apache.james.jmap.model;
 
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-
 import javax.mail.Flags;
 
 import org.apache.james.jmap.methods.ValidationResult;
@@ -31,6 +31,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 
@@ -47,6 +48,7 @@ public class UpdateMessagePatch {
         private Optional<Boolean> isFlagged = Optional.empty();
         private Optional<Boolean> isUnread = Optional.empty();
         private Optional<Boolean> isAnswered = Optional.empty();
+        private Optional<Map<String, Boolean>> keywords = Optional.empty();
         private Set<ValidationResult> validationResult = Sets.newHashSet();
 
         public Builder mailboxIds(List<String> mailboxIds) {
@@ -54,6 +56,11 @@ public class UpdateMessagePatch {
             return this;
         }
 
+        public Builder keywords(Map<String, Boolean> keywords) {
+            this.keywords = Optional.of(ImmutableMap.copyOf(keywords));
+            return this;
+        }
+
         public Builder isFlagged(Boolean isFlagged) {
             this.isFlagged = Optional.of(isFlagged);
             return this;
@@ -81,28 +88,36 @@ public class UpdateMessagePatch {
                     .message("mailboxIds property is not supposed to be empty")
                     .build()));
             }
-            return new UpdateMessagePatch(mailboxIds, isUnread, isFlagged, isAnswered, ImmutableList.copyOf(validationResult));
+
+            Optional<Keywords> updateKeywords = Keywords.factory()
+                .throwOnImapNonExposedKeywords()
+                .fromMapOrOldKeyword(keywords, getOldKeywords());
+
+            return new UpdateMessagePatch(mailboxIds, updateKeywords, ImmutableList.copyOf(validationResult));
+        }
+
+        private Optional<OldKeyword> getOldKeywords() {
+            if (isAnswered.isPresent() || isFlagged.isPresent() || isUnread.isPresent()) {
+                Optional<Boolean> isDraft = Optional.empty();
+                return Optional.of(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft));
+            }
+            return Optional.empty();
         }
+
     }
 
     private final Optional<List<String>> mailboxIds;
-    private final Optional<Boolean> isUnread;
-    private final Optional<Boolean> isFlagged;
-    private final Optional<Boolean> isAnswered;
+    private final Optional<Keywords> keywords;
 
     private final ImmutableList<ValidationResult> validationErrors;
 
     @VisibleForTesting
     UpdateMessagePatch(Optional<List<String>> mailboxIds,
-                       Optional<Boolean> isUnread,
-                       Optional<Boolean> isFlagged,
-                       Optional<Boolean> isAnswered,
+                       Optional<Keywords> keywords,
                        ImmutableList<ValidationResult> validationResults) {
 
         this.mailboxIds = mailboxIds;
-        this.isUnread = isUnread;
-        this.isFlagged = isFlagged;
-        this.isAnswered = isAnswered;
+        this.keywords = keywords;
         this.validationErrors = validationResults;
     }
 
@@ -110,22 +125,12 @@ public class UpdateMessagePatch {
         return mailboxIds;
     }
 
-    public Optional<Boolean> isUnread() {
-        return isUnread;
-    }
-
-    public Optional<Boolean> isFlagged() {
-        return isFlagged;
-    }
-
-    public Optional<Boolean> isAnswered() {
-        return isAnswered;
+    public Optional<Keywords> getKeywords() {
+        return keywords;
     }
 
     public boolean isFlagsIdentity() {
-        return !isAnswered.isPresent()
-            && !isFlagged.isPresent()
-            && !isUnread.isPresent();
+        return !keywords.isPresent();
     }
 
     public ImmutableList<ValidationResult> getValidationErrors() {
@@ -137,19 +142,12 @@ public class UpdateMessagePatch {
     }
 
     public Flags applyToState(Flags currentFlags) {
-        Flags newStateFlags = new Flags();
-
-        if (isFlagged().orElse(currentFlags.contains(Flags.Flag.FLAGGED))) {
-            newStateFlags.add(Flags.Flag.FLAGGED);
-        }
-        if (isAnswered().orElse(currentFlags.contains(Flags.Flag.ANSWERED))) {
-            newStateFlags.add(Flags.Flag.ANSWERED);
-        }
-        boolean shouldMessageBeMarkSeen = isUnread().map(b -> !b).orElse(currentFlags.contains(Flags.Flag.SEEN));
-        if (shouldMessageBeMarkSeen) {
-            newStateFlags.add(Flags.Flag.SEEN);
-        }
-        return newStateFlags;
+        return keywords.map(keyword -> {
+            if (currentFlags.contains(Flags.Flag.DRAFT) != keyword.getKeywords().contains(Keyword.DRAFT)) {
+                throw new IllegalArgumentException("Cannot add or remove draft flag");
+            }
+            return keyword.asFlagsWithRecentAndDeletedFrom(currentFlags);
+        }).orElse(new Flags());
     }
 
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/92365a2e/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java
index e97c49c..e227746 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java
@@ -20,42 +20,52 @@
 package org.apache.james.jmap.model;
 
 import static org.assertj.core.api.Assertions.assertThat;
-
 import java.util.Arrays;
 import java.util.List;
-
 import javax.mail.Flags;
 
+import org.apache.james.mailbox.FlagsBuilder;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 public class UpdateMessagePatchTest {
+    private final static String FORWARDED = "forwarded";
+    @Rule
+    public ExpectedException expectedException = ExpectedException.none();
 
     @Test
     public void UnsetUpdatePatchShouldBeValid() {
         UpdateMessagePatch emptyPatch = UpdateMessagePatch.builder().build();
         assertThat(emptyPatch.isValid()).isTrue();
-        assertThat(emptyPatch.isUnread()).isEmpty();
-        assertThat(emptyPatch.isFlagged()).isEmpty();
-        assertThat(emptyPatch.isAnswered()).isEmpty();
     }
 
     @Test
     public void builderShouldSetUnreadFalseWhenBuiltWithIsUnreadFalse() {
         UpdateMessagePatch testee = UpdateMessagePatch.builder().isUnread(false).build();
-        assertThat(testee.isUnread()).isPresent();
-        assertThat(testee.isUnread().get()).isFalse();
     }
 
     @Test
-    public void applyStateShouldSetFlaggedOnlyWhenUnsetPatchAppliedToFlaggedState() {
-        UpdateMessagePatch testee = UpdateMessagePatch.builder().build();
-        Flags isFlaggedSet = new Flags(Flags.Flag.FLAGGED);
-        List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(isFlaggedSet).getSystemFlags());
+    public void applyStateShouldSetFlaggedOnlyWhenIsFlagged() {
+        UpdateMessagePatch testee = UpdateMessagePatch.builder().isFlagged(true).build();
+        List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(new Flags()).getSystemFlags());
         assertThat(updatedFlags).containsExactly(Flags.Flag.FLAGGED);
     }
 
 
     @Test
+    public void applyStateShouldRemoveFlaggedWhenEmptyIsFlaggedOnFlaggedMessage() {
+        UpdateMessagePatch testee = UpdateMessagePatch.builder().isAnswered(true).build();
+        Flags isFlagged = new Flags(Flags.Flag.FLAGGED);
+        List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(isFlagged).getSystemFlags());
+        assertThat(updatedFlags).doesNotContain(Flags.Flag.FLAGGED);
+    }
+
+    @Test
     public void applyStateShouldReturnUnreadFlagWhenUnreadSetOnSeenMessage() {
         UpdateMessagePatch testee = UpdateMessagePatch.builder().isUnread(true).build();
         Flags isSeen = new Flags(Flags.Flag.SEEN);
@@ -64,14 +74,6 @@ public class UpdateMessagePatchTest {
     }
 
     @Test
-    public void applyStateShouldReturnFlaggedWhenEmptyPatchOnFlaggedMessage() {
-        UpdateMessagePatch testee = UpdateMessagePatch.builder().build();
-        Flags isFlagged = new Flags(Flags.Flag.FLAGGED);
-        List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(isFlagged).getSystemFlags());
-        assertThat(updatedFlags).containsExactly(Flags.Flag.FLAGGED);
-    }
-
-    @Test
     public void applyStateShouldReturnSeenWhenPatchSetsSeenOnSeenMessage() {
         UpdateMessagePatch testee = UpdateMessagePatch.builder().isUnread(false).build();
         Flags isSeen = new Flags(Flags.Flag.SEEN);
@@ -80,7 +82,116 @@ public class UpdateMessagePatchTest {
     }
 
     @Test
-    public void isIdentityShouldReturnTrueWhenNoFlags() {
+    public void applyStateShouldReturnNewFlagsWhenKeywords() {
+        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
+                "$Answered", true,
+                "$Flagged", true);
+
+        UpdateMessagePatch testee = UpdateMessagePatch.builder()
+            .keywords(keywords)
+            .build();
+        Flags isSeen = new Flags(Flags.Flag.SEEN);
+        assertThat(testee.applyToState(isSeen).getSystemFlags())
+            .containsExactly(Flags.Flag.ANSWERED, Flags.Flag.FLAGGED);
+    }
+
+    @Test
+    public void applyStateShouldReturnRemoveFlagsWhenKeywords() {
+        UpdateMessagePatch testee = UpdateMessagePatch.builder()
+            .keywords(ImmutableMap.of())
+            .build();
+        Flags isSeen = new Flags(Flags.Flag.SEEN);
+        assertThat(testee.applyToState(isSeen).getSystemFlags()).isEmpty();
+    }
+
+    @Test
+    public void applyStateShouldThrowWhenKeywordsContainDeletedFlag() {
+        expectedException.expect(IllegalArgumentException.class);
+        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
+                "$Deleted", true);
+
+        UpdateMessagePatch testee = UpdateMessagePatch.builder()
+            .keywords(keywords)
+            .build();
+
+        Flags currentFlags = new Flags(Flags.Flag.SEEN);
+
+        testee.applyToState(currentFlags);
+    }
+
+    @Test
+    public void applyStateShouldThrowWhenKeywordsContainRecentFlag() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
+                "$Recent", true);
+        UpdateMessagePatch testee = UpdateMessagePatch.builder()
+            .keywords(keywords)
+            .build();
+
+        Flags currentFlags = new Flags(Flags.Flag.SEEN);
+
+        testee.applyToState(currentFlags);
+    }
+
+    @Test
+    public void applyStateShouldReturnFlagsWithoutUserFlagWhenKeywordsContainForwarded() {
+        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
+                "$Answered", Keyword.FLAG_VALUE,
+                FORWARDED, Keyword.FLAG_VALUE);
+
+        UpdateMessagePatch testee = UpdateMessagePatch.builder()
+            .keywords(keywords)
+            .build();
+        Flags isSeen = new Flags(Flags.Flag.SEEN);
+        assertThat(testee.applyToState(isSeen).getSystemFlags())
+            .doesNotContain(Flags.Flag.USER);
+    }
+
+    @Test
+    public void applyStateShouldReturnFlagsWithUserFlagStringWhenKeywordsContainForwarded() {
+        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
+                "$Answered", Keyword.FLAG_VALUE,
+                FORWARDED, Keyword.FLAG_VALUE);
+
+        UpdateMessagePatch testee = UpdateMessagePatch.builder()
+            .keywords(keywords)
+            .build();
+        Flags isSeen = new Flags(Flags.Flag.SEEN);
+        assertThat(testee.applyToState(isSeen).getUserFlags())
+            .containsExactly(FORWARDED);
+    }
+
+    @Test
+    public void applyStateShouldReturnFlagsWithDeletedFlagWhenKeywordsDoNotContainDeletedButOriginFlagsHaveDeleted() {
+        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
+                "$Answered", Keyword.FLAG_VALUE);
+
+        UpdateMessagePatch testee = UpdateMessagePatch.builder()
+            .keywords(keywords)
+            .build();
+        Flags isSeen = new Flags(Flags.Flag.DELETED);
+        assertThat(testee.applyToState(isSeen).getSystemFlags())
+            .containsOnly(Flags.Flag.DELETED, Flags.Flag.ANSWERED);
+    }
+
+    @Test
+    public void applyStateShouldReturnFlagsWithRecentFlagWhenKeywordsDoNotContainRecentButOriginFlagsHaveRecent() {
+        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
+                "$Answered", Keyword.FLAG_VALUE);
+
+        UpdateMessagePatch testee = UpdateMessagePatch.builder()
+            .keywords(keywords)
+            .build();
+        Flags flags = FlagsBuilder.builder()
+            .add(Flags.Flag.DELETED, Flags.Flag.RECENT)
+            .build();
+        assertThat(testee.applyToState(flags).getSystemFlags())
+            .containsOnly(Flags.Flag.DELETED, Flags.Flag.RECENT, Flags.Flag.ANSWERED);
+    }
+
+    @Test
+    public void isIdentityShouldReturnTrueWhenNoFlagsAndEmptyKeywords() {
         UpdateMessagePatch messagePatch = UpdateMessagePatch.builder().build();
 
         assertThat(messagePatch.isFlagsIdentity()).isTrue();
@@ -113,4 +224,37 @@ public class UpdateMessagePatchTest {
         assertThat(messagePatch.isFlagsIdentity()).isFalse();
     }
 
+    @Test
+    public void isIdentityShouldReturnFalseWhenKeywords() {
+        ImmutableMap<String, Boolean> keywords = ImmutableMap.of(
+            "$Answered", Keyword.FLAG_VALUE,
+            "$Flagged", Keyword.FLAG_VALUE);
+
+        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
+                .keywords(keywords)
+                .build();
+
+        assertThat(messagePatch.isFlagsIdentity()).isFalse();
+    }
+
+    @Test
+    public void isIdentityShouldReturnFalseWhenEmptyKeywords() {
+        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
+                .keywords(ImmutableMap.of())
+                .build();
+
+        assertThat(messagePatch.isFlagsIdentity()).isFalse();
+    }
+
+    @Test
+    public void isIdentityShouldThrowWhenBothIsFlagAndKeywords() {
+        expectedException.expect(IllegalArgumentException.class);
+
+        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
+                .keywords(ImmutableMap.of())
+                .isAnswered(false)
+                .build();
+
+        messagePatch.isFlagsIdentity();
+    }
 }
\ No newline at end of file


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