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 ad...@apache.org on 2017/11/16 14:19:48 UTC

[01/18] james-project git commit: JAMES-2218 More clever SetMessages update validation to accept all valid states

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 <bt...@linagora.com>
Authored: Thu Nov 16 14:20:26 2017 +0700
Committer: Antoine Duprat <ad...@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 "bob@domain.tld" has a mailbox "Drafts"
-    And "bob@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    And "bob@domain.tld" tries to create a draft message "mDraft" in mailbox "Drafts"
     When "bob@domain.tld" sets flags "$Draft,$Seen" on message "mDraft"
     Then "bob@domain.tld" should see message "mDraft" with keywords $Draft,$Seen
 
   Scenario: A user can not remove a draft flag on a draft messages
     Given "bob@domain.tld" has a mailbox "Drafts"
-    And "bob@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    And "bob@domain.tld" tries to create a draft message "mDraft" in mailbox "Drafts"
     When "bob@domain.tld" sets flags "$Seen" on message "mDraft"
     Then message "mDraft" is not updated
     And "bob@domain.tld" should see message "mDraft" with keywords $Draft
 
   Scenario: A user can destroy a draft
     Given "bob@domain.tld" has a mailbox "Drafts"
-    And "bob@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    And "bob@domain.tld" tries to create a draft message "mDraft" in mailbox "Drafts"
     When "bob@domain.tld" destroys message "mDraft"
     Then "bob@domain.tld" ask for message "mDraft"
     And the notFound list should contain "mDraft"
 
   Scenario: Draft creation in outbox is not allowed
     Given "bob@domain.tld" has a mailbox "Outbox"
-    When "bob@domain.tld" creates a draft message "mDraft" in mailbox "Outbox"
+    When "bob@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 "bob@domain.tld" has a mailbox "Drafts"
-    And "bob@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    And "bob@domain.tld" tries to create a draft message "mDraft" in mailbox "Drafts"
     When "bob@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 "bob@domain.tld" has a mailbox "Drafts"
+    And "bob@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 "bob@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 "bob@domain.tld" has a mailbox "Drafts"
-    And "bob@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    And "bob@domain.tld" tries to create a draft message "mDraft" in mailbox "Drafts"
     When "bob@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-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


[05/18] james-project git commit: JAMES-2218 Migrate Draft update/destroy tests to use cucumber + JMAP created draft message

Posted by ad...@apache.org.
JAMES-2218 Migrate Draft update/destroy tests to use cucumber + JMAP created draft message


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

Branch: refs/heads/master
Commit: 45e7f3a4ff116a2f9b555e611f5696198aad9afd
Parents: 29510c2
Author: benwa <bt...@linagora.com>
Authored: Wed Nov 15 14:58:37 2017 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:28:58 2017 +0100

----------------------------------------------------------------------
 .../integration/SetMessagesMethodTest.java      | 24 -----------
 .../cucumber/GetMessagesMethodStepdefs.java     | 11 +++--
 .../cucumber/SetMessagesMethodStepdefs.java     | 45 ++++++++++++++++++++
 .../test/resources/cucumber/SetMessages.feature | 24 ++++++++++-
 4 files changed, 76 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/45e7f3a4/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
