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 2016/07/07 11:47:44 UTC

[1/4] james-project git commit: JAMES-1716 remove logic duplication to get a mailbox Id

Repository: james-project
Updated Branches:
  refs/heads/master 812b9129f -> d59881815


JAMES-1716 remove logic duplication to get a mailbox Id


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

Branch: refs/heads/master
Commit: fe2c10b48ee4f8cbaea7c315c688e8fec4b25c42
Parents: 192971e
Author: Matthieu Baechler <ma...@linagora.com>
Authored: Mon Jun 20 16:01:21 2016 +0200
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Jul 7 12:01:50 2016 +0200

----------------------------------------------------------------------
 .../methods/integration/SetMessagesMethodTest.java   | 15 +++------------
 1 file changed, 3 insertions(+), 12 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/fe2c10b4/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 20bf940..ff6ded2 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
@@ -752,10 +752,7 @@ public abstract class SetMessagesMethodTest {
     public void setMessagesShouldMoveMessageInSentWhenMessageIsSent() throws MailboxException {
         // Given
         jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "sent");
-        String sentMailboxId = getAllMailboxesIds(accessToken).stream()
-                .filter(x -> x.get("role").equals("sent"))
-                .map(x -> x.get("id"))
-                .findFirst().get();
+        String sentMailboxId = getMailboxId(accessToken, Role.SENT);
 
         String fromAddress = username;
         String messageCreationId = "user|inbox|1";
@@ -954,10 +951,7 @@ public abstract class SetMessagesMethodTest {
     @Test
     public void setMessagesShouldMoveToSentWhenSendingMessageWithOnlyFromAddress() {
         jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "sent");
-        String sentMailboxId = getAllMailboxesIds(accessToken).stream()
-                .filter(x -> x.get("role").equals("sent"))
-                .map(x -> x.get("id"))
-                .findFirst().get();
+        String sentMailboxId = getMailboxId(accessToken, Role.SENT);
 
         String messageCreationId = "user|inbox|1";
         String fromAddress = username;
@@ -1166,10 +1160,7 @@ public abstract class SetMessagesMethodTest {
     public void setMessagesShouldKeepBccInSentMailbox() throws Exception {
         // Sender
         jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, username, "sent");
-        String sentMailboxId = getAllMailboxesIds(accessToken).stream()
-                .filter(x -> x.get("role").equals("sent"))
-                .map(x -> x.get("id"))
-                .findFirst().get();
+        String sentMailboxId = getMailboxId(accessToken, Role.SENT);
 
         // Recipient
         String recipientAddress = "recipient" + "@" + USERS_DOMAIN;


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


[2/4] james-project git commit: JAMES-1716 getOutboxId should use an accesstoken

Posted by ad...@apache.org.
JAMES-1716 getOutboxId should use an accesstoken


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

Branch: refs/heads/master
Commit: 192971e578ddf81f7981cd95854b4af0e3062418
Parents: 812b912
Author: Matthieu Baechler <ma...@linagora.com>
Authored: Mon Jun 20 15:57:08 2016 +0200
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Jul 7 12:01:50 2016 +0200

----------------------------------------------------------------------
 .../integration/SetMessagesMethodTest.java      | 44 +++++++++++---------
 1 file changed, 25 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/192971e5/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 f6c18b1..20bf940 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
@@ -47,6 +47,7 @@ import javax.mail.Flags;
 import org.apache.james.GuiceJamesServer;
 import org.apache.james.jmap.JmapAuthentication;
 import org.apache.james.jmap.api.access.AccessToken;
+import org.apache.james.jmap.model.mailbox.Role;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.MailboxConstants;
 import org.apache.james.mailbox.model.MailboxPath;
@@ -116,14 +117,19 @@ public abstract class SetMessagesMethodTest {
         jmapServer.stop();
     }
     
-    private String getOutboxId() {
-        // Find username's outbox (using getMailboxes command on /jmap endpoint)
+    private String getOutboxId(AccessToken accessToken) {
+        return getMailboxId(accessToken, Role.OUTBOX);
+    }
+
+    private String getMailboxId(AccessToken accessToken, Role role) {
         return getAllMailboxesIds(accessToken).stream()
-                .filter(x -> x.get("role").equals("outbox"))
+                .filter(x -> x.get("role").equals(role.serialize()))
                 .map(x -> x.get("id"))
                 .findFirst().get();
     }
+    
 
+    
     private List<Map<String, String>> getAllMailboxesIds(AccessToken accessToken) {
         return with()
                 .header("Authorization", accessToken.serialize())
@@ -627,7 +633,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -678,7 +684,7 @@ public abstract class SetMessagesMethodTest {
             "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
             "        \"subject\": \"Thank you for joining example.com!\"," +
             "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-            "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+            "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
             "      }}" +
             "    }," +
             "    \"#0\"" +
@@ -705,7 +711,7 @@ public abstract class SetMessagesMethodTest {
         String presumedMessageId = "username@domain.tld|outbox|1";
         String fromAddress = username;
         String messageSubject = "Thank you for joining example.com!";
-        String outboxId = getOutboxId();
+        String outboxId = getOutboxId(accessToken);
         String requestBody = "[" +
                 "  [" +
                 "    \"setMessages\","+
@@ -763,7 +769,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
                 "        \"subject\": \"" + messageSubject + "\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -812,7 +818,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"cc\": [{ \"name\": \"ALICE\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -847,7 +853,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"cc\": [{ \"name\": \"ALICE\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -885,7 +891,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"cc\": [{ \"name\": \"ALICE\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -922,7 +928,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"cc\": [{ \"name\": \"ALICE\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"htmlBody\": \"Hello <i>someone</i>, and thank <b>you</b> for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -965,7 +971,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"cc\": [{ \"name\": \"ALICE\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -997,7 +1003,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
                 "        \"cc\": [{ \"name\": \"ALICE\"}]," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -1034,7 +1040,7 @@ public abstract class SetMessagesMethodTest {
             "        \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
             "        \"subject\": \"Thank you for joining example.com!\"," +
             "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-            "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+            "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
             "      }}" +
             "    }," +
             "    \"#0\"" +
@@ -1082,7 +1088,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"cc\": [{ \"name\": \"ALICE\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -1126,7 +1132,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"cc\": [{ \"name\": \"ALICE\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -1185,7 +1191,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"cc\": [{ \"name\": \"ALICE\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -1245,7 +1251,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"cc\": [{ \"name\": \"ALICE\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -1317,7 +1323,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"to\": [{ \"name\": \"BOB\", \"email\": \"" + recipientAddress + "\"}]," +
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"htmlBody\": \"Hello <b>someone</b>, and thank you for joining example.com!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +


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


[3/4] james-project git commit: JAMES-1716 Saving to draft should not send message

Posted by ad...@apache.org.
http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
index f28630b..7f32174 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/SetMessagesCreationProcessorTest.java
@@ -23,14 +23,17 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import java.time.ZonedDateTime;
 import java.util.Optional;
 import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
 
-import org.apache.james.jmap.exceptions.MailboxRoleNotFoundException;
+import org.apache.james.jmap.methods.ValueWithId.MessageWithId;
 import org.apache.james.jmap.model.CreationMessage;
 import org.apache.james.jmap.model.CreationMessage.DraftEmailer;
 import org.apache.james.jmap.model.CreationMessageId;
@@ -38,25 +41,34 @@ import org.apache.james.jmap.model.Message;
 import org.apache.james.jmap.model.MessageFactory;
 import org.apache.james.jmap.model.MessageId;
 import org.apache.james.jmap.model.MessagePreviewGenerator;
+import org.apache.james.jmap.model.MessageProperties.MessageProperty;
+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.mailbox.Role;
 import org.apache.james.jmap.send.MailFactory;
 import org.apache.james.jmap.send.MailMetadata;
 import org.apache.james.jmap.send.MailSpool;
 import org.apache.james.jmap.utils.HtmlTextExtractor;
 import org.apache.james.jmap.utils.MailboxBasedHtmlTextExtractor;
+import org.apache.james.jmap.utils.SystemMailboxesProvider;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.mock.MockMailboxSession;
+import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
 import org.apache.james.mailbox.store.TestId;
 import org.apache.james.mailbox.store.extractor.DefaultTextExtractor;
 import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox;
 import org.apache.mailet.Mail;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 
+import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 
@@ -64,11 +76,22 @@ public class SetMessagesCreationProcessorTest {
     
     private MessageFactory messageFactory;
 
-    private static final Message FAKE_MESSAGE = Message.builder()
-            .id(MessageId.of("user|outbox|1"))
+    private static final String USER = "user@example.com";
+    private static final String OUTBOX = "outbox";
+    private static final TestId OUTBOX_ID = TestId.of(12345);
+    private static final String DRAFTS = "drafts";
+    private static final TestId DRAFTS_ID = TestId.of(12);
+    private static final String OUTBOX_MESSAGE_ID = Joiner.on('|').join(USER, OUTBOX, "12345");
+    private static final String NAMESPACE = "#private";
+    private static final long UID_VALIDITY = 0l;
+    private final Mailbox outbox = new SimpleMailbox(new MailboxPath(NAMESPACE, USER, OUTBOX), UID_VALIDITY, OUTBOX_ID);
+    private final Mailbox drafts = new SimpleMailbox(new MailboxPath(NAMESPACE, USER, DRAFTS), UID_VALIDITY, DRAFTS_ID);
+
+    private static final Message FAKE_OUTBOX_MESSAGE = Message.builder()
+            .id(MessageId.of(OUTBOX_MESSAGE_ID))
             .blobId("anything")
             .threadId("anything")
-            .mailboxIds(ImmutableList.of("mailboxId"))
+            .mailboxId(OUTBOX_ID.serialize())
             .headers(ImmutableMap.of())
             .subject("anything")
             .size(0)
@@ -83,34 +106,60 @@ public class SetMessagesCreationProcessorTest {
         messageFactory = new MessageFactory(messagePreview);
     }
 
+    private final CreationMessage.Builder creationMessageBuilder = CreationMessage.builder()
+            .from(DraftEmailer.builder().name("alice").email("alice@example.com").build())
+            .to(ImmutableList.of(DraftEmailer.builder().name("bob").email("bob@example.com").build()))
+            .subject("Hey! ");
+
+    private final CreationMessageId creationMessageId = CreationMessageId.of("dlkja");
+    
+    private final SetMessagesRequest createMessageInOutbox = SetMessagesRequest.builder()
+            .create(
+                    creationMessageId, 
+                    creationMessageBuilder
+                        .mailboxId(OUTBOX_ID.serialize())
+                        .from(DraftEmailer.builder().name("user").email("user@example.com").build())
+                        .build())
+            .build();
+
+    private final Optional<Mailbox> optionalOutbox = Optional.of(outbox);
+    private final Optional<Mailbox> optionalDrafts = Optional.of(drafts);
+
+    private MessageMapper mockMapper;
+    private MailboxSessionMapperFactory stubSessionMapperFactory;
+    private MailSpool mockedMailSpool;
+    private MailFactory mockedMailFactory;
+    private SystemMailboxesProvider fakeSystemMailboxesProvider;
+    private MockMailboxSession session;
+    private MIMEMessageConverter mimeMessageConverter;
+
+    @Before
+    public void setUp() throws MailboxException {
+        mockMapper = mock(MessageMapper.class);
+        stubSessionMapperFactory = mock(MailboxSessionMapperFactory.class);
+        when(stubSessionMapperFactory.createMessageMapper(any(MailboxSession.class)))
+                .thenReturn(mockMapper);
+        mockedMailSpool = mock(MailSpool.class);
+        mockedMailFactory = mock(MailFactory.class);
+
+        fakeSystemMailboxesProvider = new TestSystemMailboxesProvider(() -> optionalOutbox, () -> optionalDrafts);
+        session = new MockMailboxSession(USER);
+        mimeMessageConverter = new MIMEMessageConverter();
+    }
+
     @Test
     public void processShouldReturnEmptyCreatedWhenRequestHasEmptyCreate() {
-        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null, null, null, null, null, messageFactory) {
-            @Override
-            protected Optional<Mailbox> getOutbox(MailboxSession session) throws MailboxException {
-				Mailbox fakeOutbox = (Mailbox) mock(Mailbox.class);
-                when(fakeOutbox.getName()).thenReturn("outbox");
-                return Optional.of(fakeOutbox);
-            }
-        };
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(
+                stubSessionMapperFactory, mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, fakeSystemMailboxesProvider);
+
         SetMessagesRequest requestWithEmptyCreate = SetMessagesRequest.builder().build();
 
-        SetMessagesResponse result = sut.process(requestWithEmptyCreate, buildStubbedSession());
+        SetMessagesResponse result = sut.process(requestWithEmptyCreate, session);
 
         assertThat(result.getCreated()).isEmpty();
         assertThat(result.getNotCreated()).isEmpty();
     }
 
-    private MailboxSession buildStubbedSession() {
-        MailboxSession.User stubUser = mock(MailboxSession.User.class);
-        when(stubUser.getUserName()).thenReturn("alice@example.com");
-        MailboxSession stubSession = mock(MailboxSession.class);
-        when(stubSession.getPathDelimiter()).thenReturn('.');
-        when(stubSession.getUser()).thenReturn(stubUser);
-        when(stubSession.getPersonalSpace()).thenReturn("#private");
-        return stubSession;
-    }
-
     @Test
     public void processShouldReturnNonEmptyCreatedWhenRequestHasNonEmptyCreate() throws MailboxException {
         // Given
@@ -119,106 +168,139 @@ public class SetMessagesCreationProcessorTest {
         when(mockSessionMapperFactory.createMessageMapper(any(MailboxSession.class)))
                 .thenReturn(stubMapper);
 
-        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null, mockSessionMapperFactory, null, null, null, messageFactory) {
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(
+                mockSessionMapperFactory, mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, fakeSystemMailboxesProvider) {
             @Override
-            protected MessageWithId<Message> createMessageInOutboxAndSend(MessageWithId.CreationMessageEntry createdEntry, MailboxSession session, Mailbox outbox, Function<Long, MessageId> buildMessageIdFromUid) {
-                return new MessageWithId<>(createdEntry.getCreationId(), FAKE_MESSAGE);
-            }
-            @Override
-            protected Optional<Mailbox> getOutbox(MailboxSession session) throws MailboxException {
-                Mailbox fakeOutbox = mock(Mailbox.class);
-                when(fakeOutbox.getName()).thenReturn("outbox");
-                return Optional.of(fakeOutbox);
+            protected MessageWithId createMessageInOutboxAndSend(ValueWithId.CreationMessageEntry createdEntry, MailboxSession session, Mailbox outbox, Function<Long, MessageId> buildMessageIdFromUid) {
+                return new MessageWithId(createdEntry.getCreationId(), FAKE_OUTBOX_MESSAGE);
             }
         };
         // When
-        SetMessagesResponse result = sut.process(buildFakeCreationRequest(), buildStubbedSession());
+        SetMessagesResponse result = sut.process(createMessageInOutbox, session);
 
         // Then
         assertThat(result.getCreated()).isNotEmpty();
         assertThat(result.getNotCreated()).isEmpty();
     }
 
-    @Test(expected = MailboxRoleNotFoundException.class)
-    public void processShouldThrowWhenOutboxNotFound() {
+    @Ignore("JAMES-1716 : should report an error")
+    @Test
+    public void processShouldReturnErrorWhenOutboxNotFound() {
         // Given
-        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null, null, null, null, null, messageFactory) {
-            @Override
-            protected Optional<Mailbox> getOutbox(MailboxSession session) throws MailboxException {
-                return Optional.empty();
-            }
-        };
+        TestSystemMailboxesProvider doNotProvideOutbox = new TestSystemMailboxesProvider(Optional::empty, () -> optionalDrafts);
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(
+                stubSessionMapperFactory, mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, doNotProvideOutbox);
         // When
-        sut.process(buildFakeCreationRequest(), null);
+        SetMessagesResponse actual = sut.process(createMessageInOutbox, session);
+        
+        assertThat(actual.getNotCreated()).hasSize(1).containsKey(creationMessageId);
+        assertThat(actual.getNotCreated().get(creationMessageId).getType()).isEqualTo("invalidProperties");
+        assertThat(actual.getNotCreated().get(creationMessageId).getDescription()).contains("target mailbox does not exists");
     }
 
     @Test
     public void processShouldCallMessageMapperWhenRequestHasNonEmptyCreate() throws MailboxException {
         // Given
-        Mailbox fakeOutbox = mock(Mailbox.class);
-        MessageMapper mockMapper = mock(MessageMapper.class);
-        MailboxSessionMapperFactory stubSessionMapperFactory = mock(MailboxSessionMapperFactory.class);
-        when(stubSessionMapperFactory.createMessageMapper(any(MailboxSession.class)))
-                .thenReturn(mockMapper);
-        MailSpool mockedMailSpool = mock(MailSpool.class);
-        MailFactory mockedMailFactory = mock(MailFactory.class);
-
-        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null,
-                stubSessionMapperFactory, new MIMEMessageConverter(), mockedMailSpool, mockedMailFactory, messageFactory) {
-            @Override
-            protected Optional<Mailbox> getOutbox(MailboxSession session) throws MailboxException {
-                TestId stubMailboxId = mock(TestId.class);
-                when(stubMailboxId.serialize()).thenReturn("user|outbox|12345");
-                when(fakeOutbox.getMailboxId()).thenReturn(stubMailboxId);
-                when(fakeOutbox.getName()).thenReturn("outbox");
-                return Optional.of(fakeOutbox);
-            }
-        };
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(
+                stubSessionMapperFactory, mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, fakeSystemMailboxesProvider);
         // When
-        sut.process(buildFakeCreationRequest(), buildStubbedSession());
+        sut.process(createMessageInOutbox, session);
 
         // Then
-        verify(mockMapper).add(eq(fakeOutbox), any(MailboxMessage.class));
+        verify(mockMapper).add(eq(outbox), any(MailboxMessage.class));
     }
 
     @Test
     public void processShouldSendMailWhenRequestHasNonEmptyCreate() throws Exception {
         // Given
-        Mailbox fakeOutbox = mock(Mailbox.class);
-        MessageMapper mockMapper = mock(MessageMapper.class);
-        MailboxSessionMapperFactory stubSessionMapperFactory = mock(MailboxSessionMapperFactory.class);
-        when(stubSessionMapperFactory.createMessageMapper(any(MailboxSession.class)))
-                .thenReturn(mockMapper);
-        MailSpool mockedMailSpool = mock(MailSpool.class);
-        MailFactory mockedMailFactory = mock(MailFactory.class);
-
-        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(null, null,
-                stubSessionMapperFactory, new MIMEMessageConverter(), mockedMailSpool, mockedMailFactory, messageFactory) {
-            @Override
-            protected Optional<Mailbox> getOutbox(MailboxSession session) throws MailboxException {
-                TestId stubMailboxId = mock(TestId.class);
-                when(stubMailboxId.serialize()).thenReturn("user|outbox|12345");
-                when(fakeOutbox.getMailboxId()).thenReturn(stubMailboxId);
-                when(fakeOutbox.getName()).thenReturn("outbox");
-                return Optional.of(fakeOutbox);
-            }
-        };
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(
+                stubSessionMapperFactory, mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, fakeSystemMailboxesProvider);
         // When
-        sut.process(buildFakeCreationRequest(), buildStubbedSession());
+        sut.process(createMessageInOutbox, session);
 
         // Then
         verify(mockedMailSpool).send(any(Mail.class), any(MailMetadata.class));
     }
 
-    private SetMessagesRequest buildFakeCreationRequest() {
-        return SetMessagesRequest.builder()
-                .create(ImmutableMap.of(CreationMessageId.of("anything-really"), CreationMessage.builder()
-                    .from(DraftEmailer.builder().name("alice").email("alice@example.com").build())
-                    .to(ImmutableList.of(DraftEmailer.builder().name("bob").email("bob@example.com").build()))
-                    .subject("Hey! ")
-                    .mailboxIds(ImmutableList.of("mailboxId"))
-                    .build()
-                ))
+    @Test
+    public void processShouldNotSpoolMailWhenNotSavingToOutbox() throws Exception {
+        // Given
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(
+                stubSessionMapperFactory, mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, fakeSystemMailboxesProvider);
+        // When
+        SetMessagesRequest notInOutboxCreationRequest =
+                SetMessagesRequest.builder()
+                    .create(CreationMessageId.of("anything-really"),
+                            creationMessageBuilder.mailboxId("any-id-but-outbox-id")
+                        .build())
+                    .build();
+
+        sut.process(notInOutboxCreationRequest, session);
+
+        // Then
+        verify(mockedMailSpool, never()).send(any(Mail.class), any(MailMetadata.class));
+    }
+
+    @Test
+    public void processShouldReturnNotImplementedErrorWhenSavingToDrafts() {
+        // Given
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(
+                stubSessionMapperFactory, mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, fakeSystemMailboxesProvider);
+
+        CreationMessageId creationMessageId = CreationMessageId.of("anything-really");
+        SetMessagesRequest createMessageInDrafts = SetMessagesRequest.builder()
+                .create(
+                        creationMessageId, creationMessageBuilder.mailboxId(DRAFTS_ID.serialize()).build())
+                .build();
+
+        // When
+        SetMessagesResponse actual = sut.process(createMessageInDrafts, session);
+
+        // Then
+        assertThat(actual.getNotCreated()).hasSize(1).containsEntry(creationMessageId, SetError.builder()
+                .type("invalidProperties")
+                .properties(MessageProperty.mailboxIds)
+                .description("Not yet implemented")
+                .build());
+    }
+
+    @Test
+    public void processShouldNotSendWhenSavingToDrafts() throws Exception {
+        // Given
+        SetMessagesCreationProcessor sut = new SetMessagesCreationProcessor(
+                stubSessionMapperFactory, mimeMessageConverter, mockedMailSpool, mockedMailFactory, messageFactory, fakeSystemMailboxesProvider);
+        // When
+        CreationMessageId creationMessageId = CreationMessageId.of("anything-really");
+        SetMessagesRequest createMessageInDrafts = SetMessagesRequest.builder()
+                .create(
+                        creationMessageId, creationMessageBuilder.mailboxId(DRAFTS_ID.serialize()).build())
                 .build();
+        sut.process(createMessageInDrafts, session);
+
+        // Then
+        verify(mockedMailSpool, never()).send(any(Mail.class), any(MailMetadata.class));
     }
-}
\ No newline at end of file
+
+
+    public static class TestSystemMailboxesProvider implements SystemMailboxesProvider {
+
+        private final Supplier<Optional<Mailbox>> outboxSupplier;
+        private final Supplier<Optional<Mailbox>> draftsSupplier;
+
+        private TestSystemMailboxesProvider(Supplier<Optional<Mailbox>> outboxSupplier,
+                                            Supplier<Optional<Mailbox>> draftsSupplier) {
+            this.outboxSupplier = outboxSupplier;
+            this.draftsSupplier = draftsSupplier;
+        }
+
+        public Stream<Mailbox> listMailboxes(Role aRole, MailboxSession session) {
+            if (aRole.equals(Role.OUTBOX)) {
+                return outboxSupplier.get().map(o -> Stream.of(o)).orElse(Stream.empty());
+            } else if (aRole.equals(Role.DRAFTS)) {
+                return draftsSupplier.get().map(d -> Stream.of(d)).orElse(Stream.empty());
+            }
+            return Stream.empty();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetErrorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetErrorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetErrorTest.java
index db11c27..24e873f 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetErrorTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetErrorTest.java
@@ -24,6 +24,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.apache.james.jmap.model.MessageProperties.MessageProperty;
 
 import java.util.Optional;
+import java.util.Set;
 
 import org.junit.Test;
 import com.google.common.collect.ImmutableSet;
@@ -85,7 +86,7 @@ public class SetErrorTest {
     @Test
     public void buildShouldDefaultToEmptyWhenPropertiesNull() {
         SetError result = SetError.builder()
-                .type("a type").description("a description").properties(null)
+                .type("a type").description("a description").properties((Set<MessageProperty>)null)
                 .build();
 
         assertThat(result.getProperties()).isPresent();
@@ -98,10 +99,33 @@ public class SetErrorTest {
         SetError result = SetError.builder()
                 .type("a type").description("a description")
                 .properties(nonNullProperty)
-                .properties(null)
+                .properties((Set<MessageProperty>)null)
                 .build();
 
         assertThat(result.getProperties()).isPresent();
         assertThat(result.getProperties().get()).isEqualTo(nonNullProperty);
     }
+
+    public void buildShouldDefaultToEmptyWhenPropertiesWithNoArgument() {
+        SetError result = SetError.builder()
+                .type("a type").description("a description").properties()
+                .build();
+
+        assertThat(result.getProperties()).isPresent();
+        assertThat(result.getProperties().get()).isEmpty();
+    }
+
+    @Test
+    public void buildShouldBeIdempotentWhenPropertiesWithNoArgument() {
+        ImmutableSet<MessageProperty> nonNullProperty = ImmutableSet.of(MessageProperty.from);
+        SetError result = SetError.builder()
+                .type("a type").description("a description")
+                .properties(nonNullProperty)
+                .properties()
+                .build();
+
+        assertThat(result.getProperties()).isPresent();
+        assertThat(result.getProperties().get()).isEqualTo(nonNullProperty);
+    }
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMessagesRequestTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMessagesRequestTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMessagesRequestTest.java
index 7be03e1..907b76f 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMessagesRequestTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/SetMessagesRequestTest.java
@@ -48,7 +48,7 @@ public class SetMessagesRequestTest {
     public void builderShouldWork() {
         ImmutableList<MessageId> destroy = ImmutableList.of(MessageId.of("user|destroy|1"));
 
-        SetMessagesRequest expected = new SetMessagesRequest(Optional.empty(), Optional.empty(), ImmutableMap.of(), ImmutableMap.of(), destroy);
+        SetMessagesRequest expected = new SetMessagesRequest(Optional.empty(), Optional.empty(), ImmutableList.of(), ImmutableMap.of(), destroy);
 
         SetMessagesRequest setMessagesRequest = SetMessagesRequest.builder()
             .accountId(null)

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/PostDequeueDecoratorTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/PostDequeueDecoratorTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/PostDequeueDecoratorTest.java
index 7e72b64..223e426 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/PostDequeueDecoratorTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/send/PostDequeueDecoratorTest.java
@@ -29,9 +29,9 @@ import java.util.Iterator;
 
 import javax.mail.Flags;
 
+import org.apache.james.jmap.exceptions.MailboxRoleNotFoundException;
 import org.apache.james.jmap.model.MessageId;
 import org.apache.james.jmap.send.exception.MailShouldBeInOutboxException;
-import org.apache.james.jmap.send.exception.MailboxRoleNotFoundException;
 import org.apache.james.jmap.send.exception.MessageIdNotFoundException;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/test/java/org/apache/james/jmap/utils/SystemMailboxesProviderImplTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/utils/SystemMailboxesProviderImplTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/utils/SystemMailboxesProviderImplTest.java
new file mode 100644
index 0000000..3ee868d
--- /dev/null
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/utils/SystemMailboxesProviderImplTest.java
@@ -0,0 +1,33 @@
+/****************************************************************
+ * 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.utils;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class SystemMailboxesProviderImplTest {
+
+    @Ignore("1716 this class needs a test suite")
+    @Test
+    public void missingTestSuite() {
+        //TODO this class needs a test suite
+    }
+    
+}


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


[4/4] james-project git commit: JAMES-1716 Saving to draft should not send message

Posted by ad...@apache.org.
JAMES-1716 Saving to draft should not send message

providing both an outside integration test and some unit tests (SetMessageCreationProcessorTest)


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

Branch: refs/heads/master
Commit: d59881815c9c5fcee73addb2a2562c28f3491e16
Parents: fe2c10b
Author: Matthieu Baechler <ma...@linagora.com>
Authored: Mon Jul 4 16:36:05 2016 +0200
Committer: Antoine Duprat <ad...@linagora.com>
Committed: Thu Jul 7 12:04:36 2016 +0200

----------------------------------------------------------------------
 .../store/mail/model/impl/SimpleMailbox.java    |   9 +-
 .../java/org/apache/james/jmap/JMAPModule.java  |   5 +-
 .../integration/SetMessagesMethodTest.java      | 110 +++++++-
 .../java/org/apache/james/jmap/JMAPServlet.java |   4 +
 .../jmap/methods/MIMEMessageConverter.java      |  14 +-
 .../MailboxInvalidMessageCreationException.java |  26 ++
 .../methods/MailboxNotImplementedException.java |  30 ++
 .../MailboxSendingNotAllowedException.java      |  38 +++
 .../james/jmap/methods/MessageWithId.java       |  49 ----
 .../methods/SetMessagesCreationProcessor.java   | 275 ++++++++++---------
 .../apache/james/jmap/methods/ValueWithId.java  |  63 +++++
 .../james/jmap/model/CreationMessage.java       |  17 +-
 .../org/apache/james/jmap/model/Message.java    |   8 +-
 .../org/apache/james/jmap/model/SetError.java   |  14 +
 .../james/jmap/model/SetMessagesRequest.java    |  30 +-
 .../james/jmap/model/SetMessagesResponse.java   |  14 +
 .../james/jmap/send/PostDequeueDecorator.java   |   2 +-
 .../exception/MailboxRoleNotFoundException.java |  30 --
 .../jmap/utils/SystemMailboxesProvider.java     |  30 ++
 .../jmap/utils/SystemMailboxesProviderImpl.java |  70 +++++
 .../jmap/methods/MIMEMessageConverterTest.java  |  25 +-
 .../SetMessagesCreationProcessorTest.java       | 268 +++++++++++-------
 .../apache/james/jmap/model/SetErrorTest.java   |  28 +-
 .../jmap/model/SetMessagesRequestTest.java      |   2 +-
 .../jmap/send/PostDequeueDecoratorTest.java     |   2 +-
 .../utils/SystemMailboxesProviderImplTest.java  |  33 +++
 26 files changed, 848 insertions(+), 348 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailbox.java
----------------------------------------------------------------------
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailbox.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailbox.java
index 05c1ec1..7d70bfd 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailbox.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailbox.java
@@ -33,13 +33,18 @@ public class SimpleMailbox implements Mailbox {
     private final long uidValidity;
     private MailboxACL acl = SimpleMailboxACL.EMPTY;
 
-    public SimpleMailbox(MailboxPath path, long uidValidity) {
+    public SimpleMailbox(MailboxPath path, long uidValidity, MailboxId mailboxId) {
+        this.id = mailboxId;
         this.namespace = path.getNamespace();
         this.user = path.getUser();
         this.name = path.getName();
         this.uidValidity = uidValidity;
     }
-    
+
+    public SimpleMailbox(MailboxPath path, long uidValidity) {
+        this(path, uidValidity, null);
+    }
+
     public SimpleMailbox(Mailbox mailbox) {
         this.id = mailbox.getMailboxId();
         this.namespace = mailbox.getNamespace();

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPModule.java b/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPModule.java
index 6fd9c1f..247c70d 100644
--- a/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPModule.java
+++ b/server/container/guice/guice-common/src/main/java/org/apache/james/jmap/JMAPModule.java
@@ -30,6 +30,8 @@ import org.apache.james.jmap.mailet.VacationMailet;
 import org.apache.james.jmap.methods.RequestHandler;
 import org.apache.james.jmap.utils.HtmlTextExtractor;
 import org.apache.james.jmap.utils.MailboxBasedHtmlTextExtractor;
+import org.apache.james.jmap.utils.SystemMailboxesProvider;
+import org.apache.james.jmap.utils.SystemMailboxesProviderImpl;
 import org.apache.james.lifecycle.api.Configurable;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailetcontainer.impl.MatcherMailetPair;
@@ -64,11 +66,12 @@ public class JMAPModule extends AbstractModule {
 
         bind(HtmlTextExtractor.class).to(MailboxBasedHtmlTextExtractor.class);
         Multibinder.newSetBinder(binder(), ConfigurationPerformer.class).addBinding().to(RequiredCapabilitiesPrecondition.class);
-        Multibinder.newSetBinder(binder(), ConfigurationPerformer.class).addBinding().to(RequiredCapabilitiesPrecondition.class);
 
         Multibinder<CamelMailetContainerModule.TransportProcessorCheck> transportProcessorChecks = Multibinder.newSetBinder(binder(), CamelMailetContainerModule.TransportProcessorCheck.class);
         transportProcessorChecks.addBinding().to(VacationMailetCheck.class);
         transportProcessorChecks.addBinding().to(BccMailetCheck.class);
+        
+        bind(SystemMailboxesProvider.class).to(SystemMailboxesProviderImpl.class);
     }
 
     @Provides

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/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 ff6ded2..af96309 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
@@ -23,6 +23,7 @@ import static com.jayway.restassured.RestAssured.given;
 import static com.jayway.restassured.RestAssured.with;
 import static com.jayway.restassured.config.EncoderConfig.encoderConfig;
 import static com.jayway.restassured.config.RestAssuredConfig.newConfig;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.endsWith;
@@ -128,8 +129,6 @@ public abstract class SetMessagesMethodTest {
                 .findFirst().get();
     }
     
-
-    
     private List<Map<String, String>> getAllMailboxesIds(AccessToken accessToken) {
         return with()
                 .header("Authorization", accessToken.serialize())
@@ -1054,7 +1053,7 @@ public abstract class SetMessagesMethodTest {
             .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].type", equalTo("invalidProperties"))
             .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", hasSize(1))
             .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", contains("from"))
-            .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].description", endsWith("Invalid 'from' field. Must be one of [username@domain.tld]"))
+            .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].description", endsWith("Invalid 'from' field. Must be one of username@domain.tld"))
             .body(ARGUMENTS + ".created", aMapWithSize(0));
     }
 
@@ -1282,10 +1281,10 @@ public abstract class SetMessagesMethodTest {
             .then()
                     .statusCode(200)
                     .body(NAME, equalTo("messageList"))
-                    .body(ARGUMENTS + ".messageIds", hasSize(1))
-            ;
+                    .body(ARGUMENTS + ".messageIds", hasSize(1));
             return true;
-        } catch(AssertionError e) {
+            
+        } catch (AssertionError e) {
             return false;
         }
     }
@@ -1333,6 +1332,101 @@ public abstract class SetMessagesMethodTest {
         calmlyAwait.atMost(30, TimeUnit.SECONDS).until( () -> isHtmlMessageReceived(recipientToken));
     }
 
+
+    @Test
+    public void setMessagesWhenSavingToDraftsShouldNotSendMessage() throws Exception {
+        String sender = username;
+        jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, sender, "sent");
+        jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, sender, "drafts");
+        String recipientAddress = "recipient" + "@" + USERS_DOMAIN;
+        String recipientPassword = "password";
+        jmapServer.serverProbe().addUser(recipientAddress, recipientPassword);
+        jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, recipientAddress, "inbox");
+        await();
+        AccessToken recipientToken = JmapAuthentication.authenticateJamesUser(recipientAddress, recipientPassword);
+
+        String senderDraftsMailboxId = getMailboxId(accessToken, Role.DRAFTS);
+
+        String messageCreationId = "creationId";
+        String fromAddress = username;
+        String requestBody = "[" +
+                "  [" +
+                "    \"setMessages\","+
+                "    {" +
+                "      \"create\": { \"" + messageCreationId  + "\" : {" +
+                "        \"from\": { \"email\": \"" + fromAddress + "\"}," +
+                "        \"to\": [{ \"name\": \"BOB\", \"email\": \"" + recipientAddress + "\"}]," +
+                "        \"cc\": [{ \"name\": \"ALICE\"}]," +
+                "        \"subject\": \"Thank you for joining example.com!\"," +
+                "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
+                "        \"mailboxIds\": [\"" + senderDraftsMailboxId + "\"], " +
+                "        \"isDraft\": false" +
+                "      }}" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        given()
+            .header("Authorization", this.accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200);
+
+        //We need to wait for an async event to not happen, we couldn't found any
+        //robust way to check that.
+        Thread.sleep(TimeUnit.SECONDS.toMillis(5));
+        assertThat(isAnyMessageFoundInRecipientsMailboxes(recipientToken)).isFalse();
+    }
+
+    @Test
+    public void setMessagesWhenSavingToRegularMailboxShouldNotSendMessage() throws Exception {
+        String sender = username;
+        jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, sender, "sent");
+        jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, sender, "drafts");
+        jmapServer.serverProbe().createMailbox(MailboxConstants.USER_NAMESPACE, sender, "regular");
+        Mailbox regularMailbox = jmapServer.serverProbe().getMailbox(MailboxConstants.USER_NAMESPACE, sender, "regular");
+        String recipientAddress = "recipient" + "@" + USERS_DOMAIN;
+        String recipientPassword = "password";
+        jmapServer.serverProbe().addUser(recipientAddress, recipientPassword);
+        await();
+
+        String messageCreationId = "creationId";
+        String fromAddress = username;
+        String requestBody = "[" +
+                "  [" +
+                "    \"setMessages\","+
+                "    {" +
+                "      \"create\": { \"" + messageCreationId  + "\" : {" +
+                "        \"from\": { \"email\": \"" + fromAddress + "\"}," +
+                "        \"to\": [{ \"name\": \"BOB\", \"email\": \"" + recipientAddress + "\"}]," +
+                "        \"cc\": [{ \"name\": \"ALICE\"}]," +
+                "        \"subject\": \"Thank you for joining example.com!\"," +
+                "        \"textBody\": \"Hello someone, and thank you for joining example.com!\"," +
+                "        \"mailboxIds\": [\"" + regularMailbox.getMailboxId().serialize() + "\"]" +
+                "      }}" +
+                "    }," +
+                "    \"#0\"" +
+                "  ]" +
+                "]";
+
+        String notCreatedMessage = ARGUMENTS + ".notCreated[\""+messageCreationId+"\"]";
+        given()
+            .header("Authorization", this.accessToken.serialize())
+            .body(requestBody)
+        .when()
+            .post("/jmap")
+        .then()
+            .statusCode(200)
+            .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+            .body(notCreatedMessage + ".type", equalTo("invalidProperties"))
+            .body(notCreatedMessage + ".description", equalTo("Not yet implemented"))
+            .body(ARGUMENTS + ".created", aMapWithSize(0));
+    }
+
+    
     private boolean isHtmlMessageReceived(AccessToken recipientToken) {
         try {
             with()
@@ -1375,7 +1469,7 @@ public abstract class SetMessagesMethodTest {
                 "        \"subject\": \"Thank you for joining example.com!\"," +
                 "        \"htmlBody\": \"Hello <b>someone</b>, and thank you for joining example.com!\"," +
                 "        \"textBody\": \"Hello someone, and thank you for joining example.com, text version!\"," +
-                "        \"mailboxIds\": [\"" + getOutboxId() + "\"]" +
+                "        \"mailboxIds\": [\"" + getOutboxId(accessToken) + "\"]" +
                 "      }}" +
                 "    }," +
                 "    \"#0\"" +
@@ -1439,8 +1533,6 @@ public abstract class SetMessagesMethodTest {
                 "]";
 
         given()
-                .accept(ContentType.JSON)
-                .contentType(ContentType.JSON)
                 .header("Authorization", this.accessToken.serialize())
                 .body(requestBody)
         .when()

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServlet.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServlet.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServlet.java
index 17827d4..832a940 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServlet.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServlet.java
@@ -36,6 +36,8 @@ import org.apache.james.jmap.methods.RequestHandler;
 import org.apache.james.jmap.model.AuthenticatedProtocolRequest;
 import org.apache.james.jmap.model.ProtocolRequest;
 import org.apache.james.jmap.model.ProtocolResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.JsonMappingException;
@@ -44,6 +46,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 
 public class JMAPServlet extends HttpServlet {
 
+    public static final Logger LOG = LoggerFactory.getLogger(JMAPServlet.class);
     public static final String JSON_CONTENT_TYPE = "application/json";
     public static final String JSON_CONTENT_TYPE_UTF8 = "application/json; charset=UTF-8";
 
@@ -70,6 +73,7 @@ public class JMAPServlet extends HttpServlet {
             resp.setContentType(JSON_CONTENT_TYPE);
             objectMapper.writeValue(resp.getOutputStream(), responses);
         } catch (IOException e) {
+            LOG.error("error handling request", e);
             resp.setStatus(SC_BAD_REQUEST);
         }
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
index 19e7c63..25ba906 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MIMEMessageConverter.java
@@ -68,7 +68,7 @@ public class MIMEMessageConverter {
         this.bodyFactory = new BasicBodyFactory();
     }
 
-    public byte[] convert(MessageWithId.CreationMessageEntry creationMessageEntry) {
+    public byte[] convert(ValueWithId.CreationMessageEntry creationMessageEntry) {
 
         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
         DefaultMessageWriter writer = new DefaultMessageWriter();
@@ -80,18 +80,18 @@ public class MIMEMessageConverter {
         return buffer.toByteArray();
     }
 
-    @VisibleForTesting Message convertToMime(MessageWithId.CreationMessageEntry creationMessageEntry) {
-        if (creationMessageEntry == null || creationMessageEntry.getMessage() == null) {
+    @VisibleForTesting Message convertToMime(ValueWithId.CreationMessageEntry creationMessageEntry) {
+        if (creationMessageEntry == null || creationMessageEntry.getValue() == null) {
             throw new IllegalArgumentException("creationMessageEntry is either null or has null message");
         }
 
         MessageBuilder messageBuilder = MessageBuilder.create();
-        if (mixedTextAndHtml(creationMessageEntry.getMessage())) {
-            messageBuilder.setBody(createMultipartBody(creationMessageEntry.getMessage()));
+        if (mixedTextAndHtml(creationMessageEntry.getValue())) {
+            messageBuilder.setBody(createMultipartBody(creationMessageEntry.getValue()));
         } else {
-            messageBuilder.setBody(createTextBody(creationMessageEntry.getMessage()));
+            messageBuilder.setBody(createTextBody(creationMessageEntry.getValue()));
         }
-        buildMimeHeaders(messageBuilder, creationMessageEntry.getCreationId(), creationMessageEntry.getMessage());
+        buildMimeHeaders(messageBuilder, creationMessageEntry.getCreationId(), creationMessageEntry.getValue());
         return messageBuilder.build();
     }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxInvalidMessageCreationException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxInvalidMessageCreationException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxInvalidMessageCreationException.java
new file mode 100644
index 0000000..3bcb1be
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxInvalidMessageCreationException.java
@@ -0,0 +1,26 @@
+/****************************************************************
+ * 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;
+
+import org.apache.james.mailbox.exception.MailboxException;
+
+public class MailboxInvalidMessageCreationException extends MailboxException {
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxNotImplementedException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxNotImplementedException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxNotImplementedException.java
new file mode 100644
index 0000000..a7da6d4
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxNotImplementedException.java
@@ -0,0 +1,30 @@
+/****************************************************************
+ * 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;
+
+import org.apache.james.mailbox.exception.MailboxException;
+
+public class MailboxNotImplementedException extends MailboxException {
+
+    public MailboxNotImplementedException(String message) {
+        super(message);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxSendingNotAllowedException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxSendingNotAllowedException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxSendingNotAllowedException.java
new file mode 100644
index 0000000..49d9141
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MailboxSendingNotAllowedException.java
@@ -0,0 +1,38 @@
+/****************************************************************
+ * 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;
+
+import java.util.Collection;
+
+import org.apache.james.mailbox.exception.MailboxException;
+
+public class MailboxSendingNotAllowedException extends MailboxException {
+
+    private Collection<String> allowedFroms;
+
+    public MailboxSendingNotAllowedException(Collection<String> allowedFroms) {
+        super();
+        this.allowedFroms = allowedFroms;
+    }
+    
+    public Collection<String> getAllowedFroms() {
+        return allowedFroms;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageWithId.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageWithId.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageWithId.java
deleted file mode 100644
index 0e4e172..0000000
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageWithId.java
+++ /dev/null
@@ -1,49 +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.jmap.methods;
-
-import org.apache.james.jmap.model.CreationMessage;
-import org.apache.james.jmap.model.CreationMessageId;
-
-public class MessageWithId<T> {
-
-    private CreationMessageId creationId;
-    private T message;
-
-    public MessageWithId(CreationMessageId creationId, T message) {
-        this.creationId = creationId;
-        this.message = message;
-    }
-
-    public CreationMessageId getCreationId() {
-        return creationId;
-    }
-
-    public T getMessage() {
-        return message;
-    }
-
-    public static class CreationMessageEntry extends MessageWithId<CreationMessage> {
-        public CreationMessageEntry(CreationMessageId creationId, CreationMessage message) {
-            super(creationId, message);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/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 49f87a9..3da428b 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
@@ -20,14 +20,11 @@
 package org.apache.james.jmap.methods;
 
 import java.io.IOException;
-import java.util.AbstractMap;
 import java.util.Date;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
-import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
 import javax.inject.Inject;
@@ -36,7 +33,8 @@ import javax.mail.MessagingException;
 import javax.mail.internet.SharedInputStream;
 import javax.mail.util.SharedByteArrayInputStream;
 
-import org.apache.james.jmap.exceptions.MailboxRoleNotFoundException;
+import org.apache.james.jmap.methods.ValueWithId.CreationMessageEntry;
+import org.apache.james.jmap.methods.ValueWithId.MessageWithId;
 import org.apache.james.jmap.model.CreationMessage;
 import org.apache.james.jmap.model.CreationMessageId;
 import org.apache.james.jmap.model.Message;
@@ -47,18 +45,17 @@ import org.apache.james.jmap.model.MessageProperties.MessageProperty;
 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.SetMessagesResponse.Builder;
 import org.apache.james.jmap.model.mailbox.Role;
 import org.apache.james.jmap.send.MailFactory;
 import org.apache.james.jmap.send.MailMetadata;
 import org.apache.james.jmap.send.MailSpool;
-import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.jmap.utils.SystemMailboxesProvider;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.exception.MailboxException;
-import org.apache.james.mailbox.model.MailboxMetaData;
+import org.apache.james.mailbox.exception.MailboxNotFoundException;
 import org.apache.james.mailbox.model.MailboxPath;
-import org.apache.james.mailbox.model.MailboxQuery;
 import org.apache.james.mailbox.store.MailboxSessionMapperFactory;
-import org.apache.james.mailbox.store.mail.MailboxMapperFactory;
 import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.model.Mailbox;
 import org.apache.james.mailbox.store.mail.model.MailboxId;
@@ -69,150 +66,186 @@ import org.apache.mailet.Mail;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.github.fge.lambdas.functions.ThrowingFunction;
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
 import com.google.common.base.Splitter;
-import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
 
 public class SetMessagesCreationProcessor implements SetMessagesProcessor {
 
-    private static final Logger LOGGER = LoggerFactory.getLogger(SetMessagesCreationProcessor.class);
-
-    private final MailboxMapperFactory mailboxMapperFactory;
-    private final MailboxManager mailboxManager;
+    private static final Logger LOG = LoggerFactory.getLogger(SetMailboxesCreationProcessor.class);
     private final MailboxSessionMapperFactory mailboxSessionMapperFactory;
     private final MIMEMessageConverter mimeMessageConverter;
     private final MailSpool mailSpool;
     private final MailFactory mailFactory;
     private final MessageFactory messageFactory;
+    private final SystemMailboxesProvider systemMailboxesProvider;
 
-    @Inject
-    @VisibleForTesting
-    SetMessagesCreationProcessor(MailboxMapperFactory mailboxMapperFactory,
-                                 MailboxManager mailboxManager,
-                                 MailboxSessionMapperFactory mailboxSessionMapperFactory,
+    
+    @VisibleForTesting @Inject
+    SetMessagesCreationProcessor(MailboxSessionMapperFactory mailboxSessionMapperFactory,
                                  MIMEMessageConverter mimeMessageConverter,
                                  MailSpool mailSpool,
                                  MailFactory mailFactory,
-                                 MessageFactory messageFactory) {
-        this.mailboxMapperFactory = mailboxMapperFactory;
-        this.mailboxManager = mailboxManager;
+                                 MessageFactory messageFactory,
+                                 SystemMailboxesProvider systemMailboxesProvider) {
         this.mailboxSessionMapperFactory = mailboxSessionMapperFactory;
         this.mimeMessageConverter = mimeMessageConverter;
         this.mailSpool = mailSpool;
         this.mailFactory = mailFactory;
         this.messageFactory = messageFactory;
+        this.systemMailboxesProvider = systemMailboxesProvider;
     }
 
     @Override
     public SetMessagesResponse process(SetMessagesRequest request, MailboxSession mailboxSession) {
-        Mailbox outbox;
+        Builder responseBuilder = SetMessagesResponse.builder();
+        request.getCreate()
+            .stream()
+            .forEach(create -> handleCreate(create, responseBuilder, mailboxSession));
+        return responseBuilder.build();
+    }
+
+    private void handleCreate(CreationMessageEntry create, Builder responseBuilder, MailboxSession mailboxSession) {
         try {
-            outbox = getOutbox(mailboxSession).orElseThrow(() -> new MailboxRoleNotFoundException(Role.OUTBOX));
-        } catch (MailboxException | MailboxRoleNotFoundException e) {
-            LOGGER.error("Unable to find a mailbox with role 'outbox'!");
-            throw Throwables.propagate(e);
-        }
+            validateImplementedFeature(create, mailboxSession);
+            validateArguments(create);
+            validateRights(create, mailboxSession);
+            MessageWithId created = handleOutboxMessages(create, mailboxSession);
+            responseBuilder.created(created.getCreationId(), created.getValue());
 
-        List<String> allowedSenders = ImmutableList.of(mailboxSession.getUser().getUserName());
+        } catch (MailboxSendingNotAllowedException e) {
+            responseBuilder.notCreated(create.getCreationId(), 
+                    SetError.builder()
+                        .type("invalidProperties")
+                        .properties(MessageProperty.from)
+                        .description("Invalid 'from' field. Must be one of " + 
+                                Joiner.on(", ").join(e.getAllowedFroms()))
+                        .build());
 
-        // handle errors
-        Predicate<CreationMessage> validMessagesTester = creationMessage -> creationMessage.isValid() && isAllowedFromAddress(creationMessage, allowedSenders);
-        Predicate<CreationMessage> invalidMessagesTester = validMessagesTester.negate();
-        Function<CreationMessage, List<ValidationResult>> toValidationResults = creationMessage -> ImmutableList.<ValidationResult>builder()
-            .addAll(creationMessage.validate())
-            .addAll(validationResultForIncorrectAddress(creationMessage, allowedSenders))
-            .build();
+        } catch (MailboxNotImplementedException e) {
+            responseBuilder.notCreated(create.getCreationId(), 
+                    SetError.builder()
+                        .type("invalidProperties")
+                        .properties(MessageProperty.mailboxIds)
+                        .description("Not yet implemented")
+                        .build());
 
-        SetMessagesResponse.Builder responseBuilder = SetMessagesResponse.builder()
-                .notCreated(handleCreationErrors(invalidMessagesTester, toValidationResults, request));
+        } catch (MailboxInvalidMessageCreationException e) {
+            responseBuilder.notCreated(create.getCreationId(),
+                    buildSetErrorFromValidationResult(create.getValue().validate()));
 
-        return request.getCreate().entrySet().stream()
-                .filter(e -> validMessagesTester.test(e.getValue()))
-                .map(e -> new MessageWithId.CreationMessageEntry(e.getKey(), e.getValue()))
-                .map(nuMsg -> createMessageInOutboxAndSend(nuMsg, mailboxSession, outbox, buildMessageIdFunc(mailboxSession, outbox)))
-                .map(msg -> SetMessagesResponse.builder().created(ImmutableMap.of(msg.getCreationId(), msg.getMessage())).build())
-                .reduce(responseBuilder, SetMessagesResponse.Builder::accumulator, SetMessagesResponse.Builder::combiner)
-                .build();
-    }
+        } catch (MailboxNotFoundException e) {
+            responseBuilder.notCreated(create.getCreationId(), 
+                    SetError.builder()
+                        .type("error")
+                        .description(e.getMailboxName() + " can't be found")
+                        .build());
 
-    private boolean isAllowedFromAddress(CreationMessage creationMessage, List<String> allowedFromMailAddresses) {
-        return creationMessage.getFrom()
-            .map(draftEmailer -> draftEmailer.getEmail()
-                .map(allowedFromMailAddresses::contains)
-                .orElse(false))
-            .orElse(false);
+        } catch (MailboxException | MessagingException e) {
+            LOG.error("Unexpected error while creating message", e);
+            responseBuilder.notCreated(create.getCreationId(), 
+                    SetError.builder()
+                        .type("error")
+                        .description("unexpected error")
+                        .build());
+        }
     }
-
-    private List<ValidationResult> validationResultForIncorrectAddress(CreationMessage creationMessage, List<String> allowedSenders) {
+    
+    private void validateImplementedFeature(CreationMessageEntry entry, MailboxSession session) throws MailboxNotImplementedException {
+        if (isAppendToMailboxWithRole(Role.DRAFTS, entry.getValue(), session)) {
+            throw new MailboxNotImplementedException("Drafts saving is not implemented");
+        }
+        if (!isAppendToMailboxWithRole(Role.OUTBOX, entry.getValue(), session)) {
+            throw new MailboxNotImplementedException("The only implemented feature is sending via outbox");
+        }
+    }
+    
+    private void validateArguments(CreationMessageEntry entry) throws MailboxInvalidMessageCreationException {
+        CreationMessage message = entry.getValue();
+        if (!message.isValid()) {
+            throw new MailboxInvalidMessageCreationException();
+        }
+    }
+    
+    private void validateRights(CreationMessageEntry entry, MailboxSession session) throws MailboxSendingNotAllowedException {
+        List<String> allowedSenders = ImmutableList.of(session.getUser().getUserName());
+        if (!isAllowedFromAddress(entry.getValue(), allowedSenders)) {
+            throw new MailboxSendingNotAllowedException(allowedSenders);
+        }
+    }
+    
+    private boolean isAllowedFromAddress(CreationMessage creationMessage, List<String> allowedFromMailAddresses) {
         return creationMessage.getFrom()
-            .map(draftEmailer -> draftEmailer
-                .getEmail()
-                .map(mail -> validationResultForIncorrectAddress(allowedSenders, mail))
-                .orElse(Lists.newArrayList()))
-            .orElse(Lists.newArrayList());
+                .map(draftEmailer -> draftEmailer.getEmail()
+                        .map(allowedFromMailAddresses::contains)
+                        .orElse(false))
+                .orElse(false);
     }
 
-    private List<ValidationResult> validationResultForIncorrectAddress(List<String> allowedSenders, String mail) {
-        if (!allowedSenders.contains(mail)) {
-            return Lists.newArrayList(ValidationResult.builder()
-                .message("Invalid 'from' field. Must be one of " + allowedSenders)
-                .property(MessageProperty.from.asFieldName())
-                .build());
+    
+    private MessageWithId handleOutboxMessages(CreationMessageEntry entry, MailboxSession session) throws MailboxException, MessagingException {
+        Mailbox outbox = getMailboxWithRole(session, Role.OUTBOX).orElseThrow(() -> new MailboxNotFoundException(Role.OUTBOX.serialize()));
+        if (!isRequestForSending(entry.getValue(), session)) {
+            throw new IllegalStateException("Messages for everything but outbox should have been filtered earlier");
         }
-        return Lists.newArrayList();
+        Function<Long, MessageId> idGenerator = uid -> generateMessageId(session, outbox, uid);
+        return createMessageInOutboxAndSend(entry, session, outbox, idGenerator);
     }
-
-    private Map<CreationMessageId, SetError> handleCreationErrors(Predicate<CreationMessage> invalidMessagesTester,
-                                                                  Function<CreationMessage, List<ValidationResult>> toValidationResults,
-                                                                  SetMessagesRequest request) {
-        return request.getCreate().entrySet().stream()
-                .filter(e -> invalidMessagesTester.test(e.getValue()))
-                .map(e -> new AbstractMap.SimpleEntry<>(e.getKey(), buildSetErrorFromValidationResult(toValidationResults.apply(e.getValue()))))
-                .collect(Collectors.toMap(AbstractMap.SimpleEntry::getKey, AbstractMap.SimpleEntry::getValue));
+    
+    @VisibleForTesting
+    protected MessageWithId createMessageInOutboxAndSend(CreationMessageEntry createdEntry,
+                                                           MailboxSession session,
+                                                           Mailbox outbox, Function<Long, MessageId> buildMessageIdFromUid) throws MailboxException, MessagingException {
+        
+        CreationMessageId creationId = createdEntry.getCreationId();
+        MessageMapper messageMapper = mailboxSessionMapperFactory.createMessageMapper(session);
+        MailboxMessage newMailboxMessage = buildMailboxMessage(createdEntry, outbox);
+        messageMapper.add(outbox, newMailboxMessage);
+        Message jmapMessage = messageFactory.fromMailboxMessage(newMailboxMessage, ImmutableList.of(), buildMessageIdFromUid);
+        sendMessage(newMailboxMessage, jmapMessage, session);
+        return new MessageWithId(creationId, jmapMessage);
+    }
+    
+    private boolean isAppendToMailboxWithRole(Role role, CreationMessage entry, MailboxSession mailboxSession) {
+        return getMailboxWithRole(mailboxSession, role)
+                .map(box -> entry.isIn(box))
+                .orElse(false);
     }
 
+    private Optional<Mailbox> getMailboxWithRole(MailboxSession mailboxSession, Role role) {
+        return systemMailboxesProvider.listMailboxes(role, mailboxSession).findFirst();
+    }
+    
     private SetError buildSetErrorFromValidationResult(List<ValidationResult> validationErrors) {
-        String formattedValidationErrorMessage = validationErrors.stream()
+        return SetError.builder()
+                .type("invalidProperties")
+                .properties(collectMessageProperties(validationErrors))
+                .description(formatValidationErrorMessge(validationErrors))
+                .build();
+    }
+
+    private String formatValidationErrorMessge(List<ValidationResult> validationErrors) {
+        return validationErrors.stream()
                 .map(err -> err.getProperty() + ": " + err.getErrorMessage())
                 .collect(Collectors.joining("\\n"));
+    }
+
+    private Set<MessageProperties.MessageProperty> collectMessageProperties(List<ValidationResult> validationErrors) {
         Splitter propertiesSplitter = Splitter.on(',').trimResults().omitEmptyStrings();
-        Set<MessageProperties.MessageProperty> properties = validationErrors.stream()
+        return validationErrors.stream()
                 .flatMap(err -> propertiesSplitter.splitToList(err.getProperty()).stream())
                 .flatMap(MessageProperty::find)
                 .collect(Collectors.toSet());
-        return SetError.builder()
-                .type("invalidProperties")
-                .properties(properties)
-                .description(formattedValidationErrorMessage)
-                .build();
     }
 
-    @VisibleForTesting
-    protected MessageWithId<Message> createMessageInOutboxAndSend(MessageWithId.CreationMessageEntry createdEntry,
-                                                           MailboxSession session,
-                                                           Mailbox outbox, Function<Long, MessageId> buildMessageIdFromUid) {
-        try {
-            MessageMapper messageMapper = mailboxSessionMapperFactory.createMessageMapper(session);
-            MailboxMessage newMailboxMessage = buildMailboxMessage(createdEntry, outbox);
-            messageMapper.add(outbox, newMailboxMessage);
-            Message jmapMessage = messageFactory.fromMailboxMessage(newMailboxMessage, ImmutableList.of(), buildMessageIdFromUid);
-            sendMessage(newMailboxMessage, jmapMessage, session);
-            return new MessageWithId<>(createdEntry.getCreationId(), jmapMessage);
-        } catch (MailboxException | MessagingException | IOException e) {
-            throw Throwables.propagate(e);
-        } catch (MailboxRoleNotFoundException e) {
-            LOGGER.error("Could not find mailbox '%s' while trying to save message.", e.getRole().serialize());
-            throw Throwables.propagate(e);
-        }
+    private boolean isRequestForSending(CreationMessage creationMessage, MailboxSession session) {
+        return isAppendToMailboxWithRole(Role.OUTBOX, creationMessage, session);
     }
-
-    private Function<Long, MessageId> buildMessageIdFunc(MailboxSession session, Mailbox outbox) {
+    
+    private MessageId generateMessageId(MailboxSession session, Mailbox outbox, long uid) {
         MailboxPath outboxPath = new MailboxPath(session.getPersonalSpace(), session.getUser().getUserName(), outbox.getName());
-        return uid -> new MessageId(session.getUser(), outboxPath, uid);
+        return new MessageId(session.getUser(), outboxPath, uid);
     }
 
     private MailboxMessage buildMailboxMessage(MessageWithId.CreationMessageEntry createdEntry, Mailbox outbox) {
@@ -221,35 +254,15 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
         long size = messageContent.length;
         int bodyStartOctet = 0;
 
-        Flags flags = getMessageFlags(createdEntry.getMessage());
+        Flags flags = getMessageFlags(createdEntry.getValue());
         PropertyBuilder propertyBuilder = buildPropertyBuilder();
         MailboxId mailboxId = outbox.getMailboxId();
-        Date internalDate = Date.from(createdEntry.getMessage().getDate().toInstant());
+        Date internalDate = Date.from(createdEntry.getValue().getDate().toInstant());
 
         return new SimpleMailboxMessage(internalDate, size,
                 bodyStartOctet, content, flags, propertyBuilder, mailboxId);
     }
 
-    @VisibleForTesting
-    protected Optional<Mailbox> getOutbox(MailboxSession session) throws MailboxException {
-        return mailboxManager.search(MailboxQuery.builder(session)
-                .privateUserMailboxes().build(), session).stream()
-            .map(MailboxMetaData::getPath)
-            .filter(this::hasRoleOutbox)
-            .map(loadMailbox(session))
-            .findFirst();
-    }
-
-    private boolean hasRoleOutbox(MailboxPath mailBoxPath) {
-        return Role.from(mailBoxPath.getName())
-                .map(Role.OUTBOX::equals)
-                .orElse(false);
-    }
-
-    private ThrowingFunction<MailboxPath, Mailbox> loadMailbox(MailboxSession session) {
-        return path -> mailboxMapperFactory.getMailboxMapper(session).findMailboxByPath(path);
-    }
-
     private PropertyBuilder buildPropertyBuilder() {
         return new PropertyBuilder();
     }
@@ -271,9 +284,17 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
         return result;
     }
 
-    private void sendMessage(MailboxMessage mailboxMessage, Message jmapMessage, MailboxSession session) throws MessagingException, IOException {
-        Mail mail = mailFactory.build(mailboxMessage, jmapMessage);
+    private void sendMessage(MailboxMessage mailboxMessage, Message jmapMessage, MailboxSession session) throws MessagingException {
+        Mail mail = buildMessage(mailboxMessage, jmapMessage);
         MailMetadata metadata = new MailMetadata(jmapMessage.getId(), session.getUser().getUserName());
         mailSpool.send(mail, metadata);
     }
+
+    private Mail buildMessage(MailboxMessage mailboxMessage, Message jmapMessage) throws MessagingException {
+        try {
+            return mailFactory.build(mailboxMessage, jmapMessage);
+        } catch (IOException e) {
+            throw new MessagingException("error building message to send", e);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/ValueWithId.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/ValueWithId.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/ValueWithId.java
new file mode 100644
index 0000000..a5548c1
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/ValueWithId.java
@@ -0,0 +1,63 @@
+/****************************************************************
+ * 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;
+
+import org.apache.james.jmap.model.CreationMessage;
+import org.apache.james.jmap.model.CreationMessageId;
+import org.apache.james.jmap.model.SetError;
+import org.apache.james.jmap.model.Message;
+
+public class ValueWithId<T> {
+
+    private CreationMessageId creationId;
+    private T value;
+
+    private ValueWithId(CreationMessageId creationId, T value) {
+        this.creationId = creationId;
+        this.value = value;
+    }
+
+    public CreationMessageId getCreationId() {
+        return creationId;
+    }
+
+    public T getValue() {
+        return value;
+    }
+
+    public static class CreationMessageEntry extends ValueWithId<CreationMessage> {
+        public CreationMessageEntry(CreationMessageId creationId, CreationMessage message) {
+            super(creationId, message);
+        }
+    }
+
+    public static class ErrorWithId extends ValueWithId<SetError> {
+        public ErrorWithId(CreationMessageId creationId, SetError error) {
+            super(creationId, error);
+        }
+    }
+
+    public static class MessageWithId extends ValueWithId<Message> {
+        public MessageWithId(CreationMessageId creationId, Message message) {
+            super(creationId, message);
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/CreationMessage.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/CreationMessage.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/CreationMessage.java
index de2453d..60f7b91 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/CreationMessage.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/CreationMessage.java
@@ -19,9 +19,8 @@
 
 package org.apache.james.jmap.model;
 
-import static org.apache.james.jmap.model.MessageProperties.MessageProperty;
-
 import java.time.ZonedDateTime;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -32,6 +31,8 @@ import javax.mail.internet.AddressException;
 import javax.mail.internet.InternetAddress;
 
 import org.apache.james.jmap.methods.ValidationResult;
+import org.apache.james.jmap.model.MessageProperties.MessageProperty;
+import org.apache.james.mailbox.store.mail.model.Mailbox;
 
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
@@ -84,8 +85,12 @@ public class CreationMessage {
             headers = ImmutableMap.builder();
         }
 
-        public Builder mailboxIds(ImmutableList<String> mailboxIds) {
-            this.mailboxIds = mailboxIds;
+        public Builder mailboxId(String... mailboxIds) {
+            return mailboxIds(Arrays.asList(mailboxIds));
+        }
+
+        public Builder mailboxIds(List<String> mailboxIds) {
+            this.mailboxIds = ImmutableList.copyOf(mailboxIds);
             return this;
         }
 
@@ -349,6 +354,10 @@ public class CreationMessage {
         from.filter(f -> !f.hasValidEmail()).ifPresent(f -> errors.add(invalidPropertyFrom));
     }
 
+    public boolean isIn(Mailbox mailbox) {
+        return mailboxIds.contains(mailbox.getMailboxId().serialize());
+    }
+    
     @JsonDeserialize(builder = DraftEmailer.Builder.class)
     public static class DraftEmailer {
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
index 1c1cd50..94b8517 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java
@@ -95,8 +95,12 @@ public class Message {
             return this;
         }
 
-        public Builder mailboxIds(ImmutableList<String> mailboxIds) {
-            this.mailboxIds = mailboxIds;
+        public Builder mailboxId(String mailboxId) {
+            return this.mailboxIds(ImmutableList.of(mailboxId));
+        }
+
+        public Builder mailboxIds(List<String> mailboxIds) {
+            this.mailboxIds = ImmutableList.copyOf(mailboxIds);
             return this;
         }
 

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetError.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetError.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetError.java
index 75aacd2..658491c 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetError.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetError.java
@@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Strings;
@@ -61,6 +62,10 @@ public class SetError {
             return this;
         }
 
+        public Builder properties(MessageProperty... properties) {
+            return properties(ImmutableSet.copyOf(properties));
+        }
+
         public Builder properties(Set<MessageProperty> properties) {
             this.properties = Optional.of(Sets.union(
                     this.properties.orElse(ImmutableSet.of()),
@@ -116,4 +121,13 @@ public class SetError {
     public int hashCode() {
         return Objects.hashCode(type, description, properties);
     }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("description", description)
+                .add("type", type)
+                .add("properties", properties)
+                .toString();
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesRequest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesRequest.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesRequest.java
index 096e9c6..a064102 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesRequest.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesRequest.java
@@ -19,13 +19,17 @@
 
 package org.apache.james.jmap.model;
 
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.function.Function;
 
+import org.apache.commons.lang.NotImplementedException;
 import org.apache.james.jmap.methods.JmapRequest;
 import org.apache.james.jmap.methods.UpdateMessagePatchConverter;
+import org.apache.james.jmap.methods.ValueWithId.CreationMessageEntry;
+import org.apache.james.util.streams.ImmutableCollectors;
 
 import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
@@ -34,7 +38,6 @@ import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
-import org.apache.commons.lang.NotImplementedException;
 
 @JsonDeserialize(builder = SetMessagesRequest.Builder.class)
 public class SetMessagesRequest implements JmapRequest {
@@ -48,13 +51,13 @@ public class SetMessagesRequest implements JmapRequest {
 
         private String accountId;
         private String ifInState;
-        private ImmutableMap.Builder<CreationMessageId, CreationMessage> create;
+        private HashMap<CreationMessageId, CreationMessage> create;
         private ImmutableMap.Builder<MessageId, Function<UpdateMessagePatchConverter, UpdateMessagePatch>> updatesProvider;
 
         private ImmutableList.Builder<MessageId> destroy;
 
         private Builder() {
-            create = ImmutableMap.builder();
+            create = new HashMap<>();
             updatesProvider = ImmutableMap.builder();
             destroy = ImmutableList.builder();
         }
@@ -73,6 +76,11 @@ public class SetMessagesRequest implements JmapRequest {
             return this;
         }
 
+        public Builder create(CreationMessageId creationMessageId, CreationMessage creation) {
+            this.create.put(creationMessageId, creation);
+            return this;
+        }
+
         public Builder create(Map<CreationMessageId, CreationMessage> creations) {
             this.create.putAll(creations);
             return this;
@@ -89,17 +97,24 @@ public class SetMessagesRequest implements JmapRequest {
         }
 
         public SetMessagesRequest build() {
-            return new SetMessagesRequest(Optional.ofNullable(accountId), Optional.ofNullable(ifInState), create.build(), updatesProvider.build(), destroy.build());
+            return new SetMessagesRequest(Optional.ofNullable(accountId), Optional.ofNullable(ifInState), 
+                    messageCreations(), updatesProvider.build(), destroy.build());
+        }
+
+        private ImmutableList<CreationMessageEntry> messageCreations() {
+            return create.entrySet().stream()
+                    .map(entry -> new CreationMessageEntry(entry.getKey(), entry.getValue()))
+                    .collect(ImmutableCollectors.toImmutableList());
         }
     }
 
     private final Optional<String> accountId;
     private final Optional<String> ifInState;
-    private final Map<CreationMessageId, CreationMessage> create;
+    private final List<CreationMessageEntry> create;
     private final Map<MessageId, Function<UpdateMessagePatchConverter, UpdateMessagePatch>> update;
     private final List<MessageId> destroy;
 
-    @VisibleForTesting SetMessagesRequest(Optional<String> accountId, Optional<String> ifInState, Map<CreationMessageId, CreationMessage> create, Map<MessageId, Function<UpdateMessagePatchConverter, UpdateMessagePatch>>  update, List<MessageId> destroy) {
+    @VisibleForTesting SetMessagesRequest(Optional<String> accountId, Optional<String> ifInState, List<CreationMessageEntry> create, Map<MessageId, Function<UpdateMessagePatchConverter, UpdateMessagePatch>>  update, List<MessageId> destroy) {
         this.accountId = accountId;
         this.ifInState = ifInState;
         this.create = create;
@@ -115,7 +130,7 @@ public class SetMessagesRequest implements JmapRequest {
         return ifInState;
     }
 
-    public Map<CreationMessageId, CreationMessage> getCreate() {
+    public List<CreationMessageEntry> getCreate() {
         return create;
     }
 
@@ -126,4 +141,5 @@ public class SetMessagesRequest implements JmapRequest {
     public List<MessageId> getDestroy() {
         return destroy;
     }
+
 }

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesResponse.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesResponse.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesResponse.java
index a536ba4..96286e7 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesResponse.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/SetMessagesResponse.java
@@ -81,6 +81,11 @@ public class SetMessagesResponse implements Method.Response {
             throw new NotImplementedException();
         }
 
+        public Builder created(CreationMessageId creationMessageId, Message message) {
+            this.created.put(creationMessageId, message);
+            return this;
+        }
+
         public Builder created(Map<CreationMessageId, Message> created) {
             this.created.putAll(created);
             return this;
@@ -105,6 +110,11 @@ public class SetMessagesResponse implements Method.Response {
             this.notCreated.putAll(notCreated);
             return this;
         }
+        
+        public Builder notCreated(CreationMessageId id, SetError error) {
+            this.notCreated.put(id, error);
+            return this;
+        }
 
         public Builder notUpdated(Map<MessageId, SetError> notUpdated) {
             this.notUpdated.putAll(notUpdated);
@@ -121,6 +131,10 @@ public class SetMessagesResponse implements Method.Response {
             return this;
         }
 
+        public Builder mergeWith(Builder otherBuilder) {
+            return otherBuilder.build().mergeInto(this);
+        }
+
         public SetMessagesResponse build() {
             return new SetMessagesResponse(accountId, oldState, newState, 
                     created.build(), updated.build(), destroyed.build(), notCreated.build(), notUpdated.build(), notDestroyed.build());

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/PostDequeueDecorator.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/PostDequeueDecorator.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/PostDequeueDecorator.java
index 6d7f083..52c2dfd 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/PostDequeueDecorator.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/PostDequeueDecorator.java
@@ -21,10 +21,10 @@ package org.apache.james.jmap.send;
 import java.io.Serializable;
 import java.util.Iterator;
 
+import org.apache.james.jmap.exceptions.MailboxRoleNotFoundException;
 import org.apache.james.jmap.model.MessageId;
 import org.apache.james.jmap.model.mailbox.Role;
 import org.apache.james.jmap.send.exception.MailShouldBeInOutboxException;
-import org.apache.james.jmap.send.exception.MailboxRoleNotFoundException;
 import org.apache.james.jmap.send.exception.MessageIdNotFoundException;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxSession;

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/exception/MailboxRoleNotFoundException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/exception/MailboxRoleNotFoundException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/exception/MailboxRoleNotFoundException.java
deleted file mode 100644
index 574edf1..0000000
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/send/exception/MailboxRoleNotFoundException.java
+++ /dev/null
@@ -1,30 +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.jmap.send.exception;
-
-import org.apache.james.jmap.model.mailbox.Role;
-import org.apache.james.queue.api.MailQueue.MailQueueException;
-
-public class MailboxRoleNotFoundException extends MailQueueException {
-
-    public MailboxRoleNotFoundException(Role role) {
-        super("Unable to find a mailbox with role " + role.serialize());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/SystemMailboxesProvider.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/SystemMailboxesProvider.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/SystemMailboxesProvider.java
new file mode 100644
index 0000000..908ef9c
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/SystemMailboxesProvider.java
@@ -0,0 +1,30 @@
+/****************************************************************
+ * 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.utils;
+
+import java.util.stream.Stream;
+
+import org.apache.james.jmap.model.mailbox.Role;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.store.mail.model.Mailbox;
+
+public interface SystemMailboxesProvider {
+    Stream<Mailbox> listMailboxes(Role aRole, MailboxSession session);
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/SystemMailboxesProviderImpl.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/SystemMailboxesProviderImpl.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/SystemMailboxesProviderImpl.java
new file mode 100644
index 0000000..b868c85
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/SystemMailboxesProviderImpl.java
@@ -0,0 +1,70 @@
+/****************************************************************
+ * 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.utils;
+
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import javax.inject.Inject;
+
+import org.apache.james.jmap.model.mailbox.Role;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.model.MailboxMetaData;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.MailboxQuery;
+import org.apache.james.mailbox.store.mail.MailboxMapperFactory;
+import org.apache.james.mailbox.store.mail.model.Mailbox;
+
+import com.github.fge.lambdas.functions.ThrowingFunction;
+import com.github.fge.lambdas.supplier.ThrowingSupplier;
+import com.google.common.annotations.VisibleForTesting;
+
+public class SystemMailboxesProviderImpl implements SystemMailboxesProvider {
+
+    private final MailboxMapperFactory mailboxMapperFactory;
+    private final MailboxManager mailboxManager;
+
+    @Inject
+    @VisibleForTesting SystemMailboxesProviderImpl(MailboxMapperFactory mailboxMapperFactory, MailboxManager mailboxManager) {
+        this.mailboxMapperFactory = mailboxMapperFactory;
+        this.mailboxManager = mailboxManager;
+    }
+
+    private boolean hasRole(Role aRole, MailboxPath mailBoxPath) {
+        return Role.from(mailBoxPath.getName())
+                .map(aRole::equals)
+                .orElse(false);
+    }
+
+    public Stream<Mailbox> listMailboxes(Role aRole, MailboxSession session) {
+        ThrowingSupplier<List<MailboxMetaData>> getAllMailboxes = () -> mailboxManager.search(MailboxQuery.builder(session).privateUserMailboxes().build(), session);
+        Predicate<MailboxPath> hasSpecifiedRole = path -> hasRole(aRole, path);
+        return getAllMailboxes.get().stream()
+                .map(MailboxMetaData::getPath)
+                .filter(hasSpecifiedRole)
+                .map(loadMailbox(session));
+    }
+
+    private ThrowingFunction<MailboxPath, Mailbox> loadMailbox(MailboxSession session) {
+        return path -> mailboxMapperFactory.getMailboxMapper(session).findMailboxByPath(path);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/d5988181/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/MIMEMessageConverterTest.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/MIMEMessageConverterTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/MIMEMessageConverterTest.java
index c2cf064..e64d6a9 100644
--- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/MIMEMessageConverterTest.java
+++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/methods/MIMEMessageConverterTest.java
@@ -26,6 +26,7 @@ import java.time.Instant;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
 
+import org.apache.james.jmap.methods.ValueWithId.MessageWithId;
 import org.apache.james.jmap.model.CreationMessage;
 import org.apache.james.jmap.model.CreationMessage.DraftEmailer;
 import org.apache.james.jmap.model.CreationMessageId;
@@ -55,7 +56,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), messageHavingInReplyTo));
 
         // Then
@@ -67,7 +68,7 @@ public class MIMEMessageConverterTest {
     public void convertToMimeShouldThrowWhenMessageIsNull() {
         MIMEMessageConverter sut = new MIMEMessageConverter();
 
-        sut.convertToMime(new MessageWithId.CreationMessageEntry(CreationMessageId.of("any"), null));
+        sut.convertToMime(new ValueWithId.CreationMessageEntry(CreationMessageId.of("any"), null));
     }
 
     @Test
@@ -83,7 +84,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), testMessage));
 
         // Then
@@ -107,7 +108,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), testMessage));
 
         // Then
@@ -128,7 +129,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), testMessage));
 
         // Then
@@ -148,7 +149,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), testMessage));
 
         // Then
@@ -169,7 +170,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), testMessage));
 
         // Then
@@ -190,7 +191,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), testMessage));
 
         // Then
@@ -247,7 +248,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), testMessage));
 
         // Then
@@ -267,7 +268,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), testMessage));
 
         // Then
@@ -288,7 +289,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), testMessage));
 
         // Then
@@ -310,7 +311,7 @@ public class MIMEMessageConverterTest {
                 .build();
 
         // When
-        Message result = sut.convertToMime(new MessageWithId.CreationMessageEntry(
+        Message result = sut.convertToMime(new ValueWithId.CreationMessageEntry(
                 CreationMessageId.of("user|mailbox|1"), testMessage));
 
         // Then


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