Repository: james-project
Updated Branches:
  refs/heads/master 51cc52ae3 -> fb73fe9e1


JAMES-2218 More clever SetMessages update validation to accept all valid states

Moving a non draft message to a draft mailbox while marking it as draft should 
be tolerated
Moving a draft message out of draft mailbox while marking it as non-draft 
should be tolerated


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

Branch: refs/heads/master
Commit: 7c1685b442269edc382643ca39e9f6b1b7b275da
Parents: e505089
Author: benwa <btell...@linagora.com>
Authored: Thu Nov 16 14:20:26 2017 +0700
Committer: Antoine Duprat <adup...@linagora.com>
Committed: Thu Nov 16 12:28:58 2017 +0100

----------------------------------------------------------------------
 .../cucumber/SetMessagesMethodStepdefs.java     | 69 +++++++++++----
 .../test/resources/cucumber/SetMessages.feature | 23 +++--
 .../DraftMessageMailboxUpdateException.java     |  4 +-
 .../methods/SetMessagesUpdateProcessor.java     | 91 +++++++++++++++-----
 .../james/jmap/model/UpdateMessagePatch.java    | 13 +--
 5 files changed, 146 insertions(+), 54 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/7c1685b4/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java
index ff30634..8cdd65a 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java
@@ -59,12 +59,12 @@ public class SetMessagesMethodStepdefs {
         this.messageIdStepdefs = messageIdStepdefs;
     }
 
-    @When("^\"([^\"]*)\" moves \"([^\"]*)\" to user mailbox \"([^\"]*)\"")
+    @When("^\"([^\"]*)\" moves \"([^\"]*)\" to user mailbox \"([^\"]*)\"$")
     public void moveMessageToMailboxWithUser(String username, String message, 
String mailbox) throws Throwable {
         userStepdefs.execWithUser(username, () -> 
moveMessageToMailbox(message, mailbox));
     }
 
-    @When("^the user moves \"([^\"]*)\" to user mailbox \"([^\"]*)\"")
+    @When("^the user moves \"([^\"]*)\" to user mailbox \"([^\"]*)\"$")
     public void moveMessageToMailbox(String message, String mailbox) throws 
