You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ad...@apache.org on 2017/11/15 11:15:49 UTC
[08/19] james-project git commit: JAMES-2214 Implement Draft saving
via JMAP
JAMES-2214 Implement Draft saving via JMAP
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/fe0522ac
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/fe0522ac
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/fe0522ac
Branch: refs/heads/master
Commit: fe0522ac5cc2eaa20619dcc8cef40075f9d39046
Parents: f85b5c6
Author: benwa <bt...@linagora.com>
Authored: Mon Nov 13 11:46:22 2017 +0700
Committer: benwa <bt...@linagora.com>
Committed: Wed Nov 15 18:04:09 2017 +0700
----------------------------------------------------------------------
.../integration/SetMessagesMethodTest.java | 540 ++++++++++++++++++-
.../InvalidDraftKeywordsException.java | 24 +
.../james/jmap/methods/MessageAppender.java | 18 +-
.../methods/SetMessagesCreationProcessor.java | 60 ++-
.../SetMessagesCreationProcessorTest.java | 27 +-
5 files changed, 624 insertions(+), 45 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/james-project/blob/fe0522ac/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 3508e65..13e1da4 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
@@ -119,8 +119,8 @@ public abstract class SetMessagesMethodTest {
private static final String SECOND_ARGUMENTS = "[1][1]";
private static final String USERS_DOMAIN = "domain.tld";
private static final String USERNAME = "username@" + USERS_DOMAIN;
- private static final String PASSWORD = "password";
private static final String BOB = "bob@" + USERS_DOMAIN;
+ private static final String PASSWORD = "password";
private static final String BOB_PASSWORD = "bobPassword";
private static final MailboxPath USER_MAILBOX = MailboxPath.forUser(USERNAME, "mailbox");
private static final String NOT_UPDATED = ARGUMENTS + ".notUpdated";
@@ -194,6 +194,10 @@ public abstract class SetMessagesMethodTest {
return getMailboxId(accessToken, Role.OUTBOX);
}
+ private String getDraftId(AccessToken accessToken) {
+ return getMailboxId(accessToken, Role.DRAFTS);
+ }
+
private String getMailboxId(AccessToken accessToken, Role role) {
return getAllMailboxesIds(accessToken).stream()
.filter(x -> x.get("role").equalsIgnoreCase(role.serialize()))
@@ -1291,6 +1295,7 @@ public abstract class SetMessagesMethodTest {
.body(NAME, equalTo("messagesSet"))
.body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
.body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].type", equalTo("error"))
+ .body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].properties", contains("mailboxIds"))
.body(ARGUMENTS + ".notCreated[\""+messageCreationId+"\"].description", endsWith("MailboxId invalid"));
}
@@ -1361,8 +1366,529 @@ public abstract class SetMessagesMethodTest {
.body(ARGUMENTS + ".created", aMapWithSize(1))
.body(ARGUMENTS + ".created", hasKey(messageCreationId))
.body(ARGUMENTS + ".created[\""+messageCreationId+"\"].keywords.$Draft", equalTo(true))
- .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].keywords.$Flagged", equalTo(true))
- ;
+ .body(ARGUMENTS + ".created[\""+messageCreationId+"\"].keywords.$Flagged", equalTo(true));
+ }
+
+ @Test
+ public void setMessagesShouldAllowDraftCreation() {
+ String messageCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ .body(ARGUMENTS + ".created", hasKey(messageCreationId));
+ }
+
+ @Test
+ public void setMessagesShouldFailWhenSavingADraftInSeveralMailboxes() {
+ String messageCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+ MailboxId mailboxId = mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, USERNAME, "mailbox");
+
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\", \"" + mailboxId.serialize() + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(1))
+ .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].type", equalTo("invalidProperties"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].properties", contains("mailboxIds"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].description", equalTo("Not yet implemented"));
+ }
+
+ @Test
+ public void setMessagesShouldBeCompatibleWithIsDraftProperty() {
+ String messageCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"isDraft\": true," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ .body(ARGUMENTS + ".created", hasKey(messageCreationId));
+ }
+
+ @Test
+ public void setMessagesShouldRejectCreateInDraftAndOutboxForASingleMessage() {
+ String messageCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\", \"" +getOutboxId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(1))
+ .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].type", equalTo("invalidProperties"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].properties", contains("mailboxIds"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].description", equalTo("Not yet implemented"));
+ }
+
+ @Test
+ public void setMessagesShouldStoreDraft() {
+ String messageCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ String receivedMessageId =
+ with()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .post("/jmap")
+ .then()
+ .extract()
+ .path(ARGUMENTS + ".created[\""+messageCreationId+"\"].id");
+
+ String firstMessage = ARGUMENTS + ".list[0]";
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body("[[\"getMessages\", {\"ids\": [\"" + receivedMessageId + "\"]}, \"#0\"]]")
+ .when()
+ .post("/jmap")
+ .then()
+ .statusCode(200)
+ .body(NAME, equalTo("messages"))
+ .body(ARGUMENTS + ".list", hasSize(1))
+ .body(firstMessage + ".id", equalTo(receivedMessageId));
+ }
+
+ @Test
+ public void setMessagesShouldNotCheckFromWhenDraft() {
+ String messageCreationId = "creationId1337";
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"invalid@domain.com\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ .body(ARGUMENTS + ".created", hasKey(messageCreationId));
+ }
+
+ @Test
+ public void setMessagesShouldNotCheckFromWhenInvalidEmailWhenDraft() {
+ String messageCreationId = "creationId1337";
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"invalid\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ .body(ARGUMENTS + ".created", hasKey(messageCreationId));
+ }
+
+ @Test
+ public void setMessagesShouldAllowDraftCreationWithoutFrom() {
+ String messageCreationId = "creationId1337";
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ .body(ARGUMENTS + ".created", hasKey(messageCreationId));
+ }
+
+ @Test
+ public void setMessagesShouldAllowDraftCreationWithoutRecipients() {
+ String messageCreationId = "creationId1337";
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"invalid@domain.com\"}," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ .body(ARGUMENTS + ".created", hasKey(messageCreationId));
+ }
+
+ @Test
+ public void setMessagesShouldRequireDraftFlagWhenSavingDraft() {
+ String messageCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Flagged\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(1))
+ .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].type", equalTo("invalidProperties"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].properties", contains("keywords"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].description", equalTo("A draft message should be flagged as Draft"));
+ }
+
+ @Test
+ public void setMessagesShouldCheckAttachmentsWhenDraft() {
+ String messageCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"attachments\": [" +
+ " {\"blobId\" : \"wrong\", \"type\" : \"image/jpeg\", \"size\" : 1337}" +
+ " ]," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(1))
+ .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].type", equalTo("invalidProperties"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].properties", contains("attachments"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].description", equalTo("Attachment not found"));
+ }
+
+ @Test
+ public void setMessagesShouldAcceptAttachmentsWhenDraft() throws Exception {
+ String messageCreationId = "creationId1337";
+ String fromAddress = USERNAME;
+ Attachment attachment = Attachment.builder()
+ .bytes("attachment".getBytes(Charsets.UTF_8))
+ .type("application/octet-stream")
+ .build();
+ uploadAttachment(attachment);
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," +
+ " \"to\": [{ \"name\": \"BOB\", \"email\": \"someone@example.com\"}]," +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"attachments\": [" +
+ " {\"blobId\" : \"" + attachment.getAttachmentId().getId() + "\", " +
+ " \"type\" : \"" + attachment.getType() + "\"," +
+ " \"size\" : " + attachment.getSize() + "}" +
+ " ]," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", accessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(0))
+ .body(ARGUMENTS + ".created", aMapWithSize(1))
+ .body(ARGUMENTS + ".created", hasKey(messageCreationId));
+ }
+
+ @Test
+ public void setMessagesShouldNotAllowDraftCreationInSomeoneElseMailbox() throws Exception {
+ String messageCreationId = "creationId1337";
+ Attachment attachment = Attachment.builder()
+ .bytes("attachment".getBytes(Charsets.UTF_8))
+ .type("application/octet-stream")
+ .build();
+ uploadAttachment(attachment);
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", bobAccessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(1))
+ .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].type", equalTo("error"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].description", endsWith("can not be found"));
+ }
+
+ @Test
+ public void setMessagesShouldNotAllowDraftCreationInADelegatedMailbox() throws Exception {
+ String messageCreationId = "creationId1337";
+ Attachment attachment = Attachment.builder()
+ .bytes("attachment".getBytes(Charsets.UTF_8))
+ .type("application/octet-stream")
+ .build();
+ uploadAttachment(attachment);
+
+ jmapServer.getProbe(ACLProbeImpl.class)
+ .addRights(
+ MailboxPath.forUser(USERNAME, DefaultMailboxes.DRAFTS),
+ BOB,
+ MailboxACL.FULL_RIGHTS);
+
+ String requestBody = "[" +
+ " [" +
+ " \"setMessages\","+
+ " {" +
+ " \"create\": { \"" + messageCreationId + "\" : {" +
+ " \"subject\": \"subject\"," +
+ " \"keywords\": {\"$Draft\": true}," +
+ " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" +
+ " }}" +
+ " }," +
+ " \"#0\"" +
+ " ]" +
+ "]";
+
+ given()
+ .header("Authorization", bobAccessToken.serialize())
+ .body(requestBody)
+ .when()
+ .post("/jmap")
+ .then()
+ .log().ifValidationFails()
+ .statusCode(200)
+ .body(NAME, equalTo("messagesSet"))
+ .body(ARGUMENTS + ".created", aMapWithSize(0))
+ .body(ARGUMENTS + ".notCreated", aMapWithSize(1))
+ .body(ARGUMENTS + ".notCreated", hasKey(messageCreationId))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].type", equalTo("error"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].properties", contains("mailboxIds"))
+ .body(ARGUMENTS + ".notCreated[\"" + messageCreationId + "\"].description", endsWith("MailboxId invalid"));
}
@Test
@@ -1814,10 +2340,9 @@ public abstract class SetMessagesMethodTest {
public void setMessagesShouldStripBccFromDeliveredEmail() throws Exception {
// Recipient
String recipientAddress = "recipient" + "@" + USERS_DOMAIN;
- String bccRecipient = "bob@" + USERS_DOMAIN;
+ String bccRecipient = BOB;
String password = "password";
dataProbe.addUser(recipientAddress, password);
- dataProbe.addUser(bccRecipient, password);
mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, recipientAddress, DefaultMailboxes.INBOX);
await();
AccessToken recipientToken = HttpJmapAuthentication.authenticateJamesUser(baseUri(), recipientAddress, password);
@@ -1933,7 +2458,6 @@ public abstract class SetMessagesMethodTest {
String bccAddress = BOB;
mailboxProbe.createMailbox(MailboxConstants.USER_NAMESPACE, bccAddress, DefaultMailboxes.INBOX);
await();
- AccessToken bccToken = HttpJmapAuthentication.authenticateJamesUser(baseUri(), BOB, BOB_PASSWORD);
String messageCreationId = "creationId1337";
String fromAddress = USERNAME;
@@ -1964,9 +2488,9 @@ public abstract class SetMessagesMethodTest {
.post("/jmap");
// Then
- calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> isAnyMessageFoundInRecipientsMailboxes(bccToken));
+ calmlyAwait.atMost(30, TimeUnit.SECONDS).until(() -> isAnyMessageFoundInRecipientsMailboxes(bobAccessToken));
with()
- .header("Authorization", bccToken.serialize())
+ .header("Authorization", bobAccessToken.serialize())
.body("[[\"getMessageList\", {\"fetchMessages\": true, \"fetchMessageProperties\": [\"bcc\"] }, \"#0\"]]")
.when()
.post("/jmap")
http://git-wip-us.apache.org/repos/asf/james-project/blob/fe0522ac/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidDraftKeywordsException.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidDraftKeywordsException.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidDraftKeywordsException.java
new file mode 100644
index 0000000..08dd9b6
--- /dev/null
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/exceptions/InvalidDraftKeywordsException.java
@@ -0,0 +1,24 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.jmap.exceptions;
+
+public class InvalidDraftKeywordsException extends IllegalArgumentException {
+
+}
http://git-wip-us.apache.org/repos/asf/james-project/blob/fe0522ac/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
----------------------------------------------------------------------
diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
index 4b1fad6..4d09810 100644
--- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
+++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/MessageAppender.java
@@ -25,10 +25,12 @@ import java.util.Optional;
import javax.inject.Inject;
import javax.mail.util.SharedByteArrayInputStream;
+import org.apache.james.jmap.methods.ValueWithId.CreationMessageEntry;
import org.apache.james.jmap.model.Attachment;
import org.apache.james.jmap.model.Keywords;
import org.apache.james.jmap.model.MessageFactory;
import org.apache.james.mailbox.AttachmentManager;
+import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxSession;
import org.apache.james.mailbox.MessageManager;
import org.apache.james.mailbox.exception.AttachmentNotFoundException;
@@ -36,6 +38,7 @@ import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.AttachmentId;
import org.apache.james.mailbox.model.Cid;
import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MessageAttachment;
import org.apache.james.util.OptionalUtils;
import org.slf4j.Logger;
@@ -49,16 +52,18 @@ import com.google.common.collect.ImmutableList;
public class MessageAppender {
private static final Logger LOGGER = LoggerFactory.getLogger(MessageAppender.class);
+ private final MailboxManager mailboxManager;
private final AttachmentManager attachmentManager;
private final MIMEMessageConverter mimeMessageConverter;
@Inject
- public MessageAppender(AttachmentManager attachmentManager, MIMEMessageConverter mimeMessageConverter) {
+ public MessageAppender(MailboxManager mailboxManager, AttachmentManager attachmentManager, MIMEMessageConverter mimeMessageConverter) {
+ this.mailboxManager = mailboxManager;
this.attachmentManager = attachmentManager;
this.mimeMessageConverter = mimeMessageConverter;
}
- public MessageFactory.MetaDataWithContent createMessageInMailbox(ValueWithId.MessageWithId.CreationMessageEntry createdEntry,
+ public MessageFactory.MetaDataWithContent appendMessageInMailbox(CreationMessageEntry createdEntry,
MessageManager mailbox,
MailboxSession session) throws MailboxException {
ImmutableList<MessageAttachment> messageAttachments = getMessageAttachments(session, createdEntry.getValue().getAttachments());
@@ -85,6 +90,14 @@ public class MessageAppender {
.build();
}
+ public MessageFactory.MetaDataWithContent appendMessageInMailbox(CreationMessageEntry createdEntry,
+ MailboxId mailboxId,
+ MailboxSession session) throws MailboxException {
+ return appendMessageInMailbox(createdEntry,
+ mailboxManager.getMailbox(mailboxId, session),
+ session);
+ }
+
private ImmutableList<MessageAttachment> getMessageAttachments(MailboxSession session, ImmutableList<Attachment> attachments) throws MailboxException {
ThrowingFunction<Attachment, Optional<MessageAttachment>> toMessageAttachment = att -> messageAttachment(session, att);
return attachments.stream()
@@ -102,7 +115,6 @@ public class MessageAppender {
.isInline(attachment.isIsInline())
.build());
} catch (AttachmentNotFoundException e) {
- // should not happen (checked before)
LOGGER.error(String.format("Attachment %s not found", attachment.getBlobId()), e);
return Optional.empty();
} catch (IllegalStateException e) {
http://git-wip-us.apache.org/repos/asf/james-project/blob/fe0522ac/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 870235c..a91a65c 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
@@ -30,9 +30,11 @@ import javax.inject.Inject;
import javax.mail.MessagingException;
import org.apache.james.jmap.exceptions.AttachmentsNotFoundException;
+import org.apache.james.jmap.exceptions.InvalidDraftKeywordsException;
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.Keyword;
import org.apache.james.jmap.model.Message;
import org.apache.james.jmap.model.MessageFactory;
import org.apache.james.jmap.model.MessageFactory.MetaDataWithContent;
@@ -107,11 +109,7 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
private void handleCreate(CreationMessageEntry create, Builder responseBuilder, MailboxSession mailboxSession) {
try {
validateIsUserOwnerOfMailboxes(create, mailboxSession);
- validateImplementedFeature(create, mailboxSession);
- validateArguments(create, mailboxSession);
- MessageWithId created = handleOutboxMessages(create, mailboxSession);
- responseBuilder.created(created.getCreationId(), created.getValue());
-
+ performCreate(create, responseBuilder, mailboxSession);
} catch (MailboxSendingNotAllowedException e) {
responseBuilder.notCreated(create.getCreationId(),
SetError.builder()
@@ -121,6 +119,14 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
Joiner.on(", ").join(e.getAllowedFroms()))
.build());
+ } catch (InvalidDraftKeywordsException e) {
+ responseBuilder.notCreated(create.getCreationId(),
+ SetError.builder()
+ .type("invalidProperties")
+ .properties(MessageProperty.keywords)
+ .description("A draft message should be flagged as Draft")
+ .build());
+
} catch (AttachmentsNotFoundException e) {
responseBuilder.notCreated(create.getCreationId(),
SetMessagesError.builder()
@@ -154,6 +160,7 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
responseBuilder.notCreated(create.getCreationId(),
SetError.builder()
.type("error")
+ .properties(MessageProperty.mailboxIds)
.description("MailboxId invalid")
.build());
@@ -167,15 +174,39 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
}
}
- private void validateImplementedFeature(CreationMessageEntry entry, MailboxSession session) throws MailboxException, MailboxNotImplementedException {
+ private void performCreate(CreationMessageEntry entry, Builder responseBuilder, MailboxSession session) throws MailboxException, MailboxNotImplementedException, MessagingException, AttachmentsNotFoundException {
if (isAppendToMailboxWithRole(Role.DRAFTS, entry.getValue(), session)) {
- throw new MailboxNotImplementedException("Drafts saving is not implemented");
+ saveDraft(entry, responseBuilder, session);
+ } else if (isAppendToMailboxWithRole(Role.OUTBOX, entry.getValue(), session)) {
+ sendMailViaOutbox(entry, responseBuilder, session);
+ } else {
+ throw new MailboxNotImplementedException("The only implemented feature is sending via outbox and draft saving");
}
- if (!isAppendToMailboxWithRole(Role.OUTBOX, entry.getValue(), session)) {
- throw new MailboxNotImplementedException("The only implemented feature is sending via outbox");
+ }
+
+ private void sendMailViaOutbox(CreationMessageEntry entry, Builder responseBuilder, MailboxSession session) throws AttachmentsNotFoundException, MailboxException, MessagingException {
+ validateArguments(entry, session);
+ MessageWithId created = handleOutboxMessages(entry, session);
+ responseBuilder.created(created.getCreationId(), created.getValue());
+ }
+
+ private void saveDraft(CreationMessageEntry entry, Builder responseBuilder, MailboxSession session) throws AttachmentsNotFoundException, MailboxException, MessagingException {
+ attachmentChecker.assertAttachmentsExist(entry, session);
+ assertDraftKeywords(entry);
+ MessageWithId created = handleDraftMessages(entry, session);
+ responseBuilder.created(created.getCreationId(), created.getValue());
+ }
+
+ private void assertDraftKeywords(CreationMessageEntry entry) {
+ Boolean isDraft = entry.getValue()
+ .getKeywords()
+ .map(keywords -> keywords.contains(Keyword.DRAFT))
+ .orElse(false);
+ if (!isDraft) {
+ throw new InvalidDraftKeywordsException();
}
}
-
+
private void validateArguments(CreationMessageEntry entry, MailboxSession session) throws MailboxInvalidMessageCreationException, AttachmentsNotFoundException, MailboxException {
CreationMessage message = entry.getValue();
if (!message.isValid()) {
@@ -201,11 +232,18 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor {
private MessageWithId handleOutboxMessages(CreationMessageEntry entry, MailboxSession session) throws MailboxException, MessagingException {
MessageManager outbox = getMailboxWithRole(session, Role.OUTBOX).orElseThrow(() -> new MailboxNotFoundException(Role.OUTBOX.serialize()));
- MetaDataWithContent newMessage = messageAppender.createMessageInMailbox(entry, outbox, session);
+ MetaDataWithContent newMessage = messageAppender.appendMessageInMailbox(entry, outbox, session);
Message jmapMessage = messageFactory.fromMetaDataWithContent(newMessage);
messageSender.sendMessage(jmapMessage, newMessage, session);
return new ValueWithId.MessageWithId(entry.getCreationId(), jmapMessage);
}
+
+ private MessageWithId handleDraftMessages(CreationMessageEntry entry, MailboxSession session) throws MailboxException, MessagingException {
+ MessageManager draftMailbox = getMailboxWithRole(session, Role.DRAFTS).orElseThrow(() -> new MailboxNotFoundException(Role.DRAFTS.serialize()));
+ MetaDataWithContent newMessage = messageAppender.appendMessageInMailbox(entry, draftMailbox, session);
+ Message jmapMessage = messageFactory.fromMetaDataWithContent(newMessage);
+ return new ValueWithId.MessageWithId(entry.getCreationId(), jmapMessage);
+ }
private boolean isAppendToMailboxWithRole(Role role, CreationMessage entry, MailboxSession mailboxSession) throws MailboxException {
return getMailboxWithRole(mailboxSession, role)
http://git-wip-us.apache.org/repos/asf/james-project/blob/fe0522ac/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 1ef68ea..a0df1b4 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
@@ -41,8 +41,6 @@ import org.apache.james.jmap.model.CreationMessage.DraftEmailer;
import org.apache.james.jmap.model.CreationMessageId;
import org.apache.james.jmap.model.MessageFactory;
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;
@@ -140,7 +138,7 @@ public class SetMessagesCreationProcessorTest {
fakeSystemMailboxesProvider = new TestSystemMailboxesProvider(() -> optionalOutbox, () -> optionalDrafts);
session = new MockMailboxSession(USER);
MIMEMessageConverter mimeMessageConverter = new MIMEMessageConverter();
- messageAppender = new MessageAppender(mockedAttachmentManager, mimeMessageConverter);
+ messageAppender = new MessageAppender(mockedMailboxManager, mockedAttachmentManager, mimeMessageConverter);
messageSender = new MessageSender(mockedMailSpool, mockedMailFactory);
sut = new SetMessagesCreationProcessor(messageFactory,
fakeSystemMailboxesProvider,
@@ -297,25 +295,6 @@ public class SetMessagesCreationProcessorTest {
}
@Test
- public void processShouldReturnNotImplementedErrorWhenSavingToDrafts() {
- 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 {
// When
CreationMessageId creationMessageId = CreationMessageId.of("anything-really");
@@ -323,7 +302,9 @@ public class SetMessagesCreationProcessorTest {
.create(
creationMessageId, creationMessageBuilder.mailboxId(DRAFTS_ID.serialize()).build())
.build();
- when(mockedMailboxManager.getMailbox(any(MailboxId.class), any())).thenReturn(drafts);
+ when(mockedMailboxManager.getMailbox(any(MailboxId.class), any()))
+ .thenReturn(drafts);
+
sut.process(createMessageInDrafts, session);
// Then
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org