index ad99100..951d369 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
@@ -564,30 +564,6 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
-    public void setMessagesShouldReturnAnErrorWhenKeywordsWithAddingDraftArePassed() throws MailboxException {
-        mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
-
-        ComposedMessageId message = mailboxProbe.appendMessage(USERNAME, USER_MAILBOX,
-                new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags(Flags.Flag.ANSWERED));
-        await();
-
-        String messageId = message.getMessageId().serialize();
-
-        given()
-            .header("Authorization", accessToken.serialize())
-            .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"keywords\": {\"$Draft\": true} } } }, \"#0\"]]", messageId))
-        .when()
-            .post("/jmap")
-        .then()
-            .log().ifValidationFails()
-            .body(NOT_UPDATED, hasKey(messageId))
-            .body(NOT_UPDATED + "[\""+messageId+"\"].type", equalTo("invalidProperties"))
-            .body(NOT_UPDATED + "[\""+messageId+"\"].properties[0]", equalTo("keywords"))
-            .body(NOT_UPDATED + "[\""+messageId+"\"].description", equalTo("keywords: Cannot add or remove draft flag"))
-            .body(ARGUMENTS + ".updated", hasSize(0));
-    }
-
-    @Test
     public void setMessagesShouldReturnAnErrorWhenKeywordsWithDeletedArePassed() throws MailboxException {
         mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/45e7f3a4/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
index f594b30..96df760 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
@@ -94,6 +94,10 @@ public class GetMessagesMethodStepdefs {
         return messageIdsByName.get(name);
     }
 
+    public MessageId addMessageId(String name, MessageId messageId) {
+        return messageIdsByName.put(name, messageId);
+    }
+
     @Given("^the user has a message \"([^\"]*)\" in \"([^\"]*)\" and \"([^\"]*)\" mailboxes with subject \"([^\"]*)\", content \"([^\"]*)\"$")
     public void appendMessageInTwoMailboxes(String messageName, String mailbox1, String mailbox2, String subject, String content) throws Exception {
         MessageId id = appendMessage(mailbox1, ContentType.noContentType(), subject, content, NO_HEADERS);
@@ -438,7 +442,7 @@ public class GetMessagesMethodStepdefs {
         askMessages(requestedMessageIds);
     }
 
-    @When("^\"(.*?)\" ask for messages \"(.*?)\"$")
+    @When("^\"(.*?)\" ask for (?:messages|message) \"(.*?)\"$")
     public void postWithAListOfIds(String user, List<String> ids) throws Throwable {
         userStepdefs.execWithUser(user, () -> postWithAListOfIds(ids));
     }
@@ -526,8 +530,9 @@ public class GetMessagesMethodStepdefs {
     }
 
     @Then("^the notFound list should contain \"([^\"]*)\"$")
-    public void assertNotFoundListContains(String ids) throws Exception {
-        assertThat(httpClient.jsonPath.<List<String>>read(ARGUMENTS + ".notFound")).contains(ids);
+    public void assertNotFoundListContains(String id) throws Exception {
+        MessageId messageId = messageIdsByName.get(id);
+        assertThat(httpClient.jsonPath.<List<String>>read(ARGUMENTS + ".notFound")).contains(messageId.serialize());
     }
 
     @Then("^the notFound list should contain the requested message id$")

http://git-wip-us.apache.org/repos/asf/james-project/blob/45e7f3a4/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 045893f..fc9370f 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
@@ -28,10 +28,12 @@ import java.util.stream.Collectors;
 import javax.inject.Inject;
 import javax.mail.Flags;
 
+import org.apache.james.jmap.DefaultMailboxes;
 import org.apache.james.jmap.model.Keywords;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.james.modules.MailboxProbeImpl;
 
 import com.google.common.collect.ImmutableList;
@@ -176,6 +178,49 @@ public class SetMessagesMethodStepdefs {
         userStepdefs.execWithUser(username, () -> setFlags(keywords, message));
     }
 
+    @When("^\"([^\"]*)\" destroys message \"([^\"]*)\"")
+    public void destroyMessage(String username, String message) throws Throwable {
+        MessageId messageId = getMessagesMethodStepdefs.getMessageId(message);
+        userStepdefs.execWithUser(username, () -> {
+            httpClient.post("[" +
+                "  [" +
+                "    \"setMessages\","+
+                "    {" +
+                "      \"destroy\": [ \"" + messageId.serialize() + "\" ]" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]");
+            mainStepdefs.awaitMethod.run();
+        });
+    }
+
+
+    @Given("^\"([^\"]*)\" creates a draft message \"([^\"]*)\"")
+    public void createDraft(String username, String message) throws Throwable {
+        userStepdefs.execWithUser(username, () -> {
+            Mailbox mailbox = mainStepdefs.mailboxProbe.getMailbox(MailboxConstants.USER_NAMESPACE,
+                username,
+                DefaultMailboxes.DRAFTS);
+            httpClient.post("[" +
+                "  [" +
+                "    \"setMessages\","+
+                "    {" +
+                "      \"create\": { \"" + message  + "\" : {" +
+                "        \"subject\": \"subject\"," +
+                "        \"keywords\": {\"$Draft\": true}," +
+                "        \"mailboxIds\": [\"" + mailbox.getMailboxId().serialize() + "\"]" +
+                "      }}" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]");
+            mainStepdefs.awaitMethod.run();
+            String messageId = httpClient.jsonPath.read("[0][1].created." + message + ".id");
+            getMessagesMethodStepdefs.addMessageId(message, mainStepdefs.messageIdFactory.fromString(messageId));
+        });
+    }
+
     @When("^the user sets flags \"([^\"]*)\" on message \"([^\"]*)\"")
     public void setFlags(List<String> keywords, String message) throws Throwable {
         MessageId messageId = getMessagesMethodStepdefs.getMessageId(message);

http://git-wip-us.apache.org/repos/asf/james-project/blob/45e7f3a4/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 60abace..2294513 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
@@ -100,4 +100,26 @@ Feature: SetMessages method on shared folders
     Given "bob@domain.tld" shares his mailbox "shared" with "alice@domain.tld" with "latires" rights
     And "bob@domain.tld" sets flags "$Flagged" on message "mBob"
     When "alice@domain.tld" sets flags "" on message "mBob"
-    Then message "mBob" is not updated
\ No newline at end of file
+    Then message "mBob" is not updated
+
+# Updating draft
+
+  Scenario: A user can update the flags on a draft
+    Given "bob@domain.tld" has a mailbox "Drafts"
+    And "bob@domain.tld" creates a draft message "mDraft"
+    When "bob@domain.tld" sets flags "$Draft,$Seen" on message "mDraft"
+    Then "bob@domain.tld" should see message "mDraft" with keywords $Draft,$Seen
+
+  Scenario: A user can not remove a draft flag on a draft messages
+    Given "bob@domain.tld" has a mailbox "Drafts"
+    And "bob@domain.tld" creates a draft message "mDraft"
+    When "bob@domain.tld" sets flags "$Seen" on message "mDraft"
+    Then message "mDraft" is not updated
+    And "bob@domain.tld" should see message "mDraft" with keywords $Draft
+
+  Scenario: A user can destroy a draft
+    Given "bob@domain.tld" has a mailbox "Drafts"
+    And "bob@domain.tld" creates a draft message "mDraft"
+    When "bob@domain.tld" destroys message "mDraft"
+    Then "bob@domain.tld" ask for message "mDraft"
+    And the notFound list should contain "mDraft"
\ 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


[02/18] james-project git commit: JAMES-2218 Extract messageId handling in cucumber tests

Posted by ad...@apache.org.
JAMES-2218 Extract messageId handling in cucumber tests


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

Branch: refs/heads/master
Commit: e505089d9aea4ce4d25d2898f9347d6939e2180d
Parents: 4ad9a0b
Author: benwa <bt...@linagora.com>
Authored: Thu Nov 16 13:09:37 2017 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:28:58 2017 +0100

----------------------------------------------------------------------
 .../cucumber/GetMessageListMethodStepdefs.java  |  8 ++--
 .../cucumber/GetMessagesMethodStepdefs.java     | 41 +++++++---------
 .../integration/cucumber/MessageIdStepdefs.java | 49 ++++++++++++++++++++
 .../cucumber/SetMessagesMethodStepdefs.java     | 24 +++++-----
 4 files changed, 81 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/e505089d/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessageListMethodStepdefs.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessageListMethodStepdefs.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessageListMethodStepdefs.java
index b5cbf9b..d4de2ec 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessageListMethodStepdefs.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessageListMethodStepdefs.java
@@ -43,13 +43,13 @@ public class GetMessageListMethodStepdefs {
     private static final String ARGUMENTS = "[0][1]";
     private final MainStepdefs mainStepdefs;
     private final HttpClient httpClient;
-    private final GetMessagesMethodStepdefs messagesMethodStepdefs;
+    private final MessageIdStepdefs messageIdStepdefs;
 
     @Inject
-    private GetMessageListMethodStepdefs(MainStepdefs mainStepdefs, HttpClient httpClient, GetMessagesMethodStepdefs messagesMethodStepdefs) {
+    private GetMessageListMethodStepdefs(MainStepdefs mainStepdefs, HttpClient httpClient, MessageIdStepdefs messageIdStepdefs) {
         this.mainStepdefs = mainStepdefs;
         this.httpClient = httpClient;
-        this.messagesMethodStepdefs = messagesMethodStepdefs;
+        this.messageIdStepdefs = messageIdStepdefs;
     }
 
     @When("^\"([^\"]*)\" asks for message list in mailboxes \"([^\"]*)\" with flag \"([^\"]*)\"$")
@@ -111,7 +111,7 @@ public class GetMessageListMethodStepdefs {
 
     @Then("^the message list contains \"([^\"]*)\"")
     public void assertContains(String messsage) throws Exception {
-        MessageId messageId = messagesMethodStepdefs.getMessageId(messsage);
+        MessageId messageId = messageIdStepdefs.getMessageId(messsage);
         assertThat(httpClient.response.getStatusLine().getStatusCode()).isEqualTo(200);
         assertThat(httpClient.jsonPath.<List<String>>read(ARGUMENTS + ".messageIds")).contains(messageId.serialize());
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/e505089d/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
index 96df760..cf9d51a 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/GetMessagesMethodStepdefs.java
@@ -24,7 +24,6 @@ import static org.assertj.core.api.Assertions.assertThat;
 import java.io.ByteArrayInputStream;
 import java.time.ZonedDateTime;
 import java.util.Date;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -77,25 +76,17 @@ public class GetMessagesMethodStepdefs {
     private final MainStepdefs mainStepdefs;
     private final UserStepdefs userStepdefs;
     private final HttpClient httpClient;
-    private final Map<String, MessageId> messageIdsByName;
+    private final MessageIdStepdefs messageIdStepdefs;
 
     private List<MessageId> requestedMessageIds;
 
     @Inject
     private GetMessagesMethodStepdefs(MainStepdefs mainStepdefs, UserStepdefs userStepdefs,
-                                      HttpClient httpClient) {
+                                      HttpClient httpClient, MessageIdStepdefs messageIdStepdefs) {
         this.mainStepdefs = mainStepdefs;
         this.userStepdefs = userStepdefs;
         this.httpClient = httpClient;
-        this.messageIdsByName = new HashMap<>();
-    }
-
-    public MessageId getMessageId(String name) {
-        return messageIdsByName.get(name);
-    }
-
-    public MessageId addMessageId(String name, MessageId messageId) {
-        return messageIdsByName.put(name, messageId);
+        this.messageIdStepdefs = messageIdStepdefs;
     }
 
     @Given("^the user has a message \"([^\"]*)\" in \"([^\"]*)\" and \"([^\"]*)\" mailboxes with subject \"([^\"]*)\", content \"([^\"]*)\"$")
@@ -105,7 +96,7 @@ public class GetMessagesMethodStepdefs {
         MailboxId mailboxId2 = mainStepdefs.mailboxProbe.getMailbox(MailboxConstants.USER_NAMESPACE, userStepdefs.getConnectedUser(), mailbox2).getMailboxId();
 
         mainStepdefs.jmapServer.getProbe(JmapGuiceProbe.class).setInMailboxes(id, userStepdefs.getConnectedUser(), mailboxId1, mailboxId2);
-        messageIdsByName.put(messageName, id);
+        messageIdStepdefs.addMessageId(messageName, id);
         mainStepdefs.awaitMethod.run();
     }
 
@@ -117,7 +108,7 @@ public class GetMessagesMethodStepdefs {
     @Given("^the user has a message \"([^\"]*)\" in \"([^\"]*)\" mailbox with subject \"([^\"]*)\", content \"([^\"]*)\"$")
     public void appendMessage(String messageName, String mailbox, String subject, String content) throws Exception {
         MessageId id = appendMessage(mailbox, ContentType.noContentType(), subject, content, NO_HEADERS);
-        messageIdsByName.put(messageName, id);
+        messageIdStepdefs.addMessageId(messageName, id);
     }
 
     @Given("^\"([^\"]*)\" has a message \"([^\"]*)\" in \"([^\"]*)\" mailbox with subject \"([^\"]*)\", content \"([^\"]*)\"$")
@@ -133,7 +124,7 @@ public class GetMessagesMethodStepdefs {
     @Given("^the user has a message \"([^\"]*)\" in \"([^\"]*)\" mailbox with content-type \"([^\"]*)\" subject \"([^\"]*)\", content \"([^\"]*)\"$")
     public void appendMessageWithContentType(String messageName, String mailbox, String contentType, String subject, String content) throws Throwable {
         MessageId id = appendMessage(mailbox, ContentType.from(contentType), subject, content, NO_HEADERS);
-        messageIdsByName.put(messageName, id);
+        messageIdStepdefs.addMessageId(messageName, id);
     }
 
     @Given("^\"([^\"]*)\" has a message \"([^\"]*)\" in \"([^\"]*)\" mailbox with content-type \"([^\"]*)\" subject \"([^\"]*)\", content \"([^\"]*)\", headers$")
@@ -144,13 +135,13 @@ public class GetMessagesMethodStepdefs {
     @Given("^the user has a message \"([^\"]*)\" in \"([^\"]*)\" mailbox with content-type \"([^\"]*)\" subject \"([^\"]*)\", content \"([^\"]*)\", headers$")
     public void appendMessage(String messageName, String mailbox, String contentType, String subject, String content, DataTable headers) throws Exception {
         MessageId id = appendMessage(mailbox, ContentType.from(contentType), subject, content, Optional.of(headers.asMap(String.class, String.class)));
-        messageIdsByName.put(messageName, id);
+        messageIdStepdefs.addMessageId(messageName, id);
     }
 
     @Given("^the user has a message \"([^\"]*)\" in \"([^\"]*)\" mailbox with subject \"([^\"]*)\", content \"([^\"]*)\", headers$")
     public void appendMessageWithHeader(String messageName, String mailbox, String subject, String content, DataTable headers) throws Exception {
         MessageId id = appendMessage(mailbox, ContentType.noContentType(), subject, content, Optional.of(headers.asMap(String.class, String.class)));
-        messageIdsByName.put(messageName, id);
+        messageIdStepdefs.addMessageId(messageName, id);
     }
 
     @Given("^\"([^\"]*)\" has a message \"([^\"]*)\" in \"([^\"]*)\" mailbox with subject \"([^\"]*)\", content \"([^\"]*)\", headers$")
@@ -171,7 +162,7 @@ public class GetMessagesMethodStepdefs {
             ClassLoader.getSystemResourceAsStream("eml/inlinedMultipart.eml"),
             Date.from(dateTime.toInstant()), false, new Flags())
             .getMessageId();
-        messageIdsByName.put(messageName, id);
+        messageIdStepdefs.addMessageId(messageName, id);
         mainStepdefs.awaitMethod.run();
     }
 
@@ -379,7 +370,7 @@ public class GetMessagesMethodStepdefs {
             new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes()),
             Date.from(dateTime.toInstant()), isRecent, flags)
             .getMessageId();
-        messageIdsByName.put(messageName, id);
+        messageIdStepdefs.addMessageId(messageName, id);
     }
 
     private void appendMessage(String messageName, String mailbox, String emlFileName) throws Exception {
@@ -391,7 +382,7 @@ public class GetMessagesMethodStepdefs {
                 Date.from(dateTime.toInstant()), false, new Flags())
                     .getMessageId();
 
-        messageIdsByName.put(messageName, id);
+        messageIdStepdefs.addMessageId(messageName, id);
     }
 
     @When("^\"([^\"]*)\" ask for messages using its accountId$")
@@ -437,7 +428,7 @@ public class GetMessagesMethodStepdefs {
     @When("^the user ask for messages \"(.*?)\"$")
     public void postWithAListOfIds(List<String> ids) throws Exception {
         requestedMessageIds = ids.stream()
-            .map(messageIdsByName::get)
+            .map(messageIdStepdefs::getMessageId)
             .collect(Guavate.toImmutableList());
         askMessages(requestedMessageIds);
     }
@@ -478,7 +469,7 @@ public class GetMessagesMethodStepdefs {
     @When("^the user is getting messages \"(.*?)\" with properties \"(.*?)\"$")
     public void postWithParameters(List<String> ids, List<String> properties) throws Exception {
         requestedMessageIds = ids.stream()
-            .map(messageIdsByName::get)
+            .map(messageIdStepdefs::getMessageId)
             .collect(Guavate.toImmutableList());
 
         String serializedIds = requestedMessageIds.stream()
@@ -531,7 +522,7 @@ public class GetMessagesMethodStepdefs {
 
     @Then("^the notFound list should contain \"([^\"]*)\"$")
     public void assertNotFoundListContains(String id) throws Exception {
-        MessageId messageId = messageIdsByName.get(id);
+        MessageId messageId = messageIdStepdefs.getMessageId(id);
         assertThat(httpClient.jsonPath.<List<String>>read(ARGUMENTS + ".notFound")).contains(messageId.serialize());
     }
 
@@ -549,7 +540,7 @@ public class GetMessagesMethodStepdefs {
 
     @Then("^the id of the message is \"([^\"]*)\"$")
     public void assertIdOfTheFirstMessage(String messageName) throws Exception {
-        MessageId id = messageIdsByName.get(messageName);
+        MessageId id = messageIdStepdefs.getMessageId(messageName);
         assertThat(httpClient.jsonPath.<String>read(FIRST_MESSAGE + ".id")).isEqualTo(id.serialize());
     }
 
@@ -588,7 +579,7 @@ public class GetMessagesMethodStepdefs {
 
     @Then("^the threadId of the message is \"([^\"]*)\"$")
     public void assertThreadIdOfTheFirstMessage(String threadId) throws Exception {
-        MessageId id = messageIdsByName.get(threadId);
+        MessageId id = messageIdStepdefs.getMessageId(threadId);
         assertThat(httpClient.jsonPath.<String>read(FIRST_MESSAGE + ".threadId")).isEqualTo(id.serialize());
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/e505089d/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/MessageIdStepdefs.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/MessageIdStepdefs.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/MessageIdStepdefs.java
new file mode 100644
index 0000000..ae971f9
--- /dev/null
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/MessageIdStepdefs.java
@@ -0,0 +1,49 @@
+/****************************************************************
+ * 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.methods.integration.cucumber;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.james.mailbox.model.MessageId;
+
+import cucumber.runtime.java.guice.ScenarioScoped;
+
+@ScenarioScoped
+public class MessageIdStepdefs {
+
+    private final Map<String, MessageId> messageIdsByName;
+
+    @Inject
+    private MessageIdStepdefs() {
+        this.messageIdsByName = new HashMap<>();
+    }
+
+    public MessageId getMessageId(String name) {
+        return messageIdsByName.get(name);
+    }
+
+    public MessageId addMessageId(String name, MessageId messageId) {
+        return messageIdsByName.put(name, messageId);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/e505089d/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 3522d3c..ff30634 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
@@ -49,14 +49,14 @@ public class SetMessagesMethodStepdefs {
     private final MainStepdefs mainStepdefs;
     private final UserStepdefs userStepdefs;
     private final HttpClient httpClient;
-    private final GetMessagesMethodStepdefs getMessagesMethodStepdefs;
+    private final MessageIdStepdefs messageIdStepdefs;
 
     @Inject
-    private SetMessagesMethodStepdefs(MainStepdefs mainStepdefs, UserStepdefs userStepdefs, HttpClient httpClient, GetMessagesMethodStepdefs getMessagesMethodStepdefs) {
+    private SetMessagesMethodStepdefs(MainStepdefs mainStepdefs, UserStepdefs userStepdefs, HttpClient httpClient, MessageIdStepdefs messageIdStepdefs) {
         this.mainStepdefs = mainStepdefs;
         this.userStepdefs = userStepdefs;
         this.httpClient = httpClient;
-        this.getMessagesMethodStepdefs = getMessagesMethodStepdefs;
+        this.messageIdStepdefs = messageIdStepdefs;
     }
 
     @When("^\"([^\"]*)\" moves \"([^\"]*)\" to user mailbox \"([^\"]*)\"")
@@ -66,7 +66,7 @@ public class SetMessagesMethodStepdefs {
 
     @When("^the user moves \"([^\"]*)\" to user mailbox \"([^\"]*)\"")
     public void moveMessageToMailbox(String message, String mailbox) throws Throwable {
-        MessageId messageId = getMessagesMethodStepdefs.getMessageId(message);
+        MessageId messageId = messageIdStepdefs.getMessageId(message);
         MailboxId mailboxId = mainStepdefs.jmapServer
             .getProbe(MailboxProbeImpl.class)
             .getMailbox(MailboxConstants.USER_NAMESPACE, userStepdefs.getConnectedUser(), mailbox)
@@ -93,7 +93,7 @@ public class SetMessagesMethodStepdefs {
 
     @When("^the user copies \"([^\"]*)\" from mailbox \"([^\"]*)\" to mailbox \"([^\"]*)\"")
     public void copyMessageToMailbox(String message, String sourceMailbox, String destinationMailbox) throws Throwable {
-        MessageId messageId = getMessagesMethodStepdefs.getMessageId(message);
+        MessageId messageId = messageIdStepdefs.getMessageId(message);
         MailboxId sourceMailboxId = mainStepdefs.jmapServer
             .getProbe(MailboxProbeImpl.class)
             .getMailbox(MailboxConstants.USER_NAMESPACE, userStepdefs.getConnectedUser(), sourceMailbox)
@@ -123,7 +123,7 @@ public class SetMessagesMethodStepdefs {
     }
 
     private void copyMessageToMailbox(String message, String sourceMailbox, String sourceUser, String destinationMailbox, String destinationUser) throws Throwable {
-        MessageId messageId = getMessagesMethodStepdefs.getMessageId(message);
+        MessageId messageId = messageIdStepdefs.getMessageId(message);
         MailboxId sourceMailboxId = mainStepdefs.jmapServer
             .getProbe(MailboxProbeImpl.class)
             .getMailbox(MailboxConstants.USER_NAMESPACE, sourceUser, sourceMailbox)
@@ -153,7 +153,7 @@ public class SetMessagesMethodStepdefs {
     }
 
     private void moveMessageToMailbox(String message, String destinationMailbox, String destinationUser) throws Throwable {
-        MessageId messageId = getMessagesMethodStepdefs.getMessageId(message);
+        MessageId messageId = messageIdStepdefs.getMessageId(message);
         MailboxId destinationMailboxId = mainStepdefs.jmapServer
             .getProbe(MailboxProbeImpl.class)
             .getMailbox(MailboxConstants.USER_NAMESPACE, destinationUser, destinationMailbox)
@@ -180,7 +180,7 @@ public class SetMessagesMethodStepdefs {
 
     @When("^\"([^\"]*)\" destroys message \"([^\"]*)\"")
     public void destroyMessage(String username, String message) throws Throwable {
-        MessageId messageId = getMessagesMethodStepdefs.getMessageId(message);
+        MessageId messageId = messageIdStepdefs.getMessageId(message);
         userStepdefs.execWithUser(username, () -> {
             httpClient.post("[" +
                 "  [" +
@@ -218,13 +218,13 @@ public class SetMessagesMethodStepdefs {
             Optional.ofNullable(
                 httpClient.jsonPath.<String>read("[0][1].created." + message + ".id"))
                 .map(mainStepdefs.messageIdFactory::fromString)
-                .ifPresent(id -> getMessagesMethodStepdefs.addMessageId(message, id));
+                .ifPresent(id -> messageIdStepdefs.addMessageId(message, id));
         });
     }
 
     @When("^the user sets flags \"([^\"]*)\" on message \"([^\"]*)\"")
     public void setFlags(List<String> keywords, String message) throws Throwable {
-        MessageId messageId = getMessagesMethodStepdefs.getMessageId(message);
+        MessageId messageId = messageIdStepdefs.getMessageId(message);
         String keywordString = keywords
             .stream()
             .map(value -> "\"" + value + "\" : true")
@@ -248,7 +248,7 @@ public class SetMessagesMethodStepdefs {
     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();
-        MessageId messageId = getMessagesMethodStepdefs.getMessageId(message);
+        MessageId messageId = messageIdStepdefs.getMessageId(message);
         MailboxId mailboxId = mainStepdefs.jmapServer
             .getProbe(MailboxProbeImpl.class)
             .getMailbox(MailboxConstants.USER_NAMESPACE, mailboxOwner, mailbox)
@@ -260,7 +260,7 @@ public class SetMessagesMethodStepdefs {
 
     @Then("^message \"([^\"]*)\" is not updated$")
     public void assertIdOfTheFirstMessage(String messageName) throws Exception {
-        MessageId id = getMessagesMethodStepdefs.getMessageId(messageName);
+        MessageId id = messageIdStepdefs.getMessageId(messageName);
         assertThat(httpClient.jsonPath.<Map<String, String>>read("[0][1].notUpdated"))
             .containsOnlyKeys(id.serialize());
     }


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


[17/18] james-project git commit: JAMES-2220 allow passing Collection instead of List to MessageIdManager.setInMailboxes

Posted by ad...@apache.org.
JAMES-2220 allow passing Collection instead of List to MessageIdManager.setInMailboxes


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

Branch: refs/heads/master
Commit: 0349ca6aab26325dc58ca5b2c97d35c2fc74e1be
Parents: 3170793
Author: Matthieu Baechler <ma...@apache.org>
Authored: Wed Nov 15 16:06:43 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:31 2017 +0100

----------------------------------------------------------------------
 .../src/main/java/org/apache/james/mailbox/MessageIdManager.java   | 2 +-
 .../java/org/apache/james/mailbox/store/StoreMessageIdManager.java | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/0349ca6a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageIdManager.java
----------------------------------------------------------------------
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageIdManager.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageIdManager.java
index 309145e..4eaa41b 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/MessageIdManager.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MessageIdManager.java
@@ -42,5 +42,5 @@ public interface MessageIdManager {
 
     void delete(MessageId messageId, List<MailboxId> mailboxIds, MailboxSession mailboxSession) throws MailboxException;
 
-    void setInMailboxes(MessageId messageId, List<MailboxId> mailboxIds, MailboxSession mailboxSession) throws MailboxException;
+    void setInMailboxes(MessageId messageId, Collection<MailboxId> mailboxIds, MailboxSession mailboxSession) throws MailboxException;
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/0349ca6a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
index dafd1a6..60c9f3f 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageIdManager.java
@@ -190,7 +190,7 @@ public class StoreMessageIdManager implements MessageIdManager {
     }
 
     @Override
-    public void setInMailboxes(MessageId messageId, List<MailboxId> targetMailboxIds, MailboxSession mailboxSession) throws MailboxException {
+    public void setInMailboxes(MessageId messageId, Collection<MailboxId> targetMailboxIds, MailboxSession mailboxSession) throws MailboxException {
         assertRightsOnMailboxes(targetMailboxIds, mailboxSession, Right.Read);
 
         List<MailboxMessage> currentMailboxMessages = findRelatedMailboxMessages(messageId, mailboxSession);


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


[13/18] james-project git commit: JAMES-2220 rework MailImpl mailid generation to make it pass tests

Posted by ad...@apache.org.
JAMES-2220 rework MailImpl mailid generation to make it pass tests


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

Branch: refs/heads/master
Commit: 31707935a2318792b609604cd9e6b40311be40ef
Parents: c80bc05
Author: Matthieu Baechler <ma...@apache.org>
Authored: Tue Nov 14 17:26:04 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:31 2017 +0100

----------------------------------------------------------------------
 .../org/apache/james/server/core/MailImpl.java  | 52 +++++++++++---------
 .../apache/james/server/core/MailImplTest.java  |  3 --
 2 files changed, 29 insertions(+), 26 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/31707935/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index 4ba435c..c5c7f9b 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -39,6 +39,7 @@ import javax.mail.MessagingException;
 import javax.mail.internet.MimeMessage;
 import javax.mail.internet.ParseException;
 
+import org.apache.commons.lang.RandomStringUtils;
 import org.apache.james.core.MailAddress;
 import org.apache.james.lifecycle.api.Disposable;
 import org.apache.james.lifecycle.api.LifecycleUtil;
@@ -51,6 +52,7 @@ import org.slf4j.LoggerFactory;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.primitives.Chars;
 
 /**
  * <p>
@@ -82,41 +84,45 @@ public class MailImpl implements Disposable, Mail {
         return new MailImpl(mail, deriveNewName(mail.getName()));
     }
 
-    private static final java.util.Random random = new java.util.Random(); // Used
-    // to
-    // generate
-    // new
-    // mail
-    // names
-
     /**
      * Create a unique new primary key name for the given MailObject.
+     * Detect if this has been called more than 8 times recursively
      *
      * @param currentName the mail to use as the basis for the new mail name
      * @return a new name
      */
     @VisibleForTesting static String deriveNewName(String currentName) throws MessagingException {
+        char separator = '!';
+        int loopThreshold = 7;
+        int suffixLength = 9;
+        int suffixMaxLength = loopThreshold * suffixLength;
+        int nameMaxLength = suffixMaxLength + 13;
+
+        detectPossibleLoop(currentName, loopThreshold, separator);
 
         // Checking if the original mail name is too long, perhaps because of a
         // loop caused by a configuration error.
         // it could cause a "null pointer exception" in AvalonMailRepository
-        // much
-        // harder to understand.
-        if (currentName.length() > 76) {
-            int count = 0;
-            int index = 0;
-            while ((index = currentName.indexOf('!', index + 1)) >= 0) {
-                count++;
-            }
-            // It looks like a configuration loop. It's better to stop.
-            if (count > 7) {
-                throw new MessagingException("Unable to create a new message name: too long." + " Possible loop in config.xml.");
-            } else {
-                currentName = currentName.substring(0, 76);
-            }
-        }
+        // much harder to understand.
+        String newName = currentName + generateRandomSuffix(suffixLength, separator);
+        return stripFirstCharsIfNeeded(nameMaxLength, newName);
+    }
 
-        return currentName + "-!" + random.nextInt(1048576);
+    private static String stripFirstCharsIfNeeded(int nameMaxLength, String newName) {
+        return newName.substring(Math.max(0, newName.length() - nameMaxLength));
+    }
+
+    private static String generateRandomSuffix(int suffixLength, char separator) {
+        return "-" + separator + RandomStringUtils.randomNumeric(suffixLength - 2);
+    }
+
+    private static void detectPossibleLoop(String currentName, int loopThreshold, char separator) throws MessagingException {
+        long occurrences = currentName.chars().filter(c -> Chars.saturatedCast(c) == separator).count();
+
+        // It looks like a configuration loop. It's better to stop.
+        if (occurrences > loopThreshold) {
+            throw new MessagingException("Unable to create a new message name: too long. Possible loop in config.xml.");
+        }
     }
 
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/31707935/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java b/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
index e90e4d0..49ce62a 100644
--- a/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
+++ b/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
@@ -191,7 +191,6 @@ public class MailImplTest {
     }
 
     @Test
-    @Ignore("short value doesn't trigger the assertion")
     public void deriveNewNameShouldThrowWhenMoreThan8NestedCallsAndSmallInitialValue() throws MessagingException {
         String called6Times = IntStream.range(0, 8)
             .mapToObj(String::valueOf)
@@ -200,7 +199,6 @@ public class MailImplTest {
     }
 
     @Test
-    @Ignore("truncation make impossible to detect 8 calls")
     public void deriveNewNameShouldThrowWhenMoreThan8NestedCallsAndLongInitialValue() throws MessagingException {
         String called6Times = IntStream.range(0, 8)
             .mapToObj(String::valueOf)
@@ -208,7 +206,6 @@ public class MailImplTest {
         assertThatThrownBy(() -> MailImpl.deriveNewName(called6Times)).isInstanceOf(MessagingException.class);
     }
 
-
     @Test
     public void deriveNewNameShouldGenerateNotEqualsCurrentName() throws MessagingException {
         assertThat(MailImpl.deriveNewName("current")).isNotEqualTo("current");


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


[15/18] james-project git commit: JAMES-2220 patch should not empty keywords when no keyword property given to SetMessages

Posted by ad...@apache.org.
JAMES-2220 patch should not empty keywords when no keyword property given to SetMessages


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

Branch: refs/heads/master
Commit: 85ad874381d2f4da9cf12e244c717ba8cc73f963
Parents: 0349ca6
Author: Matthieu Baechler <ma...@apache.org>
Authored: Wed Nov 15 17:15:42 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:31 2017 +0100

----------------------------------------------------------------------
 .../integration/SetMessagesMethodTest.java      | 26 ++++++++++++++++++++
 .../methods/SetMessagesUpdateProcessor.java     |  2 +-
 .../james/jmap/model/UpdateMessagePatch.java    |  9 ++-----
 .../jmap/model/UpdateMessagePatchTest.java      | 10 ++++++++
 4 files changed, 39 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/85ad8743/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
index 054b589..c29302b 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
@@ -564,6 +564,31 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
+    @Ignore("gitlab-446 should allowed in drafts mailbox, rejected outside")
+    public void setMessagesShouldReturnAnErrorWhenKeywordsWithAddingDraftArePassed() throws MailboxException {
+        mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
+
+        ComposedMessageId message = mailboxProbe.appendMessage(USERNAME, USER_MAILBOX,
+                new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags(Flags.Flag.ANSWERED));
+        await();
+
+        String messageId = message.getMessageId().serialize();
+
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"keywords\": {\"$Draft\": true} } } }, \"#0\"]]", messageId))
+        .when()
+            .post("/jmap")
+        .then()
+            .log().ifValidationFails()
+            .body(NOT_UPDATED, hasKey(messageId))
+            .body(NOT_UPDATED + "[\""+messageId+"\"].type", equalTo("invalidProperties"))
+            .body(NOT_UPDATED + "[\""+messageId+"\"].properties[0]", equalTo("keywords"))
+            .body(NOT_UPDATED + "[\""+messageId+"\"].description", equalTo("keywords: Cannot add or remove draft flag"))
+            .body(ARGUMENTS + ".updated", hasSize(0));
+    }
+
+    @Test
     public void setMessagesShouldReturnAnErrorWhenKeywordsWithDeletedArePassed() throws MailboxException {
         mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
 
@@ -675,6 +700,7 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
+    @Ignore("gitlab-446 should allowed outside drafts mailbox, rejected inside")
     public void setMessagesShouldReturnAnErrorWhenKeywordsWithRemoveDraftArePassed() throws MailboxException {
         mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/85ad8743/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 2497cea..c54deec 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
@@ -185,7 +185,7 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
     private List<Flags> patchFlags(List<MessageResult> messagesToBeUpdated, UpdateMessagePatch updateMessagePatch) {
         return messagesToBeUpdated.stream()
             .map(MessageResult::getFlags)
-            .map(updateMessagePatch::applyToStateNoReset)
+            .map(updateMessagePatch::applyToState)
             .collect(Guavate.toImmutableList());
     }
 

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

http://git-wip-us.apache.org/repos/asf/james-project/blob/85ad8743/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 00aeee3..c556ce6 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
@@ -257,4 +257,14 @@ public class UpdateMessagePatchTest {
 
         messagePatch.isFlagsIdentity();
     }
+
+    @Test
+    public void applyStateShouldKeepKeywordsWhenNoKeywordPatchDefined() {
+        UpdateMessagePatch messagePatch = UpdateMessagePatch.builder()
+            .build();
+        Flags flags = FlagsBuilder.builder()
+            .add(Flags.Flag.DELETED, Flags.Flag.RECENT, Flags.Flag.DRAFT)
+            .build();
+        assertThat(messagePatch.applyToState(flags)).isEqualTo(flags);
+    }
 }
\ 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


[11/18] james-project git commit: JAMES-2220 be consistent when setting name in MailImpl

Posted by ad...@apache.org.
JAMES-2220 be consistent when setting name in MailImpl


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

Branch: refs/heads/master
Commit: fe86766a41b29af28fbe4cfdfa506e9e0ea964ce
Parents: 2133702
Author: Matthieu Baechler <ma...@apache.org>
Authored: Tue Nov 14 15:48:46 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:31 2017 +0100

----------------------------------------------------------------------
 .../core/src/main/java/org/apache/james/server/core/MailImpl.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/fe86766a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index a9e3df0..86f3919 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -156,7 +156,7 @@ public class MailImpl implements Disposable, Mail {
      */
     public MailImpl(String name, MailAddress sender, Collection<MailAddress> recipients) {
         this();
-        this.name = name;
+        setName(name);
         this.sender = sender;
 
         // Copy the recipient list


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


[18/18] james-project git commit: Merge remote-tracking branch 'mine/JAMES-2218-2220'

Posted by ad...@apache.org.
Merge remote-tracking branch 'mine/JAMES-2218-2220'


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

Branch: refs/heads/master
Commit: fb73fe9e1258ab46f398987536fc09311e46341f
Parents: 51cc52a 2c9d093
Author: Antoine Duprat <ad...@linagora.com>
Authored: Thu Nov 16 15:19:13 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 15:19:13 2017 +0100

----------------------------------------------------------------------
 .../apache/james/mailbox/MessageIdManager.java  |   2 +-
 .../mailbox/store/StoreMessageIdManager.java    |   2 +-
 .../org/apache/james/server/core/MailImpl.java  | 253 +++++++------------
 .../apache/james/server/core/MailImplTest.java  | 189 ++++++++++----
 .../server/core/MailTestAllImplementations.java |  72 ------
 .../core/MimeMessageCopyOnWriteProxyTest.java   |  10 +-
 .../modules/server/MemoryMailQueueFactory.java  |   2 +-
 .../impl/camel/MatcherSplitter.java             |   2 +-
 .../mailets/AbstractRecipientRewriteTable.java  |   2 +-
 .../james/transport/mailets/DSNBounce.java      |   2 +-
 .../mailets/redirect/ProcessRedirectNotify.java |   2 +-
 .../integration/SetMessagesMethodTest.java      | 212 ++++++++++++----
 .../cucumber/GetMessageListMethodStepdefs.java  |   8 +-
 .../cucumber/GetMessagesMethodStepdefs.java     |  42 ++-
 .../integration/cucumber/MessageIdStepdefs.java |  49 ++++
 .../cucumber/SetMessagesMethodStepdefs.java     | 135 ++++++++--
 .../test/resources/cucumber/SetMessages.feature |  60 +++++
 .../DraftMessageMailboxUpdateException.java     |  29 +++
 .../InvalidDraftKeywordsException.java          |   4 +-
 .../exceptions/InvalidOutboxMoveException.java  |  23 ++
 .../james/jmap/methods/MessageSender.java       |  19 +-
 .../methods/SetMessagesCreationProcessor.java   |  20 +-
 .../methods/SetMessagesUpdateProcessor.java     | 175 ++++++++++++-
 .../james/jmap/model/UpdateMessagePatch.java    |  10 +-
 .../org/apache/james/jmap/send/MailFactory.java |   2 +
 .../methods/SetMessagesUpdateProcessorTest.java |  26 +-
 .../jmap/model/UpdateMessagePatchTest.java      |  10 +
 27 files changed, 936 insertions(+), 426 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/fb73fe9e/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
----------------------------------------------------------------------


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


[14/18] james-project git commit: JAMES-2220 Already covered by cucumber tests

Posted by ad...@apache.org.
JAMES-2220 Already covered by cucumber tests


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

Branch: refs/heads/master
Commit: 2c9d0936ed34c6e8967c8977a07e1346ded9d2bd
Parents: 29d233c
Author: benwa <bt...@linagora.com>
Authored: Thu Nov 16 17:04:30 2017 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:31 2017 +0100

----------------------------------------------------------------------
 .../integration/SetMessagesMethodTest.java      | 116 +------------------
 1 file changed, 3 insertions(+), 113 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/2c9d0936/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
index 924c4a2..88767b6 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
@@ -569,31 +569,6 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
-    @Ignore("JAMES-2220 should allowed in drafts mailbox, rejected outside")
-    public void setMessagesShouldReturnAnErrorWhenKeywordsWithAddingDraftArePassed() throws MailboxException {
-        mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
-
-        ComposedMessageId message = mailboxProbe.appendMessage(USERNAME, USER_MAILBOX,
-                new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags(Flags.Flag.ANSWERED));
-        await();
-
-        String messageId = message.getMessageId().serialize();
-
-        given()
-            .header("Authorization", accessToken.serialize())
-            .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"keywords\": {\"$Draft\": true} } } }, \"#0\"]]", messageId))
-        .when()
-            .post("/jmap")
-        .then()
-            .log().ifValidationFails()
-            .body(NOT_UPDATED, hasKey(messageId))
-            .body(NOT_UPDATED + "[\""+messageId+"\"].type", equalTo("invalidProperties"))
-            .body(NOT_UPDATED + "[\""+messageId+"\"].properties[0]", equalTo("keywords"))
-            .body(NOT_UPDATED + "[\""+messageId+"\"].description", equalTo("keywords: Cannot add or remove draft flag"))
-            .body(ARGUMENTS + ".updated", hasSize(0));
-    }
-
-    @Test
     public void setMessagesShouldReturnAnErrorWhenKeywordsWithDeletedArePassed() throws MailboxException {
         mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
 
@@ -705,31 +680,6 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
-    @Ignore("JAMES-2220 should allowed outside drafts mailbox, rejected inside")
-    public void setMessagesShouldReturnAnErrorWhenKeywordsWithRemoveDraftArePassed() throws MailboxException {
-        mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
-
-        ComposedMessageId message = mailboxProbe.appendMessage(USERNAME, USER_MAILBOX,
-                new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags(Flags.Flag.DRAFT));
-        await();
-
-        String messageId = message.getMessageId().serialize();
-
-        given()
-            .header("Authorization", accessToken.serialize())
-            .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"keywords\": {} } } }, \"#0\"]]", messageId))
-        .when()
-            .post("/jmap")
-        .then()
-            .log().ifValidationFails()
-            .body(NOT_UPDATED, hasKey(messageId))
-            .body(NOT_UPDATED + "[\""+messageId+"\"].type", equalTo("invalidProperties"))
-            .body(NOT_UPDATED + "[\""+messageId+"\"].properties[0]", equalTo("keywords"))
-            .body(NOT_UPDATED + "[\""+messageId+"\"].description", equalTo("keywords: Cannot add or remove draft flag"))
-            .body(ARGUMENTS + ".updated", hasSize(0));
-    }
-
-    @Test
     public void setMessagesShouldReturnNewKeywordsWhenKeywordsArePassedToRemoveAndAddFlag() throws MailboxException {
         mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
 
@@ -744,7 +694,7 @@ public abstract class SetMessagesMethodTest {
 
         given()
             .header("Authorization", accessToken.serialize())
-            .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"keywords\": {\"$Draft\": true, \"$Flagged\": true} } } }, \"#0\"]]", messageId))
+            .body(String.format("[[\"setMessages\", {\"update\": {\"%s\" : { \"keywords\": {\"$Answered\": true, \"$Flagged\": true} } } }, \"#0\"]]", messageId))
         .when()
             .post("/jmap")
         .then()
@@ -760,7 +710,7 @@ public abstract class SetMessagesMethodTest {
             .statusCode(200)
             .body(NAME, equalTo("messages"))
             .body(ARGUMENTS + ".list", hasSize(1))
-            .body(ARGUMENTS + ".list[0].keywords.$Draft", equalTo(true))
+            .body(ARGUMENTS + ".list[0].keywords.$Answered", equalTo(true))
             .body(ARGUMENTS + ".list[0].keywords.$Flagged", equalTo(true));
     }
 
@@ -1949,7 +1899,6 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
-    @Ignore("WIP")
     public void setMessagesShouldRejectDraftCopyToOutbox() {
         String draftCreationId = "creationId1337";
         String fromAddress = USERNAME;
@@ -1983,7 +1932,7 @@ public abstract class SetMessagesMethodTest {
             "    \"setMessages\","+
             "    {" +
             "      \"update\": { \"" + draftId + "\" : {" +
-            "        \"keywords\": {}," +
+            "        \"keywords\": {\"$Draft\":true}," +
             "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\",\"" + getDraftId(accessToken) + "\"]" +
             "      }}" +
             "    }," +
@@ -2043,65 +1992,6 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
-    public void setMessagesShouldRejectMovingMessageToOutboxWhenDraftKeyworkSet() throws MailboxException {
-        String draftCreationId = "creationId1337";
-        String fromAddress = USERNAME;
-        String createDraft = "[" +
-            "  [" +
-            "    \"setMessages\","+
-            "    {" +
-            "      \"create\": { \"" + draftCreationId  + "\" : {" +
-            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
-            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"" + BOB + "\"}]," +
-            "        \"subject\": \"subject\"," +
-            "        \"keywords\": {\"$Draft\": true}," +
-            "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
-            "      }}" +
-            "    }," +
-            "    \"#0\"" +
-            "  ]" +
-            "]";
-
-        String draftId =
-            with()
-                .header("Authorization", accessToken.serialize())
-                .body(createDraft)
-                .post("/jmap")
-                .then()
-                .extract()
-                .path(ARGUMENTS + ".created[\"" + draftCreationId + "\"].id");
-
-        String moveDraftToOutBox = "[" +
-            "  [" +
-            "    \"setMessages\","+
-            "    {" +
-            "      \"update\": { \"" + draftId + "\" : {" +
-            "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
-            "      }}" +
-            "    }," +
-            "    \"#0\"" +
-            "  ]" +
-            "]";
-
-
-        given()
-            .header("Authorization", accessToken.serialize())
-            .body(moveDraftToOutBox)
-        .when()
-            .post("/jmap")
-        .then()
-            .statusCode(200)
-            .body(NAME, equalTo("messagesSet"))
-            .body(ARGUMENTS + ".notUpdated", hasKey(draftId))
-            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].type", equalTo("invalidProperties"))
-            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].description", endsWith("message with $Draft keyword can't be moved outside outbox"))
-            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].properties", hasSize(1))
-            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].properties", contains("keywords"))
-            .body(ARGUMENTS + ".created", aMapWithSize(0));
-    }
-
-
-    @Test
     public void setMessagesShouldSupportArbitraryMessageId() {
         String messageCreationId = "1717fcd1-603e-44a5-b2a6-1234dbcd5723";
         String fromAddress = USERNAME;


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


[03/18] james-project git commit: JAMES-2218 Disable moving / copying draft message

Posted by ad...@apache.org.
JAMES-2218 Disable moving / copying draft message


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

Branch: refs/heads/master
Commit: 4ad9a0b730c8b776b1e8a2399169192a9bd24172
Parents: ad893d0
Author: benwa <bt...@linagora.com>
Authored: Wed Nov 15 16:34:42 2017 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:28:58 2017 +0100

----------------------------------------------------------------------
 .../test/resources/cucumber/SetMessages.feature | 24 ++++++++++-
 .../DraftMessageMailboxUpdateException.java     | 29 +++++++++++++
 .../methods/SetMessagesUpdateProcessor.java     | 44 ++++++++++++++++++--
 .../methods/SetMessagesUpdateProcessorTest.java | 14 ++++++-
 4 files changed, 105 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/4ad9a0b7/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 9e6dcce..ac816e4 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
@@ -127,4 +127,26 @@ Feature: SetMessages method on shared folders
   Scenario: Draft creation in outbox is not allowed
     Given "bob@domain.tld" has a mailbox "Outbox"
     When "bob@domain.tld" creates a draft message "mDraft" in mailbox "Outbox"
-    Then message "mDraft" is not created
\ No newline at end of file
+    Then message "mDraft" is not created
+
+  Scenario: A user can not move draft out of draft mailbox
+    Given "bob@domain.tld" has a mailbox "Drafts"
+    And "bob@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    When "bob@domain.tld" moves "mDraft" to user mailbox "shared"
+    Then message "mDraft" is not updated
+
+  Scenario: A user can not copy draft out of draft mailbox
+    Given "bob@domain.tld" has a mailbox "Drafts"
+    And "bob@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
+    When "bob@domain.tld" copies "mDraft" from mailbox "Drafts" to mailbox "shared"
+    Then message "mDraft" is not updated
+
+  Scenario: A user can not copy draft out of draft mailbox
+    Given "bob@domain.tld" has a mailbox "Drafts"
+    When "bob@domain.tld" moves "mBob" to user mailbox "Drafts"
+    Then message "mBob" is not updated
+
+  Scenario: A user can not copy draft out of draft mailbox
+    Given "bob@domain.tld" has a mailbox "Drafts"
+    When "bob@domain.tld" copies "mBob" from mailbox "shared" to mailbox "Drafts"
+    Then message "mBob" is not updated
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/4ad9a0b7/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
new file mode 100644
index 0000000..296dc9c
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/DraftMessageMailboxUpdateException.java
@@ -0,0 +1,29 @@
+/****************************************************************
+ * 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.exceptions;
+
+import org.apache.james.mailbox.exception.MailboxException;
+
+public class DraftMessageMailboxUpdateException extends MailboxException {
+
+    public DraftMessageMailboxUpdateException() {
+        super();
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/4ad9a0b7/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 da7dceb..f23ba55 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
@@ -30,11 +30,14 @@ import java.util.stream.Stream;
 import javax.inject.Inject;
 import javax.mail.Flags;
 
+import org.apache.james.jmap.exceptions.DraftMessageMailboxUpdateException;
 import org.apache.james.jmap.model.MessageProperties;
 import org.apache.james.jmap.model.SetError;
 import org.apache.james.jmap.model.SetMessagesRequest;
 import org.apache.james.jmap.model.SetMessagesResponse;
 import org.apache.james.jmap.model.UpdateMessagePatch;
+import org.apache.james.jmap.model.mailbox.Role;
+import org.apache.james.jmap.utils.SystemMailboxesProvider;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageIdManager;
 import org.apache.james.mailbox.MessageManager;
@@ -61,6 +64,7 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
 
     private final UpdateMessagePatchConverter updatePatchConverter;
     private final MessageIdManager messageIdManager;
+    private final SystemMailboxesProvider systemMailboxesProvider;
     private final Factory mailboxIdFactory;
     private final MetricFactory metricFactory;
 
@@ -68,9 +72,10 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
     @VisibleForTesting SetMessagesUpdateProcessor(
         UpdateMessagePatchConverter updatePatchConverter,
         MessageIdManager messageIdManager,
-        Factory mailboxIdFactory, MetricFactory metricFactory) {
+        SystemMailboxesProvider systemMailboxesProvider, Factory mailboxIdFactory, MetricFactory metricFactory) {
         this.updatePatchConverter = updatePatchConverter;
         this.messageIdManager = messageIdManager;
+        this.systemMailboxesProvider = systemMailboxesProvider;
         this.mailboxIdFactory = mailboxIdFactory;
         this.metricFactory = metricFactory;
     }
@@ -99,7 +104,9 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
             if (messages.isEmpty()) {
                 addMessageIdNotFoundToResponse(messageId, builder);
             } else {
-                setInMailboxes(messageId, updateMessagePatch, mailboxSession);
+                setInMailboxes(messageId, updateMessagePatch,
+                    messages.stream().map(MessageResult::getFlags),
+                    mailboxSession);
                 Optional<MailboxException> updateError = messages.stream()
                     .flatMap(message -> updateFlags(messageId, updateMessagePatch, mailboxSession, message))
                     .findAny();
@@ -109,6 +116,8 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
                     builder.updated(ImmutableList.of(messageId));
                 }
             }
+        } catch (DraftMessageMailboxUpdateException e) {
+            handleDraftMessageMailboxUpdateException(messageId, builder);
         } catch (MailboxException e) {
             handleMessageUpdateException(messageId, builder, e);
         } catch (IllegalArgumentException e) {
@@ -134,7 +143,7 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
         }
     }
 
-    private void setInMailboxes(MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession) throws MailboxException {
+    private void setInMailboxes(MessageId messageId, UpdateMessagePatch updateMessagePatch, Stream<Flags> originalFlags, MailboxSession mailboxSession) throws MailboxException {
         Optional<List<String>> serializedMailboxIds = updateMessagePatch.getMailboxIds();
         if (serializedMailboxIds.isPresent()) {
             List<MailboxId> mailboxIds = serializedMailboxIds.get()
@@ -142,10 +151,30 @@ 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()
@@ -155,6 +184,15 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
                         .build()));
     }
 
+    private SetMessagesResponse.Builder handleDraftMessageMailboxUpdateException(MessageId messageId,
+                                                                     SetMessagesResponse.Builder builder) {
+        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")
+            .build()));
+    }
+
     private SetMessagesResponse.Builder handleMessageUpdateException(MessageId messageId,
                                               SetMessagesResponse.Builder builder,
                                               MailboxException e) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/4ad9a0b7/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
index 424f28f..de2f809 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
@@ -28,6 +28,9 @@ import org.apache.james.jmap.model.MessageProperties;
 import org.apache.james.jmap.model.SetMessagesRequest;
 import org.apache.james.jmap.model.SetMessagesResponse;
 import org.apache.james.jmap.model.UpdateMessagePatch;
+import org.apache.james.jmap.utils.SystemMailboxesProvider;
+import org.apache.james.mailbox.MessageIdManager;
+import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.TestMessageId;
 import org.apache.james.metrics.api.NoopMetricFactory;
@@ -42,7 +45,14 @@ public class SetMessagesUpdateProcessorTest {
 
     @Test
     public void processShouldReturnEmptyUpdatedWhenRequestHasEmptyUpdate() {
-        SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(null, null, null, new NoopMetricFactory());
+        UpdateMessagePatchConverter updatePatchConverter = null;
+        MessageIdManager messageIdManager = null;
+        SystemMailboxesProvider systemMailboxesProvider = null;
+        MailboxId.Factory mailboxIdFactory = null;
+        SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(updatePatchConverter,
+            messageIdManager,
+            systemMailboxesProvider,
+            mailboxIdFactory, new NoopMetricFactory());
         SetMessagesRequest requestWithEmptyUpdate = SetMessagesRequest.builder().build();
 
         SetMessagesResponse result = sut.process(requestWithEmptyUpdate, null);
@@ -66,7 +76,7 @@ public class SetMessagesUpdateProcessorTest {
         when(mockConverter.fromJsonNode(any(ObjectNode.class)))
                 .thenReturn(mockInvalidPatch);
 
-        SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(mockConverter, null, null, new NoopMetricFactory());
+        SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(mockConverter, null, null, null, new NoopMetricFactory());
         MessageId requestMessageId = TestMessageId.of(1);
         SetMessagesRequest requestWithInvalidUpdate = SetMessagesRequest.builder()
                 .update(ImmutableMap.of(requestMessageId, JsonNodeFactory.instance.objectNode()))


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


[10/18] james-project git commit: JAMES-2220 Implement sending draft with move to Outbox

Posted by ad...@apache.org.
JAMES-2220 Implement sending draft with move to Outbox


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

Branch: refs/heads/master
Commit: 29d233cc497e6e7a6f62f38285e751940fae5e48
Parents: 85ad874
Author: Matthieu Baechler <ma...@apache.org>
Authored: Tue Nov 14 21:40:30 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:31 2017 +0100

----------------------------------------------------------------------
 .../org/apache/james/server/core/MailImpl.java  |  29 +++
 .../integration/SetMessagesMethodTest.java      | 214 ++++++++++++++++++-
 .../exceptions/InvalidOutboxMoveException.java  |  23 ++
 .../james/jmap/methods/MessageSender.java       |  19 +-
 .../methods/SetMessagesUpdateProcessor.java     | 102 +++++++--
 .../org/apache/james/jmap/send/MailFactory.java |   2 +
 .../methods/SetMessagesUpdateProcessorTest.java |  16 +-
 7 files changed, 386 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index c5c7f9b..af9020a 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -28,6 +28,7 @@ import java.io.ObjectOutputStream;
 import java.io.OptionalDataException;
 import java.io.OutputStream;
 import java.io.Serializable;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
@@ -35,7 +36,10 @@ import java.util.Iterator;
 import java.util.Map;
 import java.util.UUID;
 
+import javax.mail.Address;
 import javax.mail.MessagingException;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
 import javax.mail.internet.MimeMessage;
 import javax.mail.internet.ParseException;
 
@@ -49,6 +53,8 @@ import org.apache.mailet.PerRecipientHeaders.Header;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.github.fge.lambdas.Throwing;
+import com.github.steveash.guavate.Guavate;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -84,6 +90,29 @@ public class MailImpl implements Disposable, Mail {
         return new MailImpl(mail, deriveNewName(mail.getName()));
     }
 
+    public static MailImpl fromMimeMessage(String name, MimeMessage mimeMessage) throws MessagingException {
+        MailAddress sender = getSender(mimeMessage);
+        ImmutableList<MailAddress> recipients = getRecipients(mimeMessage);
+        return new MailImpl(name, sender, recipients, mimeMessage);
+    }
+
+    private static ImmutableList<MailAddress> getRecipients(MimeMessage mimeMessage) throws MessagingException {
+        return Arrays.stream(mimeMessage.getAllRecipients())
+            .map(Throwing.function(MailImpl::castToMailAddress).sneakyThrow())
+            .collect(Guavate.toImmutableList());
+    }
+
+    private static MailAddress getSender(MimeMessage mimeMessage) throws MessagingException {
+        Address[] sender = mimeMessage.getFrom();
+        Preconditions.checkArgument(sender.length == 1);
+        return castToMailAddress(sender[0]);
+    }
+
+    private static MailAddress castToMailAddress(Address address) throws AddressException {
+        Preconditions.checkArgument(address instanceof InternetAddress);
+        return new MailAddress((InternetAddress) address);
+    }
+
     /**
      * Create a unique new primary key name for the given MailObject.
      * Detect if this has been called more than 8 times recursively

http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
index c29302b..924c4a2 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
@@ -198,6 +198,11 @@ public abstract class SetMessagesMethodTest {
         return getMailboxId(accessToken, Role.DRAFTS);
     }
 
+    private String getInboxId(AccessToken accessToken) {
+        return getMailboxId(accessToken, Role.INBOX);
+    }
+
+
     private String getMailboxId(AccessToken accessToken, Role role) {
         return getAllMailboxesIds(accessToken).stream()
             .filter(x -> x.get("role").equalsIgnoreCase(role.serialize()))
@@ -564,7 +569,7 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
-    @Ignore("gitlab-446 should allowed in drafts mailbox, rejected outside")
+    @Ignore("JAMES-2220 should allowed in drafts mailbox, rejected outside")
     public void setMessagesShouldReturnAnErrorWhenKeywordsWithAddingDraftArePassed() throws MailboxException {
         mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
 
@@ -700,7 +705,7 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
-    @Ignore("gitlab-446 should allowed outside drafts mailbox, rejected inside")
+    @Ignore("JAMES-2220 should allowed outside drafts mailbox, rejected inside")
     public void setMessagesShouldReturnAnErrorWhenKeywordsWithRemoveDraftArePassed() throws MailboxException {
         mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
 
@@ -1445,7 +1450,7 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
-    public void setMessageShouldAllowDraftCreationWhenUsingIsDraftProperty() {
+    public void setMessagesShouldAllowDraftCreationWhenUsingIsDraftProperty() {
         String messageCreationId = "creationId1337";
         String fromAddress = USERNAME;
         String requestBody = "[" +
@@ -1894,6 +1899,209 @@ public abstract class SetMessagesMethodTest {
     }
 
     @Test
+    public void setMessagesShouldSendMessageByMovingDraftToOutbox() {
+        String draftCreationId = "creationId1337";
+        String fromAddress = USERNAME;
+        String createDraft = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"create\": { \"" + draftCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"" + BOB + "\"}]," +
+            "        \"subject\": \"subject\"," +
+            "        \"keywords\": {\"$Draft\": true}," +
+            "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        String draftId =
+            with()
+                .header("Authorization", accessToken.serialize())
+                .body(createDraft)
+                .post("/jmap")
+            .then()
+                .extract()
+                .path(ARGUMENTS + ".created[\"" + draftCreationId + "\"].id");
+
+        String moveDraftToOutBox = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"update\": { \"" + draftId + "\" : {" +
+            "        \"keywords\": {}," +
+            "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        with()
+            .header("Authorization", accessToken.serialize())
+            .body(moveDraftToOutBox)
+            .post("/jmap");
+
+        calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> isAnyMessageFoundInRecipientsMailboxes(bobAccessToken));
+    }
+
+    @Test
+    @Ignore("WIP")
+    public void setMessagesShouldRejectDraftCopyToOutbox() {
+        String draftCreationId = "creationId1337";
+        String fromAddress = USERNAME;
+        String createDraft = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"create\": { \"" + draftCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"" + BOB + "\"}]," +
+            "        \"subject\": \"subject\"," +
+            "        \"keywords\": {\"$Draft\": true}," +
+            "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        String draftId =
+            with()
+                .header("Authorization", accessToken.serialize())
+                .body(createDraft)
+                .post("/jmap")
+            .then()
+                .extract()
+                .path(ARGUMENTS + ".created[\"" + draftCreationId + "\"].id");
+
+        String copyDraftToOutBox = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"update\": { \"" + draftId + "\" : {" +
+            "        \"keywords\": {}," +
+            "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\",\"" + getDraftId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+        
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body(copyDraftToOutBox)
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("messagesSet"))
+            .body(ARGUMENTS + ".notUpdated", hasKey(draftId))
+            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].type", equalTo("invalidArguments"))
+            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].description", endsWith("One can not have a message in mailboxes that don't have all the `draft` role"))
+            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].properties", hasSize(1))
+            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].properties", contains("mailboxIds"))
+            .body(ARGUMENTS + ".created", aMapWithSize(0));
+    }
+
+    @Test
+    public void setMessagesShouldRejectMovingMessageToOutboxWhenNotInDraft() throws MailboxException {
+        ComposedMessageId message = mailboxProbe.appendMessage(USERNAME, MailboxPath.forUser(USERNAME, MailboxConstants.INBOX),
+            new ByteArrayInputStream("Subject: test\r\n\r\ntestmail".getBytes(Charsets.UTF_8)), new Date(), false, new Flags());
+        await();
+
+        String messageId = message.getMessageId().serialize();
+        String moveMessageToOutBox = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"update\": { \"" + messageId + "\" : {" +
+            "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body(moveMessageToOutBox)
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("messagesSet"))
+            .body(ARGUMENTS + ".notUpdated", hasKey(messageId))
+            .body(ARGUMENTS + ".notUpdated[\""+messageId+"\"].type", equalTo("invalidProperties"))
+            .body(ARGUMENTS + ".notUpdated[\""+messageId+"\"].description", endsWith("only drafts can be moved to Outbox"))
+            .body(ARGUMENTS + ".notUpdated[\""+messageId+"\"].properties", hasSize(1))
+            .body(ARGUMENTS + ".notUpdated[\""+messageId+"\"].properties", contains("mailboxIds"))
+            .body(ARGUMENTS + ".created", aMapWithSize(0));
+    }
+
+    @Test
+    public void setMessagesShouldRejectMovingMessageToOutboxWhenDraftKeyworkSet() throws MailboxException {
+        String draftCreationId = "creationId1337";
+        String fromAddress = USERNAME;
+        String createDraft = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"create\": { \"" + draftCreationId  + "\" : {" +
+            "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+            "        \"to\": [{ \"name\": \"BOB\", \"email\": \"" + BOB + "\"}]," +
+            "        \"subject\": \"subject\"," +
+            "        \"keywords\": {\"$Draft\": true}," +
+            "        \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+        String draftId =
+            with()
+                .header("Authorization", accessToken.serialize())
+                .body(createDraft)
+                .post("/jmap")
+                .then()
+                .extract()
+                .path(ARGUMENTS + ".created[\"" + draftCreationId + "\"].id");
+
+        String moveDraftToOutBox = "[" +
+            "  [" +
+            "    \"setMessages\","+
+            "    {" +
+            "      \"update\": { \"" + draftId + "\" : {" +
+            "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
+            "      }}" +
+            "    }," +
+            "    \"#0\"" +
+            "  ]" +
+            "]";
+
+
+        given()
+            .header("Authorization", accessToken.serialize())
+            .body(moveDraftToOutBox)
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(NAME, equalTo("messagesSet"))
+            .body(ARGUMENTS + ".notUpdated", hasKey(draftId))
+            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].type", equalTo("invalidProperties"))
+            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].description", endsWith("message with $Draft keyword can't be moved outside outbox"))
+            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].properties", hasSize(1))
+            .body(ARGUMENTS + ".notUpdated[\""+draftId+"\"].properties", contains("keywords"))
+            .body(ARGUMENTS + ".created", aMapWithSize(0));
+    }
+
+
+    @Test
     public void setMessagesShouldSupportArbitraryMessageId() {
         String messageCreationId = "1717fcd1-603e-44a5-b2a6-1234dbcd5723";
         String fromAddress = USERNAME;

http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidOutboxMoveException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidOutboxMoveException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidOutboxMoveException.java
new file mode 100644
index 0000000..a70a750
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidOutboxMoveException.java
@@ -0,0 +1,23 @@
+/****************************************************************
+ * 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.exceptions;
+
+public class InvalidOutboxMoveException extends RuntimeException {
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
index 3ce4086..d6835c9 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageSender.java
@@ -22,6 +22,7 @@ package org.apache.james.jmap.methods;
 import javax.inject.Inject;
 import javax.mail.MessagingException;
 
+import org.apache.james.core.MailAddress;
 import org.apache.james.jmap.model.Envelope;
 import org.apache.james.jmap.model.MessageFactory;
 import org.apache.james.jmap.send.MailFactory;
@@ -30,6 +31,7 @@ import org.apache.james.jmap.send.MailSpool;
 import org.apache.james.lifecycle.api.LifecycleUtil;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.mailet.Mail;
 
 public class MessageSender {
@@ -55,9 +57,22 @@ public class MessageSender {
         }
     }
 
+    public void sendMessage(MessageId messageId,
+                            Mail mail,
+                            MailboxSession session) throws MailboxException, MessagingException {
+        assertUserIsSender(session, mail.getSender());
+        MailMetadata metadata = new MailMetadata(messageId, session.getUser().getUserName());
+        mailSpool.send(mail, metadata);
+    }
+
     private void assertUserIsInSenders(Envelope envelope, MailboxSession session) throws MailboxSendingNotAllowedException {
-        String allowedSender = session.getUser().getUserName();
-        if (!session.getUser().isSameUser(envelope.getFrom().asString())) {
+        MailAddress sender = envelope.getFrom();
+        assertUserIsSender(session, sender);
+    }
+
+    private void assertUserIsSender(MailboxSession session, MailAddress sender) throws MailboxSendingNotAllowedException {
+        if (!session.getUser().isSameUser(sender.asString())) {
+            String allowedSender = session.getUser().getUserName();
             throw new MailboxSendingNotAllowedException(allowedSender);
         }
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/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 c54deec..e3865c0 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
@@ -21,16 +21,23 @@ package org.apache.james.jmap.methods;
 
 import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;
 
+import java.io.IOException;
+import java.util.Collection;
 import java.util.List;
 import java.util.Optional;
+import java.util.Properties;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import javax.inject.Inject;
 import javax.mail.Flags;
+import javax.mail.MessagingException;
+import javax.mail.Session;
+import javax.mail.internet.MimeMessage;
 
 import org.apache.james.jmap.exceptions.DraftMessageMailboxUpdateException;
+import org.apache.james.jmap.exceptions.InvalidOutboxMoveException;
 import org.apache.james.jmap.model.MessageProperties;
 import org.apache.james.jmap.model.SetError;
 import org.apache.james.jmap.model.SetMessagesRequest;
@@ -42,6 +49,7 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageIdManager;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.model.FetchGroupImpl;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxId.Factory;
@@ -49,6 +57,8 @@ import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.metrics.api.TimeMetric;
+import org.apache.james.server.core.MailImpl;
+import org.apache.james.util.OptionalUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -67,17 +77,22 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
     private final SystemMailboxesProvider systemMailboxesProvider;
     private final Factory mailboxIdFactory;
     private final MetricFactory metricFactory;
+    private final MessageSender messageSender;
 
     @Inject
     @VisibleForTesting SetMessagesUpdateProcessor(
-        UpdateMessagePatchConverter updatePatchConverter,
-        MessageIdManager messageIdManager,
-        SystemMailboxesProvider systemMailboxesProvider, Factory mailboxIdFactory, MetricFactory metricFactory) {
+            UpdateMessagePatchConverter updatePatchConverter,
+            MessageIdManager messageIdManager,
+            SystemMailboxesProvider systemMailboxesProvider,
+            Factory mailboxIdFactory,
+            MessageSender messageSender,
+            MetricFactory metricFactory) {
         this.updatePatchConverter = updatePatchConverter;
         this.messageIdManager = messageIdManager;
         this.systemMailboxesProvider = systemMailboxesProvider;
         this.mailboxIdFactory = mailboxIdFactory;
         this.metricFactory = metricFactory;
+        this.messageSender = messageSender;
     }
 
     @Override
@@ -116,10 +131,18 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
                 } else {
                     builder.updated(ImmutableList.of(messageId));
                 }
+                sendMessageWhenOutboxInTargetMailboxIds(messageId, updateMessagePatch, mailboxSession, builder);
             }
         } catch (DraftMessageMailboxUpdateException e) {
             handleDraftMessageMailboxUpdateException(messageId, builder, e);
-        } catch (MailboxException e) {
+        } catch (InvalidOutboxMoveException e) {
+            ValidationResult invalidPropertyMailboxIds = ValidationResult.builder()
+                .property(MessageProperties.MessageProperty.mailboxIds.asFieldName())
+                .message("only drafts can be moved to Outbox")
+                .build();
+
+            handleInvalidRequest(builder, messageId, ImmutableList.of(invalidPropertyMailboxIds));
+        } catch (MailboxException|IOException|MessagingException e) {
             handleMessageUpdateException(messageId, builder, e);
         } catch (IllegalArgumentException e) {
             ValidationResult invalidPropertyKeywords = ValidationResult.builder()
@@ -129,24 +152,54 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
 
             handleInvalidRequest(builder, messageId, ImmutableList.of(invalidPropertyKeywords));
         }
+    }
 
+    private void sendMessageWhenOutboxInTargetMailboxIds(MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession, SetMessagesResponse.Builder builder) throws MailboxException, MessagingException, IOException {
+        if (isTargetingOutbox(mailboxSession, listTargetMailboxIds(updateMessagePatch))) {
+            Optional<MessageResult> messagesToSend =
+                messageIdManager.getMessages(
+                    ImmutableList.of(messageId), FetchGroupImpl.FULL_CONTENT, mailboxSession)
+                    .stream()
+                    .findFirst();
+            if (messagesToSend.isPresent()) {
+                MailImpl mail = buildMailFromMessage(messagesToSend.get());
+                messageSender.sendMessage(messageId, mail, mailboxSession);
+            } else {
+                addMessageIdNotFoundToResponse(messageId, builder);
+            }
+        }
     }
 
     private void assertValidUpdate(List<MessageResult> messagesToBeUpdated, UpdateMessagePatch updateMessagePatch, MailboxSession session) throws MailboxException {
         List<MailboxId> draftMailboxes = mailboxIdFor(Role.DRAFTS, session);
+        List<MailboxId> outboxMailboxes = mailboxIdFor(Role.OUTBOX, session);
+
+        ImmutableList<MailboxId> previousMailboxes = messagesToBeUpdated.stream()
+            .map(MessageResult::getMailboxId)
+            .collect(Guavate.toImmutableList());
         List<Flags> futureFlags = patchFlags(messagesToBeUpdated, updateMessagePatch);
-        List<MailboxId> targetMailboxes = getTargetedMailboxes(messagesToBeUpdated, updateMessagePatch);
+        List<MailboxId> targetMailboxes = getTargetedMailboxes(previousMailboxes, updateMessagePatch);
 
+        boolean originIsDraft = previousMailboxes.stream().allMatch(draftMailboxes::contains);
+        boolean targetIsOutbox = targetMailboxes.stream().anyMatch(outboxMailboxes::contains);
         boolean targetHasDraft = targetMailboxes.stream().anyMatch(draftMailboxes::contains);
         boolean targetHasNonDraft = targetMailboxes.stream().anyMatch(id -> !draftMailboxes.contains(id));
 
-        assertValidUpdate(futureFlags, targetHasDraft, targetHasNonDraft);
+        assertValidUpdate(futureFlags, targetHasDraft, targetHasNonDraft, targetIsOutbox, originIsDraft);
     }
 
-    private void assertValidUpdate(List<Flags> futureFlags, boolean targetHasDraft, boolean targetHasNonDraft) throws DraftMessageMailboxUpdateException {
+    private void assertValidUpdate(List<Flags> futureFlags, boolean targetHasDraft, boolean targetHasNonDraft,
+                                   boolean targetIsOutbox, boolean originIsDraft) throws DraftMessageMailboxUpdateException {
         assertMessageIsNotInDraftAndNonDraftMailboxes(targetHasDraft, targetHasNonDraft);
         assertNoNonDraftMessageInsideDraftMailbox(futureFlags, targetHasDraft);
         assertNoDraftMessageOutOfDraftMailbox(futureFlags, targetHasNonDraft);
+        assertOutboxMoveOnlyFromDraft(targetIsOutbox, originIsDraft);
+    }
+
+    private void assertOutboxMoveOnlyFromDraft(boolean targetIsOutbox, boolean originIsDraft) {
+        if (targetIsOutbox && !originIsDraft) {
+            throw new InvalidOutboxMoveException();
+        }
     }
 
     private void assertMessageIsNotInDraftAndNonDraftMailboxes(boolean targetHasDraft, boolean targetHasNonDraft) throws DraftMessageMailboxUpdateException {
@@ -173,10 +226,7 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
         }
     }
 
-    private List<MailboxId> getTargetedMailboxes(List<MessageResult> messagesToBeUpdated, UpdateMessagePatch updateMessagePatch) {
-        ImmutableList<MailboxId> previousMailboxes = messagesToBeUpdated.stream()
-            .map(MessageResult::getMailboxId)
-            .collect(Guavate.toImmutableList());
+    private List<MailboxId> getTargetedMailboxes(ImmutableList<MailboxId> previousMailboxes, UpdateMessagePatch updateMessagePatch) {
         return updateMessagePatch.getMailboxIds()
             .map(ids -> ids.stream().map(mailboxIdFactory::fromString).collect(Guavate.toImmutableList()))
             .orElse(previousMailboxes);
@@ -195,6 +245,34 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
             .collect(Guavate.toImmutableList());
     }
 
+    private MailImpl buildMailFromMessage(MessageResult message) throws MessagingException, IOException, MailboxException {
+        return MailImpl.fromMimeMessage(message.getMessageId().serialize(),
+            new MimeMessage(
+                Session.getDefaultInstance(new Properties()),
+                message.getFullContent().getInputStream()));
+    }
+
+    private Set<MailboxId> listTargetMailboxIds(UpdateMessagePatch updateMessagePatch) {
+        return OptionalUtils.toStream(updateMessagePatch.getMailboxIds())
+            .flatMap(Collection::stream)
+            .map(mailboxIdFactory::fromString)
+            .collect(Guavate.toImmutableSet());
+    }
+
+    private boolean isTargetingOutbox(MailboxSession mailboxSession, Set<MailboxId> targetMailboxIds) throws MailboxException {
+        Set<MailboxId> outboxes = listMailboxIdsForRole(mailboxSession, Role.OUTBOX);
+        if (outboxes.isEmpty()) {
+            throw new MailboxNotFoundException("At least one outbox should be accessible");
+        }
+        return targetMailboxIds.stream().anyMatch(outboxes::contains);
+    }
+
+    private Set<MailboxId> listMailboxIdsForRole(MailboxSession session, Role role) throws MailboxException {
+        return systemMailboxesProvider.getMailboxByRole(role, session)
+            .map(MessageManager::getId)
+            .collect(Guavate.toImmutableSet());
+    }
+
     private Stream<MailboxException> updateFlags(MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession, MessageResult messageResult) {
         try {
             if (!updateMessagePatch.isFlagsIdentity()) {
@@ -240,7 +318,7 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor {
 
     private SetMessagesResponse.Builder handleMessageUpdateException(MessageId messageId,
                                               SetMessagesResponse.Builder builder,
-                                              MailboxException e) {
+                                              Exception e) {
         LOGGER.error("An error occurred when updating a message", e);
         return builder.notUpdated(ImmutableMap.of(messageId, SetError.builder()
                 .type("anErrorOccurred")

http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java
index 3d156d3..966f659 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/MailFactory.java
@@ -40,4 +40,6 @@ public class MailFactory {
             message.getContent());
     }
 
+
+
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/29d233cc/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
index de2f809..2264fda 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessorTest.java
@@ -49,10 +49,13 @@ public class SetMessagesUpdateProcessorTest {
         MessageIdManager messageIdManager = null;
         SystemMailboxesProvider systemMailboxesProvider = null;
         MailboxId.Factory mailboxIdFactory = null;
+        MessageSender messageSender = null;
         SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(updatePatchConverter,
             messageIdManager,
             systemMailboxesProvider,
-            mailboxIdFactory, new NoopMetricFactory());
+            mailboxIdFactory,
+            messageSender,
+            new NoopMetricFactory());
         SetMessagesRequest requestWithEmptyUpdate = SetMessagesRequest.builder().build();
 
         SetMessagesResponse result = sut.process(requestWithEmptyUpdate, null);
@@ -76,7 +79,16 @@ public class SetMessagesUpdateProcessorTest {
         when(mockConverter.fromJsonNode(any(ObjectNode.class)))
                 .thenReturn(mockInvalidPatch);
 
-        SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(mockConverter, null, null, null, new NoopMetricFactory());
+        MessageIdManager messageIdManager = null;
+        SystemMailboxesProvider systemMailboxesProvider = null;
+        MailboxId.Factory mailboxIdFactory = null;
+        MessageSender messageSender = null;
+        SetMessagesUpdateProcessor sut = new SetMessagesUpdateProcessor(mockConverter,
+            messageIdManager,
+            systemMailboxesProvider,
+            mailboxIdFactory,
+            messageSender,
+            new NoopMetricFactory());
         MessageId requestMessageId = TestMessageId.of(1);
         SetMessagesRequest requestWithInvalidUpdate = SetMessagesRequest.builder()
                 .update(ImmutableMap.of(requestMessageId, JsonNodeFactory.instance.objectNode()))


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


[07/18] james-project git commit: JAMES-2220 replace ambiguous MailImpl constructor with factory method

Posted by ad...@apache.org.
JAMES-2220 replace ambiguous MailImpl constructor with factory method


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

Branch: refs/heads/master
Commit: 05e5da3540dae357a40c86c54ed1ae246b03313f
Parents: 549f7ae
Author: Matthieu Baechler <ma...@apache.org>
Authored: Tue Nov 14 15:42:37 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:30 2017 +0100

----------------------------------------------------------------------
 .../org/apache/james/server/core/MailImpl.java  | 20 ++++++++++----------
 .../apache/james/server/core/MailImplTest.java  | 19 +++++++++++++++++--
 .../core/MimeMessageCopyOnWriteProxyTest.java   |  8 ++++----
 .../modules/server/MemoryMailQueueFactory.java  |  2 +-
 .../impl/camel/MatcherSplitter.java             |  2 +-
 .../mailets/AbstractRecipientRewriteTable.java  |  2 +-
 .../james/transport/mailets/DSNBounce.java      |  2 +-
 .../mailets/redirect/ProcessRedirectNotify.java |  2 +-
 8 files changed, 36 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/05e5da35/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index ad66e7f..07aed83 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -72,6 +72,16 @@ import com.google.common.base.Preconditions;
  */
 public class MailImpl implements Disposable, Mail {
 
+    /**
+     * Create a copy of the input mail and assign it a new name
+     *
+     * @param mail original mail
+     * @throws MessagingException when the message is not clonable
+     */
+    public static MailImpl duplicate(Mail mail) throws MessagingException {
+        return new MailImpl(mail, newName(mail));
+    }
+
     private static final Logger LOGGER = LoggerFactory.getLogger(MailImpl.class);
 
     /**
@@ -156,16 +166,6 @@ public class MailImpl implements Disposable, Mail {
     }
 
     /**
-     * Create a copy of the input mail and assign it a new name
-     *
-     * @param mail original mail
-     * @throws MessagingException when the message is not clonable
-     */
-    public MailImpl(Mail mail) throws MessagingException {
-        this(mail, newName(mail));
-    }
-
-    /**
      * @param mail
      * @param newName
      * @throws MessagingException

http://git-wip-us.apache.org/repos/asf/james-project/blob/05e5da35/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java b/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
index 5f299a4..f1beea2 100644
--- a/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
+++ b/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
@@ -126,6 +126,21 @@ public class MailImplTest {
     }
 
     @Test
+    public void duplicateFactoryMethodShouldGenerateNewObjectWithSameValuesButName() throws MessagingException, IOException {
+        ImmutableList<MailAddress> recipients = ImmutableList.of();
+        String name = MailUtil.newId();
+        String sender = "sender@localhost";
+        MailAddress senderMailAddress = new MailAddress(sender);
+
+        MailImpl mail = new MailImpl(name, senderMailAddress, recipients, emptyMessage);
+        MailImpl duplicate = MailImpl.duplicate(mail);
+
+        assertThat(duplicate).isNotSameAs(mail).isEqualToIgnoringGivenFields(mail, "message", "name");
+        assertThat(duplicate.getName()).isNotEqualTo(name);
+        assertThat(duplicate.getMessage().getInputStream()).hasSameContentAs(mail.getMessage().getInputStream());
+    }
+
+    @Test
     public void duplicateShouldGenerateNewObjectWithSameValuesButName() throws MessagingException, IOException {
         ImmutableList<MailAddress> recipients = ImmutableList.of();
         String name = MailUtil.newId();
@@ -133,10 +148,10 @@ public class MailImplTest {
         MailAddress senderMailAddress = new MailAddress(sender);
 
         MailImpl mail = new MailImpl(name, senderMailAddress, recipients, emptyMessage);
-        MailImpl duplicate = (MailImpl) mail.duplicate("new name");
+        MailImpl duplicate = MailImpl.duplicate(mail);
 
         assertThat(duplicate).isNotSameAs(mail).isEqualToIgnoringGivenFields(mail, "message", "name");
-        assertThat(duplicate.getName()).isEqualTo("new name");
+        assertThat(duplicate.getName()).isNotEqualTo(name);
         assertThat(duplicate.getMessage().getInputStream()).hasSameContentAs(mail.getMessage().getInputStream());
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/05e5da35/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java b/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java
index 1829d11..1253da1 100644
--- a/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java
+++ b/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java
@@ -60,7 +60,7 @@ public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest {
         MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources(
                 content + sep + body);
         MailImpl mail = new MailImpl("test", new MailAddress("test@test.com"), r, messageFromSources);
-        MailImpl m2 = (MailImpl) mail.duplicate("clone");
+        MailImpl m2 = MailImpl.duplicate(mail);
         System.out.println("mail: " + getReferences(mail.getMessage()) + " m2: " + getReferences(m2.getMessage()));
         assertNotSame(m2, mail);
         assertNotSame(m2.getMessage(), mail.getMessage());
@@ -86,7 +86,7 @@ public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest {
         MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources(
                 content + sep + body);
         MailImpl mail = new MailImpl("test", new MailAddress("test@test.com"), r, messageFromSources);
-        MailImpl m2 = (MailImpl) mail.duplicate("clone");
+        MailImpl m2 = MailImpl.duplicate(mail);
         System.out.println("mail: " + getReferences(mail.getMessage()) + " m2: " + getReferences(m2.getMessage()));
         assertNotSame(m2, mail);
         assertNotSame(m2.getMessage(), mail.getMessage());
@@ -137,7 +137,7 @@ public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest {
                 content + sep + body);
         MailImpl mail = new MailImpl("test", new MailAddress("test@test.com"), r, messageFromSources);
         // cloning the message
-        Mail mailClone = mail.duplicate("clone");
+        Mail mailClone = MailImpl.duplicate(mail);
         assertTrue(isSameMimeMessage(mailClone.getMessage(), mail.getMessage()));
         MimeMessage mm = getWrappedMessage(mail.getMessage());
         assertNotSame(mail.getMessage(), mailClone.getMessage());
@@ -194,7 +194,7 @@ public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest {
                 content + sep + body);
         MailImpl mail = new MailImpl("test", new MailAddress("test@test.com"), r, messageFromSources);
         // cloning the message
-        MailImpl mailClone = (MailImpl) mail.duplicate("clone");
+        MailImpl mailClone = MailImpl.duplicate(mail);
         LifecycleUtil.dispose(mail);
 
         assertNotNull(getWrappedMessage(mailClone.getMessage()));

http://git-wip-us.apache.org/repos/asf/james-project/blob/05e5da35/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/server/MemoryMailQueueFactory.java
----------------------------------------------------------------------
diff --git a/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/server/MemoryMailQueueFactory.java b/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/server/MemoryMailQueueFactory.java
index d3f5941..8fcc941 100644
--- a/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/server/MemoryMailQueueFactory.java
+++ b/server/container/guice/memory-guice/src/main/java/org/apache/james/modules/server/MemoryMailQueueFactory.java
@@ -87,7 +87,7 @@ public class MemoryMailQueueFactory implements MailQueueFactory {
         }
 
         private Mail cloneMail(Mail mail) throws MessagingException {
-            MailImpl mailImpl = new MailImpl(mail);
+            MailImpl mailImpl = MailImpl.duplicate(mail);
             Optional.ofNullable(mail.getMessage())
                     .ifPresent(Throwing.consumer(message -> mailImpl.setMessage(new MimeMessage(message))));
             return mailImpl;

http://git-wip-us.apache.org/repos/asf/james-project/blob/05e5da35/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/MatcherSplitter.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/MatcherSplitter.java b/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/MatcherSplitter.java
index df09138..36c77c2 100644
--- a/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/MatcherSplitter.java
+++ b/server/mailet/mailetcontainer-camel/src/main/java/org/apache/james/mailetcontainer/impl/camel/MatcherSplitter.java
@@ -157,7 +157,7 @@ public class MatcherSplitter {
                 } else {
                     mail.setRecipients(rcpts);
 
-                    Mail newMail = new MailImpl(mail);
+                    Mail newMail = MailImpl.duplicate(mail);
                     newMail.setRecipients(matchedRcpts);
 
                     // Set a header because the matcher matched. This can be

http://git-wip-us.apache.org/repos/asf/james-project/blob/05e5da35/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/AbstractRecipientRewriteTable.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/AbstractRecipientRewriteTable.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/AbstractRecipientRewriteTable.java
index 095af8e..86c4077 100755
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/AbstractRecipientRewriteTable.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/AbstractRecipientRewriteTable.java
@@ -176,7 +176,7 @@ public abstract class AbstractRecipientRewriteTable extends GenericMailet {
 
             // duplicates the Mail object, to be able to modify the new mail
             // keeping the original untouched
-            MailImpl newMail = new MailImpl(mail);
+            MailImpl newMail = MailImpl.duplicate(mail);
             try {
                 try {
                     newMail.setRemoteAddr(dns.getLocalHost().getHostAddress());

http://git-wip-us.apache.org/repos/asf/james-project/blob/05e5da35/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java
index f1edc22..13b0e9a 100755
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/DSNBounce.java
@@ -242,7 +242,7 @@ public class DSNBounce extends GenericMailet implements RedirectNotify {
     }
 
     private void trySendBounce(Mail originalMail) throws MessagingException {
-        MailImpl newMail = new MailImpl(originalMail);
+        MailImpl newMail = MailImpl.duplicate(originalMail);
         try {
             newMail.setRemoteHost(getRemoteHost());
             newMail.setRemoteAddr(getRemoteAddr());

http://git-wip-us.apache.org/repos/asf/james-project/blob/05e5da35/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/redirect/ProcessRedirectNotify.java
----------------------------------------------------------------------
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/redirect/ProcessRedirectNotify.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/redirect/ProcessRedirectNotify.java
index 0cf003c..7e4e297 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/redirect/ProcessRedirectNotify.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/redirect/ProcessRedirectNotify.java
@@ -47,7 +47,7 @@ public class ProcessRedirectNotify {
 
         // duplicates the Mail object, to be able to modify the new mail keeping
         // the original untouched
-        MailImpl newMail = new MailImpl(originalMail);
+        MailImpl newMail = MailImpl.duplicate(originalMail);
         try {
             MailModifier mailModifier = MailModifier.builder()
                     .mailet(mailet)


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


[08/18] james-project git commit: JAMES-2220 port MailImpl tests to assertj and covers constructors

Posted by ad...@apache.org.
JAMES-2220 port MailImpl tests to assertj and covers constructors


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

Branch: refs/heads/master
Commit: 549f7aef3953c40edcb832fea3bff6dea7b0c641
Parents: 7c1685b
Author: Matthieu Baechler <ma...@apache.org>
Authored: Tue Nov 14 15:36:59 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:30 2017 +0100

----------------------------------------------------------------------
 .../org/apache/james/server/core/MailImpl.java  |  14 +-
 .../apache/james/server/core/MailImplTest.java  | 129 +++++++++++--------
 .../server/core/MailTestAllImplementations.java |  72 -----------
 .../core/MimeMessageCopyOnWriteProxyTest.java   |  10 +-
 4 files changed, 86 insertions(+), 139 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/549f7aef/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index 2e9aa38..ad66e7f 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -49,6 +49,7 @@ import org.apache.mailet.PerRecipientHeaders.Header;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 
 /**
@@ -170,7 +171,7 @@ public class MailImpl implements Disposable, Mail {
      * @throws MessagingException
      */
     @SuppressWarnings("unchecked")
-    public MailImpl(Mail mail, String newName) throws MessagingException {
+    private MailImpl(Mail mail, String newName) throws MessagingException {
         this(newName, mail.getSender(), mail.getRecipients(), mail.getMessage());
         setRemoteHost(mail.getRemoteHost());
         setRemoteAddr(mail.getRemoteAddr());
@@ -229,22 +230,13 @@ public class MailImpl implements Disposable, Mail {
     }
 
     /**
-     * Duplicate the MailImpl.
-     *
-     * @return a MailImpl that is a duplicate of this one
-     */
-    public Mail duplicate() {
-        return duplicate(name);
-    }
-
-    /**
      * Duplicate the MailImpl, replacing the mail name with the one passed in as
      * an argument.
      *
      * @param newName the name for the duplicated mail
      * @return a MailImpl that is a duplicate of this one with a different name
      */
-    public Mail duplicate(String newName) {
+    @VisibleForTesting Mail duplicate(String newName) {
         try {
             return new MailImpl(this, newName);
         } catch (MessagingException me) {

http://git-wip-us.apache.org/repos/asf/james-project/blob/549f7aef/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java b/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
index 5ab4cd0..5f299a4 100644
--- a/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
+++ b/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
@@ -18,15 +18,14 @@
  ****************************************************************/
 package org.apache.james.server.core;
 
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNotSame;
-import static org.junit.Assert.assertNull;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
 
 import javax.mail.MessagingException;
 import javax.mail.Session;
@@ -35,83 +34,110 @@ import javax.mail.internet.MimeMessage;
 import org.apache.james.core.MailAddress;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.test.MailUtil;
+import org.junit.Before;
 import org.junit.Test;
 
-public class MailImplTest extends MailTestAllImplementations {
+import com.google.common.collect.ImmutableList;
+
+public class MailImplTest {
 
     private static final Session NO_SESSION = null;
-    
-    @Override
-    protected Mail createMailImplementation() {
-        return new MailImpl();
+    private MimeMessage emptyMessage;
+
+    @Before
+    public void setup() throws MessagingException {
+        emptyMessage = new MimeMessage(NO_SESSION, new ByteArrayInputStream(new byte[0]));
+    }
+
+    @Test
+    public void mailImplShouldHaveSensibleInitialValues() throws MessagingException {
+        MailImpl mail = new MailImpl();
+
+        assertThat(mail.hasAttributes()).describedAs("no initial attributes").isFalse();
+        assertThat(mail.getErrorMessage()).describedAs("no initial error").isNull();
+        assertThat(mail.getLastUpdated()).isCloseTo(new Date(), TimeUnit.SECONDS.toMillis(1));
+        assertThat(mail.getRecipients()).describedAs("no initial recipient").isNullOrEmpty();
+        assertThat(mail.getRemoteAddr()).describedAs("initial remote address is localhost ip").isEqualTo("127.0.0.1");
+        assertThat(mail.getRemoteHost()).describedAs("initial remote host is localhost").isEqualTo("localhost");
+        assertThat(mail.getState()).describedAs("default initial state").isEqualTo(Mail.DEFAULT);
+        assertThat(mail.getMessage()).isNull();
+        assertThat(mail.getSender()).isNull();
+        assertThat(mail.getName()).isNull();
     }
 
     @Test
-    public void testConstr1() throws MessagingException {
+    public void mailImplShouldThrowWhenComputingSizeOnDefaultInstance() throws MessagingException {
         MailImpl mail = new MailImpl();
 
-        helperTestInitialState(mail);
-        helperTestMessageSize(mail, 0); // MimeMessageWrapper default is 0
-        assertNull("no initial message", mail.getMessage());
-        assertNull("no initial sender", mail.getSender());
-        assertNull("no initial name", mail.getName());
+        assertThatThrownBy(mail::getMessageSize).isInstanceOf(NullPointerException.class);
     }
 
     @Test
-    public void testConstr2() throws MessagingException {
-        ArrayList<MailAddress> recepients = new ArrayList<>();
+    public void mailImplConstructionShouldSetDefaultValuesOnUnspecifiedFields() throws MessagingException {
+        ArrayList<MailAddress> recipients = new ArrayList<>();
         String name = MailUtil.newId();
         String sender = "sender@localhost";
         MailAddress senderMailAddress = new MailAddress(sender);
-        MailImpl mail = new MailImpl(name, senderMailAddress, recepients);
+        MailImpl mail = new MailImpl(name, senderMailAddress, recipients);
 
-        helperTestInitialState(mail); // MimeMessageWrapper default is 0
-        helperTestMessageSize(mail, 0); // MimeMessageWrapper default is 0
-        assertNull("no initial message", mail.getMessage());
-        assertEquals("sender", sender, mail.getSender().toString());
-        assertEquals("name", name, mail.getName());
 
-        mail.setMessage(new MimeMessage(NO_SESSION));
-        assertNotNull("message", mail.getMessage());
+        MailImpl expected = new MailImpl();
+        assertThat(mail).isEqualToIgnoringGivenFields(expected, "sender", "name", "recipients", "lastUpdated");
+        assertThat(mail.getLastUpdated()).isCloseTo(new Date(), TimeUnit.SECONDS.toMillis(1));
     }
 
     @Test
-    public void testConstr3() throws MessagingException {
-        ArrayList<MailAddress> recepients = new ArrayList<>();
+    public void mailImplConstructionShouldSetSpecifiedFields() throws MessagingException {
+        ImmutableList<MailAddress> recipients = ImmutableList.of();
+        String name = MailUtil.newId();
+        String sender = "sender@localhost";
+        MailAddress senderMailAddress = new MailAddress(sender);
+        MailImpl mail = new MailImpl(name, senderMailAddress, recipients);
+
+        assertThat(mail.getSender().asString()).isEqualTo(sender);
+        assertThat(mail.getName()).isEqualTo(name);
+
+     }
+
+    @Test
+    public void mailImplConstructionWithMimeMessageShouldSetSpecifiedFields() throws MessagingException {
+        ImmutableList<MailAddress> recipients = ImmutableList.of();
         String name = MailUtil.newId();
         String sender = "sender@localhost";
         MailAddress senderMailAddress = new MailAddress(sender);
-        MimeMessage mimeMessage = new MimeMessage(NO_SESSION, new ByteArrayInputStream(new byte[0]));
-        MailImpl mail = new MailImpl(name, senderMailAddress, recepients, mimeMessage);
-
-        helperTestInitialState(mail);
-        helperTestMessageSize(mail, 0);
-        assertEquals("initial message", mimeMessage.getMessageID(), mail.getMessage().getMessageID());
-        assertEquals("sender", sender, mail.getSender().toString());
-        assertEquals("name", name, mail.getName());
-        mail.dispose();
+
+        MailImpl expected = new MailImpl(name, senderMailAddress, recipients);
+        MailImpl mail = new MailImpl(name, senderMailAddress, recipients, emptyMessage);
+
+        assertThat(mail).isEqualToIgnoringGivenFields(expected, "message", "lastUpdated");
+        assertThat(mail.getLastUpdated()).isCloseTo(new Date(), TimeUnit.SECONDS.toMillis(1));
     }
 
     @Test
-    public void testDuplicate() throws MessagingException {
-        MailImpl mail = new MailImpl();
-        MailImpl duplicate = (MailImpl) mail.duplicate();
-        assertNotSame("is real duplicate", mail, duplicate);
-        helperTestInitialState(duplicate);
-        helperTestMessageSize(duplicate, 0);
+    public void mailImplConstructionWithMimeMessageShouldNotOverwriteMessageId() throws MessagingException {
+        ImmutableList<MailAddress> recipients = ImmutableList.of();
+        String name = MailUtil.newId();
+        String sender = "sender@localhost";
+        MailAddress senderMailAddress = new MailAddress(sender);
+
+        MailImpl mail = new MailImpl(name, senderMailAddress, recipients, emptyMessage);
+
+        assertThat(mail.getMessage().getMessageID()).isEqualTo(emptyMessage.getMessageID());
     }
 
     @Test
-    public void testDuplicateNewName() throws MessagingException {
-        String newName = "aNewName";
+    public void duplicateShouldGenerateNewObjectWithSameValuesButName() throws MessagingException, IOException {
+        ImmutableList<MailAddress> recipients = ImmutableList.of();
+        String name = MailUtil.newId();
+        String sender = "sender@localhost";
+        MailAddress senderMailAddress = new MailAddress(sender);
 
-        MailImpl mail = new MailImpl();
-        assertFalse("before + after names differ", newName.equals(mail.getName()));
+        MailImpl mail = new MailImpl(name, senderMailAddress, recipients, emptyMessage);
+        MailImpl duplicate = (MailImpl) mail.duplicate("new name");
 
-        MailImpl duplicate = (MailImpl) mail.duplicate(newName);
-        assertEquals("new name set", newName, duplicate.getName());
-        helperTestInitialState(duplicate);
-        helperTestMessageSize(duplicate, 0);
+        assertThat(duplicate).isNotSameAs(mail).isEqualToIgnoringGivenFields(mail, "message", "name");
+        assertThat(duplicate.getName()).isEqualTo("new name");
+        assertThat(duplicate.getMessage().getInputStream()).hasSameContentAs(mail.getMessage().getInputStream());
     }
 
     @Test
@@ -121,4 +147,5 @@ public class MailImplTest extends MailTestAllImplementations {
         assertThatThrownBy(() -> mail.setAttribute(null, "toto"))
             .isInstanceOf(NullPointerException.class);
     }
+
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/549f7aef/server/container/core/src/test/java/org/apache/james/server/core/MailTestAllImplementations.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/test/java/org/apache/james/server/core/MailTestAllImplementations.java b/server/container/core/src/test/java/org/apache/james/server/core/MailTestAllImplementations.java
deleted file mode 100644
index 1a133b2..0000000
--- a/server/container/core/src/test/java/org/apache/james/server/core/MailTestAllImplementations.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/****************************************************************
- * 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.server.core;
-
-import javax.mail.MessagingException;
-
-import org.apache.mailet.Mail;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-/**
- * testing common behavior of Mail implementors. subclasses automatically get
- * their Mail-behavior tested.
- */
-public abstract class MailTestAllImplementations {
-
-    /** provide the concrete implementation to test */
-    protected abstract Mail createMailImplementation();
-
-    protected void helperTestInitialState(Mail mail) {
-        assertFalse("no initial attributes", mail.hasAttributes());
-        assertNull("no initial error", mail.getErrorMessage());
-        assertNotNull("initial last update set", mail.getLastUpdated());
-        try {
-            assertTrue("no initial recipient", mail.getRecipients().isEmpty());
-        } catch (NullPointerException e) {
-            // current behavior. *BUT*, shouldn't this method better return with
-            // an empty list?!
-        }
-        assertEquals("initial remote address is localhost ip", "127.0.0.1", mail.getRemoteAddr());
-        assertEquals("initial remote host is localhost", "localhost", mail.getRemoteHost());
-        assertEquals("default initial state", Mail.DEFAULT, mail.getState());
-    }
-
-    protected void helperTestMessageSize(Mail mail, int expectedMsgSize) throws MessagingException {
-        try {
-            assertEquals("initial message size == " + expectedMsgSize, expectedMsgSize, mail.getMessageSize());
-        } catch (NullPointerException e) {
-            // current behavior. *BUT*, shouldn't this method return more
-            // gracefully?!
-        }
-    }
-
-    @Test
-    public void testAttributes() {
-        Mail mail = createMailImplementation();
-        assertFalse("no initial attributes", mail.hasAttributes());
-        assertFalse("attributes initially empty", mail.getAttributeNames().hasNext());
-        assertNull("not found on emtpy list", mail.getAttribute("test"));
-        assertNull("no previous item with key", mail.setAttribute("testKey", "testValue"));
-        assertEquals("item found", "testValue", mail.getAttribute("testKey"));
-        assertTrue("has attribute", mail.hasAttributes());
-        assertEquals("item removed", "testValue", mail.removeAttribute("testKey"));
-        assertNull("item no longer found", mail.getAttribute("testKey"));
-    }
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/549f7aef/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java b/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java
index afea93c..1829d11 100644
--- a/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java
+++ b/server/container/core/src/test/java/org/apache/james/server/core/MimeMessageCopyOnWriteProxyTest.java
@@ -60,7 +60,7 @@ public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest {
         MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources(
                 content + sep + body);
         MailImpl mail = new MailImpl("test", new MailAddress("test@test.com"), r, messageFromSources);
-        MailImpl m2 = (MailImpl) mail.duplicate();
+        MailImpl m2 = (MailImpl) mail.duplicate("clone");
         System.out.println("mail: " + getReferences(mail.getMessage()) + " m2: " + getReferences(m2.getMessage()));
         assertNotSame(m2, mail);
         assertNotSame(m2.getMessage(), mail.getMessage());
@@ -86,7 +86,7 @@ public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest {
         MimeMessageCopyOnWriteProxy messageFromSources = (MimeMessageCopyOnWriteProxy) getMessageFromSources(
                 content + sep + body);
         MailImpl mail = new MailImpl("test", new MailAddress("test@test.com"), r, messageFromSources);
-        MailImpl m2 = (MailImpl) mail.duplicate();
+        MailImpl m2 = (MailImpl) mail.duplicate("clone");
         System.out.println("mail: " + getReferences(mail.getMessage()) + " m2: " + getReferences(m2.getMessage()));
         assertNotSame(m2, mail);
         assertNotSame(m2.getMessage(), mail.getMessage());
@@ -104,7 +104,7 @@ public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest {
         assertEquals(m2.getMessage().getSubject(), "new Subject");
         assertEquals(mail.getMessage().getSubject(), "foo");
         // cloning again the messages
-        Mail m2clone = m2.duplicate();
+        Mail m2clone = m2.duplicate("clone2");
         assertTrue(isSameMimeMessage(m2clone.getMessage(), m2.getMessage()));
         MimeMessage mm = getWrappedMessage(m2.getMessage());
         assertNotSame(m2.getMessage(), m2clone.getMessage());
@@ -137,7 +137,7 @@ public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest {
                 content + sep + body);
         MailImpl mail = new MailImpl("test", new MailAddress("test@test.com"), r, messageFromSources);
         // cloning the message
-        Mail mailClone = mail.duplicate();
+        Mail mailClone = mail.duplicate("clone");
         assertTrue(isSameMimeMessage(mailClone.getMessage(), mail.getMessage()));
         MimeMessage mm = getWrappedMessage(mail.getMessage());
         assertNotSame(mail.getMessage(), mailClone.getMessage());
@@ -194,7 +194,7 @@ public class MimeMessageCopyOnWriteProxyTest extends MimeMessageFromStreamTest {
                 content + sep + body);
         MailImpl mail = new MailImpl("test", new MailAddress("test@test.com"), r, messageFromSources);
         // cloning the message
-        MailImpl mailClone = (MailImpl) mail.duplicate();
+        MailImpl mailClone = (MailImpl) mail.duplicate("clone");
         LifecycleUtil.dispose(mail);
 
         assertNotNull(getWrappedMessage(mailClone.getMessage()));


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


[06/18] james-project git commit: JAMES-2220 be consistent when setting recipients in MailImpl

Posted by ad...@apache.org.
JAMES-2220 be consistent when setting recipients in MailImpl


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

Branch: refs/heads/master
Commit: 8d0a70de1f885da53d4e7f8c1d7d9c4e3c1e1572
Parents: 05e5da3
Author: Matthieu Baechler <ma...@apache.org>
Authored: Tue Nov 14 15:45:13 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:30 2017 +0100

----------------------------------------------------------------------
 .../java/org/apache/james/server/core/MailImpl.java    | 13 ++++---------
 1 file changed, 4 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/8d0a70de/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index 07aed83..d146a07 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -51,6 +51,7 @@ import org.slf4j.LoggerFactory;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 
 /**
  * <p>
@@ -142,6 +143,7 @@ public class MailImpl implements Disposable, Mail {
         setState(Mail.DEFAULT);
         attributes = new HashMap<>();
         perRecipientSpecificHeaders = new PerRecipientHeaders();
+        this.recipients = null;
     }
 
     /**
@@ -156,12 +158,10 @@ public class MailImpl implements Disposable, Mail {
         this();
         this.name = name;
         this.sender = sender;
-        this.recipients = null;
 
         // Copy the recipient list
         if (recipients != null) {
-            this.recipients = new ArrayList<>();
-            this.recipients.addAll(recipients);
+            setRecipients(recipients);
         }
     }
 
@@ -397,14 +397,9 @@ public class MailImpl implements Disposable, Mail {
         }
     }
 
-    /**
-     * Set the recipients for this MailImpl.
-     *
-     * @param recipients the recipients for this MailImpl
-     */
     @Override
     public void setRecipients(Collection<MailAddress> recipients) {
-        this.recipients = recipients;
+        this.recipients = ImmutableList.copyOf(recipients);
     }
 
     /**


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


[04/18] james-project git commit: JAMES-2218 Disable creating Outbox messages with Draft flags

Posted by ad...@apache.org.
JAMES-2218 Disable creating Outbox messages with Draft 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/ad893d02
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/ad893d02
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/ad893d02

Branch: refs/heads/master
Commit: ad893d026fc73c07cc6204926d9413b951f7df4d
Parents: 45e7f3a
Author: benwa <bt...@linagora.com>
Authored: Wed Nov 15 15:28:12 2017 +0700
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:28:58 2017 +0100

----------------------------------------------------------------------
 .../integration/SetMessagesMethodTest.java      | 10 +++++-----
 .../cucumber/SetMessagesMethodStepdefs.java     | 21 +++++++++++++-------
 .../test/resources/cucumber/SetMessages.feature | 13 ++++++++----
 .../InvalidDraftKeywordsException.java          |  4 +++-
 .../methods/SetMessagesCreationProcessor.java   | 20 ++++++++++++++-----
 5 files changed, 46 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/ad893d02/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
index 951d369..054b589 100644
--- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
+++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/SetMessagesMethodTest.java
@@ -1288,7 +1288,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
                 "        \"subject\": \"subject\"," +
                 "        \"isDraft\": true," +
-                "        \"keywords\": {\"$Draft\": true}," +
+                "        \"keywords\": {\"$Answered\": true}," +
                 "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
@@ -1321,7 +1321,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
                 "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
                 "        \"subject\": \"subject\"," +
-                "        \"keywords\": {\"$Draft\": true, \"$Flagged\": true}," +
+                "        \"keywords\": {\"$Answered\": true, \"$Flagged\": true}," +
                 "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
@@ -1341,7 +1341,7 @@ public abstract class SetMessagesMethodTest {
             .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
             .body(ARGUMENTS + ".created", aMapWithSize(1))
             .body(ARGUMENTS + ".created", hasKey(messageCreationId))
-            .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].keywords.$Draft", equalTo(true))
+            .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].keywords.$Answered", equalTo(true))
             .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].keywords.$Flagged", equalTo(true));
     }
 
@@ -4497,7 +4497,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
                 "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
                 "        \"subject\": \"subject\"," +
-                "        \"keywords\": {\"$Draft\": true, \"$Unknown\": true}," +
+                "        \"keywords\": {\"$Answered\": true, \"$Unknown\": true}," +
                 "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
@@ -4530,7 +4530,7 @@ public abstract class SetMessagesMethodTest {
             "        \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
             "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
             "        \"subject\": \"subject\"," +
-            "        \"keywords\": {\"$Draft\": true, \"$Deleted\": true}," +
+            "        \"keywords\": {\"$Answered\": true, \"$Deleted\": true}," +
             "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
             "      }}" +
             "    }," +

http://git-wip-us.apache.org/repos/asf/james-project/blob/ad893d02/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 fc9370f..3522d3c 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
@@ -23,12 +23,12 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.mail.Flags;
 
-import org.apache.james.jmap.DefaultMailboxes;
 import org.apache.james.jmap.model.Keywords;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxId;
@@ -195,13 +195,12 @@ public class SetMessagesMethodStepdefs {
         });
     }
 
-
-    @Given("^\"([^\"]*)\" creates a draft message \"([^\"]*)\"")
-    public void createDraft(String username, String message) throws Throwable {
+    @Given("^\"([^\"]*)\" creates 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,
                 username,
-                DefaultMailboxes.DRAFTS);
+                mailboxName);
             httpClient.post("[" +
                 "  [" +
                 "    \"setMessages\","+
@@ -216,8 +215,10 @@ public class SetMessagesMethodStepdefs {
                 "  ]" +
                 "]");
             mainStepdefs.awaitMethod.run();
-            String messageId = httpClient.jsonPath.read("[0][1].created." + message + ".id");
-            getMessagesMethodStepdefs.addMessageId(message, mainStepdefs.messageIdFactory.fromString(messageId));
+            Optional.ofNullable(
+                httpClient.jsonPath.<String>read("[0][1].created." + message + ".id"))
+                .map(mainStepdefs.messageIdFactory::fromString)
+                .ifPresent(id -> getMessagesMethodStepdefs.addMessageId(message, id));
         });
     }
 
@@ -264,4 +265,10 @@ public class SetMessagesMethodStepdefs {
             .containsOnlyKeys(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/ad893d02/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 2294513..9e6dcce 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,20 +106,25 @@ Feature: SetMessages method on shared folders
 
   Scenario: A user can update the flags on a draft
     Given "bob@domain.tld" has a mailbox "Drafts"
-    And "bob@domain.tld" creates a draft message "mDraft"
+    And "bob@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
     When "bob@domain.tld" sets flags "$Draft,$Seen" on message "mDraft"
     Then "bob@domain.tld" should see message "mDraft" with keywords $Draft,$Seen
 
   Scenario: A user can not remove a draft flag on a draft messages
     Given "bob@domain.tld" has a mailbox "Drafts"
-    And "bob@domain.tld" creates a draft message "mDraft"
+    And "bob@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
     When "bob@domain.tld" sets flags "$Seen" on message "mDraft"
     Then message "mDraft" is not updated
     And "bob@domain.tld" should see message "mDraft" with keywords $Draft
 
   Scenario: A user can destroy a draft
     Given "bob@domain.tld" has a mailbox "Drafts"
-    And "bob@domain.tld" creates a draft message "mDraft"
+    And "bob@domain.tld" creates a draft message "mDraft" in mailbox "Drafts"
     When "bob@domain.tld" destroys message "mDraft"
     Then "bob@domain.tld" ask for message "mDraft"
-    And the notFound list should contain "mDraft"
\ No newline at end of file
+    And the notFound list should contain "mDraft"
+
+  Scenario: Draft creation in outbox is not allowed
+    Given "bob@domain.tld" has a mailbox "Outbox"
+    When "bob@domain.tld" creates a draft message "mDraft" in mailbox "Outbox"
+    Then message "mDraft" is not created
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/ad893d02/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidDraftKeywordsException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidDraftKeywordsException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidDraftKeywordsException.java
index 08dd9b6..a9f7e2f 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidDraftKeywordsException.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidDraftKeywordsException.java
@@ -20,5 +20,7 @@
 package org.apache.james.jmap.exceptions;
 
 public class InvalidDraftKeywordsException extends IllegalArgumentException {
-
+    public InvalidDraftKeywordsException(String s) {
+        super(s);
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/ad893d02/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
index 4bb43dc..400834e 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesCreationProcessor.java
@@ -126,7 +126,7 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
                 SetError.builder()
                     .type("invalidProperties")
                     .properties(MessageProperty.keywords)
-                    .description("A draft message should be flagged as Draft")
+                    .description(e.getMessage())
                     .build());
 
         } catch (AttachmentsNotFoundException e) {
@@ -188,6 +188,7 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
 
     private void sendMailViaOutbox(CreationMessageEntry entry, Builder responseBuilder, MailboxSession session) throws AttachmentsNotFoundException, MailboxException, MessagingException {
         validateArguments(entry, session);
+        assertNoDraftKeywords(entry);
         MessageWithId created = handleOutboxMessages(entry, session);
         responseBuilder.created(created.getCreationId(), created.getValue());
     }
@@ -200,13 +201,22 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
     }
 
     private void assertDraftKeywords(CreationMessageEntry entry) {
-        Boolean isDraft = entry.getValue()
+        if (!isDraft(entry)) {
+            throw new InvalidDraftKeywordsException("A draft message should be flagged as Draft");
+        }
+    }
+
+    private void assertNoDraftKeywords(CreationMessageEntry entry) {
+        if (isDraft(entry)) {
+            throw new InvalidDraftKeywordsException("A draft message can not be created out of draft mailbox");
+        }
+    }
+
+    private Boolean isDraft(CreationMessageEntry entry) {
+        return entry.getValue()
             .getKeywords()
             .map(keywords -> keywords.contains(Keyword.DRAFT))
             .orElse(false);
-        if (!isDraft) {
-            throw new InvalidDraftKeywordsException();
-        }
     }
 
     private void validateArguments(CreationMessageEntry entry, MailboxSession session) throws MailboxInvalidMessageCreationException, AttachmentsNotFoundException, MailboxException {


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


[12/18] james-project git commit: JAMES-2220 remove useless comments in MailImpl

Posted by ad...@apache.org.
JAMES-2220 remove useless comments in MailImpl


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

Branch: refs/heads/master
Commit: 2133702412fe39e89480486e1560125bb46cfb64
Parents: 8d0a70d
Author: Matthieu Baechler <ma...@apache.org>
Authored: Tue Nov 14 15:48:34 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:31 2017 +0100

----------------------------------------------------------------------
 .../org/apache/james/server/core/MailImpl.java  | 90 --------------------
 1 file changed, 90 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/21337024/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index d146a07..a9e3df0 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -165,11 +165,6 @@ public class MailImpl implements Disposable, Mail {
         }
     }
 
-    /**
-     * @param mail
-     * @param newName
-     * @throws MessagingException
-     */
     @SuppressWarnings("unchecked")
     private MailImpl(Mail mail, String newName) throws MessagingException {
         this(newName, mail.getSender(), mail.getRecipients(), mail.getMessage());
@@ -218,11 +213,6 @@ public class MailImpl implements Disposable, Mail {
     /**
      * A constructor that creates a MailImpl with the specified name, sender,
      * recipients, and MimeMessage.
-     *
-     * @param name       the name of the MailImpl
-     * @param sender     the sender for this MailImpl
-     * @param recipients the collection of recipients of this MailImpl
-     * @param message    the MimeMessage associated with this MailImpl
      */
     public MailImpl(String name, MailAddress sender, Collection<MailAddress> recipients, MimeMessage message) {
         this(name, sender, recipients);
@@ -245,101 +235,51 @@ public class MailImpl implements Disposable, Mail {
         return null;
     }
 
-    /**
-     * Get the error message associated with this MailImpl.
-     *
-     * @return the error message associated with this MailImpl
-     */
     @Override
     public String getErrorMessage() {
         return errorMessage;
     }
 
-    /**
-     * Get the MimeMessage associated with this MailImpl.
-     *
-     * @return the MimeMessage associated with this MailImpl
-     */
     @Override
     public MimeMessage getMessage() throws MessagingException {
         return message;
     }
 
-    /**
-     * Set the name of this MailImpl.
-     *
-     * @param name the name of this MailImpl
-     */
     @Override
     public void setName(String name) {
         this.name = name;
     }
 
-    /**
-     * Get the name of this MailImpl.
-     *
-     * @return the name of this MailImpl
-     */
     @Override
     public String getName() {
         return name;
     }
 
-    /**
-     * Get the recipients of this MailImpl.
-     *
-     * @return the recipients of this MailImpl
-     */
     @Override
     public Collection<MailAddress> getRecipients() {
         return recipients;
     }
 
-    /**
-     * Get the sender of this MailImpl.
-     *
-     * @return the sender of this MailImpl
-     */
     @Override
     public MailAddress getSender() {
         return sender;
     }
 
-    /**
-     * Get the state of this MailImpl.
-     *
-     * @return the state of this MailImpl
-     */
     @Override
     public String getState() {
         return state;
     }
 
-    /**
-     * Get the remote host associated with this MailImpl.
-     *
-     * @return the remote host associated with this MailImpl
-     */
     @Override
     public String getRemoteHost() {
         return remoteHost;
     }
 
-    /**
-     * Get the remote address associated with this MailImpl.
-     *
-     * @return the remote address associated with this MailImpl
-     */
     @Override
     public String getRemoteAddr() {
         return remoteAddr;
     }
 
-    /**
-     * Get the last updated time for this MailImpl.
-     *
-     * @return the last updated time for this MailImpl
-     */
     @Override
     public Date getLastUpdated() {
         return lastUpdated;
@@ -364,11 +304,6 @@ public class MailImpl implements Disposable, Mail {
         return MimeMessageUtil.getMessageSize(message);
     }
 
-    /**
-     * Set the error message associated with this MailImpl.
-     *
-     * @param msg the new error message associated with this MailImpl
-     */
     @Override
     public void setErrorMessage(String msg) {
         this.errorMessage = msg;
@@ -402,47 +337,22 @@ public class MailImpl implements Disposable, Mail {
         this.recipients = ImmutableList.copyOf(recipients);
     }
 
-    /**
-     * Set the sender of this MailImpl.
-     *
-     * @param sender the sender of this MailImpl
-     */
     public void setSender(MailAddress sender) {
         this.sender = sender;
     }
 
-    /**
-     * Set the state of this MailImpl.
-     *
-     * @param state the state of this MailImpl
-     */
     public void setState(String state) {
         this.state = state;
     }
 
-    /**
-     * Set the remote address associated with this MailImpl.
-     *
-     * @param remoteHost the new remote host associated with this MailImpl
-     */
     public void setRemoteHost(String remoteHost) {
         this.remoteHost = remoteHost;
     }
 
-    /**
-     * Set the remote address associated with this MailImpl.
-     *
-     * @param remoteAddr the new remote address associated with this MailImpl
-     */
     public void setRemoteAddr(String remoteAddr) {
         this.remoteAddr = remoteAddr;
     }
 
-    /**
-     * Set the date this mail was last updated.
-     *
-     * @param lastUpdated the date the mail was last updated
-     */
     public void setLastUpdated(Date lastUpdated) {
         // Make a defensive copy to ensure that the date
         // doesn't get changed external to the class


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


[09/18] james-project git commit: JAMES-2220 cover MailImpl name generation with tests

Posted by ad...@apache.org.
JAMES-2220 cover MailImpl name generation with tests


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

Branch: refs/heads/master
Commit: c80bc05615e2917a72c0b62c49ffb090ed218f3c
Parents: 007ca71
Author: Matthieu Baechler <ma...@apache.org>
Authored: Tue Nov 14 16:49:26 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:31 2017 +0100

----------------------------------------------------------------------
 .../org/apache/james/server/core/MailImpl.java  | 79 ++++++++++----------
 .../apache/james/server/core/MailImplTest.java  | 50 +++++++++++++
 2 files changed, 89 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/c80bc056/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index fb0bf26..4ba435c 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -28,7 +28,6 @@ import java.io.ObjectOutputStream;
 import java.io.OptionalDataException;
 import java.io.OutputStream;
 import java.io.Serializable;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Date;
 import java.util.HashMap;
@@ -80,9 +79,47 @@ public class MailImpl implements Disposable, Mail {
      * @throws MessagingException when the message is not clonable
      */
     public static MailImpl duplicate(Mail mail) throws MessagingException {
-        return new MailImpl(mail, newName(mail));
+        return new MailImpl(mail, deriveNewName(mail.getName()));
     }
 
+    private static final java.util.Random random = new java.util.Random(); // Used
+    // to
+    // generate
+    // new
+    // mail
+    // names
+
+    /**
+     * Create a unique new primary key name for the given MailObject.
+     *
+     * @param currentName the mail to use as the basis for the new mail name
+     * @return a new name
+     */
+    @VisibleForTesting static String deriveNewName(String currentName) throws MessagingException {
+
+        // Checking if the original mail name is too long, perhaps because of a
+        // loop caused by a configuration error.
+        // it could cause a "null pointer exception" in AvalonMailRepository
+        // much
+        // harder to understand.
+        if (currentName.length() > 76) {
+            int count = 0;
+            int index = 0;
+            while ((index = currentName.indexOf('!', index + 1)) >= 0) {
+                count++;
+            }
+            // It looks like a configuration loop. It's better to stop.
+            if (count > 7) {
+                throw new MessagingException("Unable to create a new message name: too long." + " Possible loop in config.xml.");
+            } else {
+                currentName = currentName.substring(0, 76);
+            }
+        }
+
+        return currentName + "-!" + random.nextInt(1048576);
+    }
+
+
     private static final Logger LOGGER = LoggerFactory.getLogger(MailImpl.class);
 
     /**
@@ -532,44 +569,6 @@ public class MailImpl implements Disposable, Mail {
         return in.readObject();
     }
 
-    private static final java.util.Random random = new java.util.Random(); // Used
-    // to
-    // generate
-    // new
-    // mail
-    // names
-
-    /**
-     * Create a unique new primary key name for the given MailObject.
-     *
-     * @param mail the mail to use as the basis for the new mail name
-     * @return a new name
-     */
-    public static String newName(Mail mail) throws MessagingException {
-        String oldName = mail.getName();
-
-        // Checking if the original mail name is too long, perhaps because of a
-        // loop caused by a configuration error.
-        // it could cause a "null pointer exception" in AvalonMailRepository
-        // much
-        // harder to understand.
-        if (oldName.length() > 76) {
-            int count = 0;
-            int index = 0;
-            while ((index = oldName.indexOf('!', index + 1)) >= 0) {
-                count++;
-            }
-            // It looks like a configuration loop. It's better to stop.
-            if (count > 7) {
-                throw new MessagingException("Unable to create a new message name: too long." + " Possible loop in config.xml.");
-            } else {
-                oldName = oldName.substring(0, 76);
-            }
-        }
-
-        return oldName + "-!" + random.nextInt(1048576);
-    }
-
     /**
      * Generate a new identifier/name for a mail being processed by this server.
      *

http://git-wip-us.apache.org/repos/asf/james-project/blob/c80bc056/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java b/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
index f1beea2..e90e4d0 100644
--- a/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
+++ b/server/container/core/src/test/java/org/apache/james/server/core/MailImplTest.java
@@ -26,6 +26,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
 
 import javax.mail.MessagingException;
 import javax.mail.Session;
@@ -35,8 +36,10 @@ import org.apache.james.core.MailAddress;
 import org.apache.mailet.Mail;
 import org.apache.mailet.base.test.MailUtil;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
+import com.github.fge.lambdas.Throwing;
 import com.google.common.collect.ImmutableList;
 
 public class MailImplTest {
@@ -163,4 +166,51 @@ public class MailImplTest {
             .isInstanceOf(NullPointerException.class);
     }
 
+    @Test
+    public void deriveNewNameShouldThrowOnNull() {
+        assertThatThrownBy(() -> MailImpl.deriveNewName(null)).isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    public void deriveNewNameShouldGenerateNonEmptyStringOnEmpty() throws MessagingException {
+        assertThat(MailImpl.deriveNewName("")).isNotEmpty();
+    }
+
+    @Test
+    public void deriveNewNameShouldNeverGenerateMoreThan86Characters() throws MessagingException {
+        String longString = "mu1Eeseemu1Eeseemu1Eeseemu1Eeseemu1Eeseemu1Eeseemu1Eeseemu1Eeseemu1Eeseeseemu1Eesee";
+        assertThat(MailImpl.deriveNewName(longString).length()).isLessThan(86);
+    }
+
+    @Test
+    public void deriveNewNameShouldThrowWhenMoreThan8NestedCalls() throws MessagingException {
+        String called6Times = IntStream.range(0, 8)
+            .mapToObj(String::valueOf)
+            .reduce("average value ", Throwing.binaryOperator((left, right) -> MailImpl.deriveNewName(left)));
+        assertThatThrownBy(() -> MailImpl.deriveNewName(called6Times)).isInstanceOf(MessagingException.class);
+    }
+
+    @Test
+    @Ignore("short value doesn't trigger the assertion")
+    public void deriveNewNameShouldThrowWhenMoreThan8NestedCallsAndSmallInitialValue() throws MessagingException {
+        String called6Times = IntStream.range(0, 8)
+            .mapToObj(String::valueOf)
+            .reduce("small", Throwing.binaryOperator((left, right) -> MailImpl.deriveNewName(left)));
+        assertThatThrownBy(() -> MailImpl.deriveNewName(called6Times)).isInstanceOf(MessagingException.class);
+    }
+
+    @Test
+    @Ignore("truncation make impossible to detect 8 calls")
+    public void deriveNewNameShouldThrowWhenMoreThan8NestedCallsAndLongInitialValue() throws MessagingException {
+        String called6Times = IntStream.range(0, 8)
+            .mapToObj(String::valueOf)
+            .reduce("looooooonnnnnngggggggggggggggg", Throwing.binaryOperator((left, right) -> MailImpl.deriveNewName(left)));
+        assertThatThrownBy(() -> MailImpl.deriveNewName(called6Times)).isInstanceOf(MessagingException.class);
+    }
+
+
+    @Test
+    public void deriveNewNameShouldGenerateNotEqualsCurrentName() throws MessagingException {
+        assertThat(MailImpl.deriveNewName("current")).isNotEqualTo("current");
+    }
 }


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


[16/18] james-project git commit: JAMES-2220 be consistent when setting sender in MailImpl

Posted by ad...@apache.org.
JAMES-2220 be consistent when setting sender in MailImpl


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

Branch: refs/heads/master
Commit: 007ca713a377ed21a7e8c6bd1cd13d2acac5ac84
Parents: fe86766
Author: Matthieu Baechler <ma...@apache.org>
Authored: Tue Nov 14 15:50:10 2017 +0100
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Nov 16 12:30:31 2017 +0100

----------------------------------------------------------------------
 .../core/src/main/java/org/apache/james/server/core/MailImpl.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/007ca713/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
----------------------------------------------------------------------
diff --git a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
index 86f3919..fb0bf26 100644
--- a/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
+++ b/server/container/core/src/main/java/org/apache/james/server/core/MailImpl.java
@@ -157,7 +157,7 @@ public class MailImpl implements Disposable, Mail {
     public MailImpl(String name, MailAddress sender, Collection<MailAddress> recipients) {
         this();
         setName(name);
-        this.sender = sender;
+        setSender(sender);
 
         // Copy the recipient list
         if (recipients != null) {


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