Throwable {
         MessageId messageId = messageIdStepdefs.getMessageId(message);
         MailboxId mailboxId = mainStepdefs.jmapServer
@@ -86,12 +86,36 @@ public class SetMessagesMethodStepdefs {
         mainStepdefs.awaitMethod.run();
     }
 
-    @When("^\"([^\"]*)\" copies \"([^\"]*)\" from mailbox \"([^\"]*)\" to 
mailbox \"([^\"]*)\"")
+    @When("^the user moves \"([^\"]*)\" to user mailbox \"([^\"]*)\" and set 
flags \"([^\"]*)\"$")
+    public void moveMessageToMailboxAndChangeFlags(String message, String 
mailbox, List<String> keywords) throws Throwable {
+        MessageId messageId = messageIdStepdefs.getMessageId(message);
+        MailboxId mailboxId = mainStepdefs.jmapServer
+            .getProbe(MailboxProbeImpl.class)
+            .getMailbox(MailboxConstants.USER_NAMESPACE, 
userStepdefs.getConnectedUser(), mailbox)
+            .getMailboxId();
+        String keywordString = toKeywordsString(keywords);
+
+        httpClient.post("[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"update\": { \"" + messageId.serialize() + "\" : {" +
+            "        \"mailboxIds\": [\"" + mailboxId.serialize() + "\"]," +
+            "        \"keywords\": {" + keywordString + "}" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]");
+        mainStepdefs.awaitMethod.run();
+    }
+
+    @When("^\"([^\"]*)\" copies \"([^\"]*)\" from mailbox \"([^\"]*)\" to 
mailbox \"([^\"]*)\"$")
     public void copyMessageToMailbox(String username, String message, String 
sourceMailbox, String destinationMailbox) throws Throwable {
         userStepdefs.execWithUser(username, () -> 
copyMessageToMailbox(message, sourceMailbox, destinationMailbox));
     }
 
-    @When("^the user copies \"([^\"]*)\" from mailbox \"([^\"]*)\" to mailbox 
\"([^\"]*)\"")
+    @When("^the user copies \"([^\"]*)\" from mailbox \"([^\"]*)\" to mailbox 
\"([^\"]*)\"$")
     public void copyMessageToMailbox(String message, String sourceMailbox, 
String destinationMailbox) throws Throwable {
         MessageId messageId = messageIdStepdefs.getMessageId(message);
         MailboxId sourceMailboxId = mainStepdefs.jmapServer
@@ -117,7 +141,7 @@ public class SetMessagesMethodStepdefs {
         mainStepdefs.awaitMethod.run();
     }
 
-    @When("^\"([^\"]*)\" copies \"([^\"]*)\" from mailbox \"([^\"]*)\" of user 
\"([^\"]*)\" to mailbox \"([^\"]*)\" of user \"([^\"]*)\"")
+    @When("^\"([^\"]*)\" copies \"([^\"]*)\" from mailbox \"([^\"]*)\" of user 
\"([^\"]*)\" to mailbox \"([^\"]*)\" of user \"([^\"]*)\"$")
     public void copyMessageToMailbox(String username, String message, String 
sourceMailbox, String sourceUser, String destinationMailbox, String 
destinationUser) throws Throwable {
         userStepdefs.execWithUser(username, () -> 
copyMessageToMailbox(message, sourceMailbox, sourceUser, destinationMailbox, 
destinationUser));
     }
@@ -147,7 +171,7 @@ public class SetMessagesMethodStepdefs {
         mainStepdefs.awaitMethod.run();
     }
 
-    @Given("^\"([^\"]*)\" moves \"([^\"]*)\" to mailbox \"([^\"]*)\" of user 
\"([^\"]*)\"")
+    @Given("^\"([^\"]*)\" moves \"([^\"]*)\" to mailbox \"([^\"]*)\" of user 
\"([^\"]*)\"$")
     public void moveMessageToMailbox(String username, String message, String 
destinationMailbox, String destinationUser) throws Throwable {
         userStepdefs.execWithUser(username, () -> 
moveMessageToMailbox(message, destinationMailbox, destinationUser));
     }
@@ -173,12 +197,12 @@ public class SetMessagesMethodStepdefs {
         mainStepdefs.awaitMethod.run();
     }
 
-    @When("^\"([^\"]*)\" sets flags \"([^\"]*)\" on message \"([^\"]*)\"")
+    @When("^\"([^\"]*)\" sets flags \"([^\"]*)\" on message \"([^\"]*)\"$")
     public void setFlags(String username, List<String> keywords, String 
message) throws Throwable {
         userStepdefs.execWithUser(username, () -> setFlags(keywords, message));
     }
 
-    @When("^\"([^\"]*)\" destroys message \"([^\"]*)\"")
+    @When("^\"([^\"]*)\" destroys message \"([^\"]*)\"$")
     public void destroyMessage(String username, String message) throws 
Throwable {
         MessageId messageId = messageIdStepdefs.getMessageId(message);
         userStepdefs.execWithUser(username, () -> {
@@ -195,7 +219,7 @@ public class SetMessagesMethodStepdefs {
         });
     }
 
-    @Given("^\"([^\"]*)\" creates a draft message \"([^\"]*)\" in mailbox 
\"([^\"]*)\"")
+    @Given("^\"([^\"]*)\" tries to create a draft message \"([^\"]*)\" in 
mailbox \"([^\"]*)\"$")
     public void createDraft(String username, String message, String 
mailboxName) throws Throwable {
         userStepdefs.execWithUser(username, () -> {
             Mailbox mailbox = 
mainStepdefs.mailboxProbe.getMailbox(MailboxConstants.USER_NAMESPACE,
@@ -222,13 +246,10 @@ public class SetMessagesMethodStepdefs {
         });
     }
 
-    @When("^the user sets flags \"([^\"]*)\" on message \"([^\"]*)\"")
+    @When("^the user sets flags \"([^\"]*)\" on message \"([^\"]*)\"$")
     public void setFlags(List<String> keywords, String message) throws 
Throwable {
         MessageId messageId = messageIdStepdefs.getMessageId(message);
-        String keywordString = keywords
-            .stream()
-            .map(value -> "\"" + value + "\" : true")
-            .collect(Collectors.joining(","));
+        String keywordString = toKeywordsString(keywords);
 
         httpClient.post("[" +
             "  [" +
@@ -244,7 +265,14 @@ public class SetMessagesMethodStepdefs {
         mainStepdefs.awaitMethod.run();
     }
 
-    @When("^message \"([^\"]*)\" has flags (.*) in mailbox \"([^\"]*)\" of 
user \"([^\"]*)\"")
+    private String toKeywordsString(List<String> keywords) {
+        return keywords
+                .stream()
+                .map(value -> "\"" + value + "\" : true")
+                .collect(Collectors.joining(","));
+    }
+
+    @When("^message \"([^\"]*)\" has flags (.*) in mailbox \"([^\"]*)\" of 
user \"([^\"]*)\"$")
     public void setMessageFlagsInSpecifiedMailbox(String message, List<String> 
flags, String mailbox, String mailboxOwner) throws Exception {
         Flags newFlags = Keywords.factory().fromList(flags).asFlags();
         String username = userStepdefs.getConnectedUser();
@@ -259,13 +287,20 @@ public class SetMessagesMethodStepdefs {
     }
 
     @Then("^message \"([^\"]*)\" is not updated$")
-    public void assertIdOfTheFirstMessage(String messageName) throws Exception 
{
+    public void assertNotUpdate(String messageName) throws Exception {
         MessageId id = messageIdStepdefs.getMessageId(messageName);
         assertThat(httpClient.jsonPath.<Map<String, 
String>>read("[0][1].notUpdated"))
             .containsOnlyKeys(id.serialize());
     }
 
-    @Then("^message \"([^\"]*)\" is not created")
+    @Then("^message \"([^\"]*)\" is updated$")
+    public void assertUpdated(String messageName) throws Exception {
+        MessageId id = messageIdStepdefs.getMessageId(messageName);
+        assertThat(httpClient.jsonPath.<List<String>>read("[0][1].updated"))
+            .containsOnly(id.serialize());
+    }
+
+    @Then("^message \"([^\"]*)\" is not created$")
     public void assertNotCreated(String messageName) throws Exception {;
         assertThat(httpClient.jsonPath.<Map<String, 
String>>read("[0][1].notCreated"))
             .containsOnlyKeys(messageName);

http://git-wip-us.apache.org/repos/asf/james-project/blob/7c1685b4/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature
 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature
index ac816e4..2fdc88d 100644
--- 
a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature
+++ 
b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature
@@ -106,38 +106,49 @@ Feature: SetMessages method on shared folders
 
   Scenario: A user can update the flags on a draft
     Given "b...@domain.tld" has a mailbox "Drafts"
-    And "b...@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    And "b...@domain.tld" tries to create a draft message "mDraft" in mailbox 
"Drafts"
     When "b...@domain.tld" sets flags "$Draft,$Seen" on message "mDraft"
     Then "b...@domain.tld" should see message "mDraft" with keywords 
$Draft,$Seen
 
   Scenario: A user can not remove a draft flag on a draft messages
     Given "b...@domain.tld" has a mailbox "Drafts"
-    And "b...@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    And "b...@domain.tld" tries to create a draft message "mDraft" in mailbox 
"Drafts"
     When "b...@domain.tld" sets flags "$Seen" on message "mDraft"
     Then message "mDraft" is not updated
     And "b...@domain.tld" should see message "mDraft" with keywords $Draft
 
   Scenario: A user can destroy a draft
     Given "b...@domain.tld" has a mailbox "Drafts"
-    And "b...@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    And "b...@domain.tld" tries to create a draft message "mDraft" in mailbox 
"Drafts"
     When "b...@domain.tld" destroys message "mDraft"
     Then "b...@domain.tld" ask for message "mDraft"
     And the notFound list should contain "mDraft"
 
   Scenario: Draft creation in outbox is not allowed
     Given "b...@domain.tld" has a mailbox "Outbox"
-    When "b...@domain.tld" creates a draft message "mDraft" in mailbox "Outbox"
+    When "b...@domain.tld" tries to create a draft message "mDraft" in mailbox 
"Outbox"
     Then message "mDraft" is not created
 
   Scenario: A user can not move draft out of draft mailbox
     Given "b...@domain.tld" has a mailbox "Drafts"
-    And "b...@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    And "b...@domain.tld" tries to create a draft message "mDraft" in mailbox 
"Drafts"
     When "b...@domain.tld" moves "mDraft" to user mailbox "shared"
     Then message "mDraft" is not updated
 
+  Scenario: A user can move draft out of draft mailbox when removing draft flag
+    Given "b...@domain.tld" has a mailbox "Drafts"
+    And "b...@domain.tld" tries to create a draft message "mDraft" in mailbox 
"Drafts"
+    When the user moves "mDraft" to user mailbox "shared" and set flags ""
+    Then message "mDraft" is updated
+
+  Scenario: A user can move non-draft messages to draft mailbox when setting 
$Draft
+    Given "b...@domain.tld" has a mailbox "Drafts"
+    When the user moves "mBob" to user mailbox "Drafts" and set flags "$Draft"
+    Then message "mBob" is updated
+
   Scenario: A user can not copy draft out of draft mailbox
     Given "b...@domain.tld" has a mailbox "Drafts"
-    And "b...@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    And "b...@domain.tld" tries to create a draft message "mDraft" in mailbox 
"Drafts"
     When "b...@domain.tld" copies "mDraft" from mailbox "Drafts" to mailbox 
"shared"
     Then message "mDraft" is not updated
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/7c1685b4/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/DraftMessageMailboxUpdateException.java
----------------------------------------------------------------------
diff --git 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/DraftMessageMailboxUpdateException.java
 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/DraftMessageMailboxUpdateException.java
index 296dc9c..862a282 100644
--- 
a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/DraftMessageMailboxUpdateException.java
+++ 
b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/DraftMessageMailboxUpdateException.java
@@ -23,7 +23,7 @@ import org.apache.james.mailbox.exception.MailboxException;
 
 public class DraftMessageMailboxUpdateException extends MailboxException {
 
-    public DraftMessageMailboxUpdateException() {
-        super();
+    public DraftMessageMailboxUpdateException(String message) {
+        super(message);
     }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/7c1685b4/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 f23ba55..2497cea 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
@@ -100,6 +100,7 @@ public class SetMessagesUpdateProcessor implements 
SetMessagesProcessor {
                         SetMessagesResponse.Builder builder) {
         try {
             List<MessageResult> messages = 
messageIdManager.getMessages(ImmutableList.of(messageId), 
FetchGroupImpl.MINIMAL, mailboxSession);
+            assertValidUpdate(messages, updateMessagePatch, mailboxSession);
 
             if (messages.isEmpty()) {
                 addMessageIdNotFoundToResponse(messageId, builder);
@@ -117,7 +118,7 @@ public class SetMessagesUpdateProcessor implements 
SetMessagesProcessor {
                 }
             }
         } catch (DraftMessageMailboxUpdateException e) {
-            handleDraftMessageMailboxUpdateException(messageId, builder);
+            handleDraftMessageMailboxUpdateException(messageId, builder, e);
         } catch (MailboxException e) {
             handleMessageUpdateException(messageId, builder, e);
         } catch (IllegalArgumentException e) {
@@ -131,6 +132,69 @@ public class SetMessagesUpdateProcessor implements 
SetMessagesProcessor {
 
     }
 
+    private void assertValidUpdate(List<MessageResult> messagesToBeUpdated, 
UpdateMessagePatch updateMessagePatch, MailboxSession session) throws 
MailboxException {
+        List<MailboxId> draftMailboxes = mailboxIdFor(Role.DRAFTS, session);
+        List<Flags> futureFlags = patchFlags(messagesToBeUpdated, 
updateMessagePatch);
+        List<MailboxId> targetMailboxes = 
getTargetedMailboxes(messagesToBeUpdated, updateMessagePatch);
+
+        boolean targetHasDraft = 
targetMailboxes.stream().anyMatch(draftMailboxes::contains);
+        boolean targetHasNonDraft = targetMailboxes.stream().anyMatch(id -> 
!draftMailboxes.contains(id));
+
+        assertValidUpdate(futureFlags, targetHasDraft, targetHasNonDraft);
+    }
+
+    private void assertValidUpdate(List<Flags> futureFlags, boolean 
targetHasDraft, boolean targetHasNonDraft) throws 
DraftMessageMailboxUpdateException {
+        assertMessageIsNotInDraftAndNonDraftMailboxes(targetHasDraft, 
targetHasNonDraft);
+        assertNoNonDraftMessageInsideDraftMailbox(futureFlags, targetHasDraft);
+        assertNoDraftMessageOutOfDraftMailbox(futureFlags, targetHasNonDraft);
+    }
+
+    private void assertMessageIsNotInDraftAndNonDraftMailboxes(boolean 
targetHasDraft, boolean targetHasNonDraft) throws 
DraftMessageMailboxUpdateException {
+        if (targetHasDraft && targetHasNonDraft) {
+            throw new DraftMessageMailboxUpdateException("One can not have a 
message in mailboxes that don't have all the `draft` role");
+        }
+    }
+
+    private void assertNoNonDraftMessageInsideDraftMailbox(List<Flags> 
futureFlags, boolean targetHasDraft) throws DraftMessageMailboxUpdateException {
+        if (targetHasDraft) {
+            boolean anyNonDraftMessage = futureFlags.stream().anyMatch(flags 
-> !flags.contains(Flags.Flag.DRAFT));
+            if (anyNonDraftMessage) {
+                throw new DraftMessageMailboxUpdateException("Messages without 
'$Draft' keyword are prohibited in drafts mailboxes");
+            }
+        }
+    }
+
+    private void assertNoDraftMessageOutOfDraftMailbox(List<Flags> 
futureFlags, boolean targetHasNonDraft) throws 
DraftMessageMailboxUpdateException {
+        if (targetHasNonDraft) {
+            boolean anyDraftMessage = futureFlags.stream().anyMatch(flags -> 
flags.contains(Flags.Flag.DRAFT));
+            if (anyDraftMessage) {
+                throw new DraftMessageMailboxUpdateException("Messages with 
'$Draft' keyword are prohibited in non drafts mailboxes");
+            }
+        }
+    }
+
+    private List<MailboxId> getTargetedMailboxes(List<MessageResult> 
messagesToBeUpdated, UpdateMessagePatch updateMessagePatch) {
+        ImmutableList<MailboxId> previousMailboxes = 
messagesToBeUpdated.stream()
+            .map(MessageResult::getMailboxId)
+            .collect(Guavate.toImmutableList());
+        return updateMessagePatch.getMailboxIds()
+            .map(ids -> 
ids.stream().map(mailboxIdFactory::fromString).collect(Guavate.toImmutableList()))
+            .orElse(previousMailboxes);
+    }
+
+    private List<Flags> patchFlags(List<MessageResult> messagesToBeUpdated, 
UpdateMessagePatch updateMessagePatch) {
+        return messagesToBeUpdated.stream()
+            .map(MessageResult::getFlags)
+            .map(updateMessagePatch::applyToStateNoReset)
+            .collect(Guavate.toImmutableList());
+    }
+
+    private List<MailboxId> mailboxIdFor(Role role, MailboxSession session) 
throws MailboxException {
+        return systemMailboxesProvider.getMailboxByRole(role, session)
+            .map(MessageManager::getId)
+            .collect(Guavate.toImmutableList());
+    }
+
     private Stream<MailboxException> updateFlags(MessageId messageId, 
UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession, 
MessageResult messageResult) {
         try {
             if (!updateMessagePatch.isFlagsIdentity()) {
@@ -151,30 +215,10 @@ public class SetMessagesUpdateProcessor implements 
SetMessagesProcessor {
                 .map(mailboxIdFactory::fromString)
                 .collect(Guavate.toImmutableList());
 
-            assertNotDraftMailbox(mailboxSession, mailboxIds);
-            assertNotDraftMessage(originalFlags);
-
             messageIdManager.setInMailboxes(messageId, mailboxIds, 
mailboxSession);
         }
     }
 
-    private void assertNotDraftMessage(Stream<Flags> originalFlags) throws 
DraftMessageMailboxUpdateException {
-        boolean isADraftMessage = originalFlags
-            .anyMatch(flags -> flags.contains(Flags.Flag.DRAFT));
-        if (isADraftMessage) {
-            throw new DraftMessageMailboxUpdateException();
-        }
-    }
-
-    private void assertNotDraftMailbox(MailboxSession mailboxSession, 
List<MailboxId> mailboxIds) throws MailboxException {
-        Stream<MessageManager> draftMailboxes = 
systemMailboxesProvider.getMailboxByRole(Role.DRAFTS, mailboxSession);
-
-        boolean containsDraftMailboxes = 
draftMailboxes.map(MessageManager::getId).anyMatch(mailboxIds::contains);
-        if (containsDraftMailboxes) {
-            throw new DraftMessageMailboxUpdateException();
-        }
-    }
-
     private void addMessageIdNotFoundToResponse(MessageId messageId, 
SetMessagesResponse.Builder builder) {
         builder.notUpdated(ImmutableMap.of(messageId,
                 SetError.builder()
@@ -185,11 +229,12 @@ public class SetMessagesUpdateProcessor implements 
SetMessagesProcessor {
     }
 
     private SetMessagesResponse.Builder 
handleDraftMessageMailboxUpdateException(MessageId messageId,
-                                                                     
SetMessagesResponse.Builder builder) {
+                                                                     
SetMessagesResponse.Builder builder,
+                                                                     
DraftMessageMailboxUpdateException e) {
         return builder.notUpdated(ImmutableMap.of(messageId, SetError.builder()
             .type("invalidArguments")
             .properties(MessageProperties.MessageProperty.mailboxIds)
-            .description("Draft messages can not be moved or copied out of the 
Draft mailbox")
+            .description(e.getMessage())
             .build()));
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/7c1685b4/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 49a2ac8..a49df67 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
@@ -149,12 +149,13 @@ public class UpdateMessagePatch {
     }
 
     public Flags applyToState(Flags currentFlags) {
-        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());
+        return keywords.map(keyword -> 
keyword.asFlagsWithRecentAndDeletedFrom(currentFlags))
+            .orElse(new Flags());
+    }
+
+    public Flags applyToStateNoReset(Flags currentFlags) {
+        return keywords.map(keyword -> 
keyword.asFlagsWithRecentAndDeletedFrom(currentFlags))
+            .orElse(currentFlags);
     }
 
 }


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

Reply via email to