JAMES-2232 Flags in JMAP should be handled by Keywords & is attributes
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/35a6e923 Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/35a6e923 Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/35a6e923 Branch: refs/heads/master Commit: 35a6e92304605fb95d2a9cbec607de22cef053d3 Parents: 5f8f0d5 Author: Antoine Duprat <adup...@linagora.com> Authored: Thu Nov 23 15:12:25 2017 +0100 Committer: Antoine Duprat <adup...@linagora.com> Committed: Tue Nov 28 14:49:41 2017 +0100 ---------------------------------------------------------------------- .../integration/SetMessagesMethodTest.java | 357 +++++++++++++++++++ .../cucumber/SetMessagesMethodStepdefs.java | 18 + .../test/resources/cucumber/SetMessages.feature | 11 + .../james/jmap/methods/MessageAppender.java | 22 +- .../methods/SetMessagesCreationProcessor.java | 21 +- .../methods/SetMessagesUpdateProcessor.java | 36 +- .../james/jmap/model/CreationMessage.java | 23 +- .../org/apache/james/jmap/model/Keywords.java | 70 ++-- .../org/apache/james/jmap/model/OldKeyword.java | 46 +++ .../james/jmap/model/UpdateMessagePatch.java | 25 +- .../james/jmap/utils/KeywordsCombiner.java | 6 +- .../james/jmap/json/ParsingWritingObjects.java | 2 +- .../james/jmap/model/CreationMessageTest.java | 15 + .../apache/james/jmap/model/KeywordsTest.java | 178 +-------- .../apache/james/jmap/model/OldKeywordTest.java | 59 ++- .../jmap/model/UpdateMessagePatchTest.java | 35 -- 16 files changed, 641 insertions(+), 283 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/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 00a28d1..2470b51 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 @@ -1429,6 +1429,47 @@ public abstract class SetMessagesMethodTest { } @Test + public void setMessagesShouldMarkAsDraftWhenIsDraftPassed() { + String messageCreationId = "creationId1337"; + String fromAddress = USERNAME; + String requestBody = "[" + + " [" + + " \"setMessages\","+ + " {" + + " \"create\": { \"" + messageCreationId + "\" : {" + + " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," + + " \"to\": [{ \"name\": \"BOB\", \"email\": \"some...@example.com\"}]," + + " \"subject\": \"subject\"," + + " \"isDraft\": true," + + " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + String messageId = given() + .header("Authorization", accessToken.serialize()) + .body(requestBody) + .when() + .post("/jmap") + .then() + .extract() + .body() + .<String>path(ARGUMENTS + ".created." + messageCreationId + ".id"); + + with() + .header("Authorization", accessToken.serialize()) + .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]") + .post("/jmap") + .then() + .log().ifValidationFails() + .body(NAME, equalTo("messages")) + .body(ARGUMENTS + ".list", hasSize(1)) + .body(ARGUMENTS + ".list[0].isDraft", equalTo(true)); + } + + @Test public void setMessagesShouldRejectCreateInDraftAndOutboxForASingleMessage() { String messageCreationId = "creationId1337"; String fromAddress = USERNAME; @@ -4852,4 +4893,320 @@ public abstract class SetMessagesMethodTest { .body(SECOND_ARGUMENTS + ".list", hasSize(1)) .body(SECOND_ARGUMENTS + ".list[0].keywords.$Seen", equalTo(true)); } + + @Test + public void setMessagesShouldCreateMessageWithFlagsWhenFlagsAttributesAreGiven() { + String messageCreationId = "creationId1337"; + String fromAddress = USERNAME; + String requestBody = "[" + + " [" + + " \"setMessages\","+ + " {" + + " \"create\": { \"" + messageCreationId + "\" : {" + + " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," + + " \"to\": [{ \"name\": \"BOB\", \"email\": \"some...@example.com\"}]," + + " \"subject\": \"subject\"," + + " \"isUnread\": true," + + " \"isFlagged\": true," + + " \"isAnswered\": true," + + " \"isDraft\": true," + + " \"isForwarded\": true," + + " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + String messageId = given() + .header("Authorization", accessToken.serialize()) + .body(requestBody) + .when() + .post("/jmap") + .then() + .extract() + .body() + .<String>path(ARGUMENTS + ".created."+ messageCreationId +".id"); + + with() + .header("Authorization", accessToken.serialize()) + .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]") + .post("/jmap") + .then() + .log().ifValidationFails() + .body(NAME, equalTo("messages")) + .body(ARGUMENTS + ".list", hasSize(1)) + .body(ARGUMENTS + ".list[0].isUnread", equalTo(true)) + .body(ARGUMENTS + ".list[0].isFlagged", equalTo(true)) + .body(ARGUMENTS + ".list[0].isAnswered", equalTo(true)) + .body(ARGUMENTS + ".list[0].isDraft", equalTo(true)) + .body(ARGUMENTS + ".list[0].isForwarded", equalTo(true)); + } + + @Test + public void setMessagesShouldUpdateFlagsWhenSomeAreAlreadySet() { + String messageCreationId = "creationId1337"; + String fromAddress = USERNAME; + String requestBody = "[" + + " [" + + " \"setMessages\","+ + " {" + + " \"create\": { \"" + messageCreationId + "\" : {" + + " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," + + " \"to\": [{ \"name\": \"BOB\", \"email\": \"some...@example.com\"}]," + + " \"subject\": \"subject\"," + + " \"isDraft\": true," + + " \"isForwarded\": true," + + " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + String messageId = given() + .header("Authorization", accessToken.serialize()) + .body(requestBody) + .when() + .post("/jmap") + .then() + .extract() + .body() + .<String>path(ARGUMENTS + ".created."+ messageCreationId +".id"); + + String updateRequestBody = "[" + + " [" + + " \"setMessages\","+ + " {" + + " \"update\": { \"" + messageId + "\" : {" + + " \"isUnread\": true," + + " \"isFlagged\": true," + + " \"isAnswered\": true," + + " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + given() + .header("Authorization", accessToken.serialize()) + .body(updateRequestBody) + .when() + .post("/jmap"); + + with() + .header("Authorization", accessToken.serialize()) + .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]") + .post("/jmap") + .then() + .log().ifValidationFails() + .body(NAME, equalTo("messages")) + .body(ARGUMENTS + ".list", hasSize(1)) + .body(ARGUMENTS + ".list[0].isUnread", equalTo(true)) + .body(ARGUMENTS + ".list[0].isFlagged", equalTo(true)) + .body(ARGUMENTS + ".list[0].isAnswered", equalTo(true)) + .body(ARGUMENTS + ".list[0].isDraft", equalTo(true)) + .body(ARGUMENTS + ".list[0].isForwarded", equalTo(true)); + } + + @Test + public void setMessagesShouldRemoveFlagsWhenAskedFor() { + String messageCreationId = "creationId1337"; + String fromAddress = USERNAME; + String requestBody = "[" + + " [" + + " \"setMessages\","+ + " {" + + " \"create\": { \"" + messageCreationId + "\" : {" + + " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," + + " \"to\": [{ \"name\": \"BOB\", \"email\": \"some...@example.com\"}]," + + " \"subject\": \"subject\"," + + " \"isUnread\": true," + + " \"isFlagged\": true," + + " \"isAnswered\": true," + + " \"isDraft\": true," + + " \"isForwarded\": true," + + " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + String messageId = given() + .header("Authorization", accessToken.serialize()) + .body(requestBody) + .when() + .post("/jmap") + .then() + .extract() + .body() + .<String>path(ARGUMENTS + ".created." + messageCreationId + ".id"); + + String updateRequestBody = "[" + + " [" + + " \"setMessages\","+ + " {" + + " \"update\": { \"" + messageId + "\" : {" + + " \"isUnread\": false," + + " \"isFlagged\": false," + + " \"isAnswered\": false," + + " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + given() + .header("Authorization", accessToken.serialize()) + .body(updateRequestBody) + .when() + .post("/jmap"); + + with() + .header("Authorization", accessToken.serialize()) + .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]") + .post("/jmap") + .then() + .log().ifValidationFails() + .body(NAME, equalTo("messages")) + .body(ARGUMENTS + ".list", hasSize(1)) + .body(ARGUMENTS + ".list[0].isUnread", equalTo(false)) + .body(ARGUMENTS + ".list[0].isFlagged", equalTo(false)) + .body(ARGUMENTS + ".list[0].isAnswered", equalTo(false)) + .body(ARGUMENTS + ".list[0].isDraft", equalTo(true)) + .body(ARGUMENTS + ".list[0].isForwarded", equalTo(true)); + } + + @Test + public void setMessagesShouldReturnErrorWhenTryingToChangeDraftFlagAmongOthers() { + String messageCreationId = "creationId1337"; + String fromAddress = USERNAME; + String requestBody = "[" + + " [" + + " \"setMessages\","+ + " {" + + " \"create\": { \"" + messageCreationId + "\" : {" + + " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," + + " \"to\": [{ \"name\": \"BOB\", \"email\": \"some...@example.com\"}]," + + " \"subject\": \"subject\"," + + " \"isUnread\": true," + + " \"isFlagged\": true," + + " \"isAnswered\": true," + + " \"isDraft\": true," + + " \"isForwarded\": true," + + " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + String messageId = given() + .header("Authorization", accessToken.serialize()) + .body(requestBody) + .when() + .post("/jmap") + .then() + .extract() + .body() + .<String>path(ARGUMENTS + ".created." + messageCreationId + ".id"); + + String updateRequestBody = "[" + + " [" + + " \"setMessages\","+ + " {" + + " \"update\": { \"" + messageId + "\" : {" + + " \"isUnread\": false," + + " \"isFlagged\": false," + + " \"isAnswered\": false," + + " \"isDraft\": false," + + " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + given() + .header("Authorization", accessToken.serialize()) + .body(updateRequestBody) + .when() + .post("/jmap") + .then() + .statusCode(400); + } + + @Test + public void setMessagesShouldNotModifyTheMessageWhenTryingToChangeDraftFlagAmongOthers() { + String messageCreationId = "creationId1337"; + String fromAddress = USERNAME; + String requestBody = "[" + + " [" + + " \"setMessages\","+ + " {" + + " \"create\": { \"" + messageCreationId + "\" : {" + + " \"from\": { \"name\": \"Me\", \"email\": \"" + fromAddress + "\"}," + + " \"to\": [{ \"name\": \"BOB\", \"email\": \"some...@example.com\"}]," + + " \"subject\": \"subject\"," + + " \"isUnread\": true," + + " \"isFlagged\": true," + + " \"isAnswered\": true," + + " \"isDraft\": true," + + " \"isForwarded\": true," + + " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + String messageId = given() + .header("Authorization", accessToken.serialize()) + .body(requestBody) + .when() + .post("/jmap") + .then() + .extract() + .body() + .<String>path(ARGUMENTS + ".created." + messageCreationId + ".id"); + + String updateRequestBody = "[" + + " [" + + " \"setMessages\","+ + " {" + + " \"update\": { \"" + messageId + "\" : {" + + " \"isUnread\": false," + + " \"isFlagged\": false," + + " \"isAnswered\": false," + + " \"isDraft\": false," + + " \"mailboxIds\": [\"" + getDraftId(accessToken) + "\"]" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"; + + given() + .header("Authorization", accessToken.serialize()) + .body(updateRequestBody) + .when() + .post("/jmap"); + + with() + .header("Authorization", accessToken.serialize()) + .body("[[\"getMessages\", {\"ids\": [\"" + messageId + "\"]}, \"#0\"]]") + .post("/jmap") + .then() + .log().ifValidationFails() + .body(NAME, equalTo("messages")) + .body(ARGUMENTS + ".list", hasSize(1)) + .body(ARGUMENTS + ".list[0].isUnread", equalTo(true)) + .body(ARGUMENTS + ".list[0].isFlagged", equalTo(true)) + .body(ARGUMENTS + ".list[0].isAnswered", equalTo(true)) + .body(ARGUMENTS + ".list[0].isDraft", equalTo(true)) + .body(ARGUMENTS + ".list[0].isForwarded", equalTo(true)); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java index 8cdd65a..026ac90 100644 --- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/java/org/apache/james/jmap/methods/integration/cucumber/SetMessagesMethodStepdefs.java @@ -202,6 +202,24 @@ public class SetMessagesMethodStepdefs { userStepdefs.execWithUser(username, () -> setFlags(keywords, message)); } + @When("^\"([^\"]*)\" marks the message \"([^\"]*)\" as flagged$") + public void flag(String username, String message) throws Throwable { + MessageId messageId = messageIdStepdefs.getMessageId(message); + + httpClient.post("[" + + " [" + + " \"setMessages\","+ + " {" + + " \"update\": { \"" + messageId.serialize() + "\" : {" + + " \"isFlagged\": true" + + " }}" + + " }," + + " \"#0\"" + + " ]" + + "]"); + mainStepdefs.awaitMethod.run(); + } + @When("^\"([^\"]*)\" destroys message \"([^\"]*)\"$") public void destroyMessage(String username, String message) throws Throwable { MessageId messageId = messageIdStepdefs.getMessageId(message); http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature ---------------------------------------------------------------------- diff --git a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature index 46ede38..f3ac1bf 100644 --- a/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature +++ b/server/protocols/jmap-integration-testing/jmap-integration-testing-common/src/test/resources/cucumber/SetMessages.feature @@ -80,6 +80,11 @@ Feature: SetMessages method on shared folders # Flags update + Scenario: A user can update the flags on a message + Given "al...@domain.tld" sets flags "$Flagged,$Seen" on message "mAlice" + When "al...@domain.tld" sets flags "$Flagged,$Forwarded" on message "mAlice" + Then "al...@domain.tld" should see message "mAlice" with keywords $Flagged,$Forwarded + Scenario: A delegated user add keywords on a delegated message when having "write" right Given "b...@domain.tld" shares his mailbox "shared" with "al...@domain.tld" with "lrw" rights When "al...@domain.tld" sets flags "$Flagged" on message "mBob" @@ -117,6 +122,12 @@ Feature: SetMessages method on shared folders Then message "mDraft" is not updated And "b...@domain.tld" should see message "mDraft" with keywords $Draft + Scenario: A user can add a flag on a draft + Given "b...@domain.tld" has a mailbox "Drafts" + And "b...@domain.tld" tries to create a draft message "mDraft" in mailbox "Drafts" + When "b...@domain.tld" marks the message "mDraft" as flagged + Then "b...@domain.tld" should see message "mDraft" with keywords $Draft,$Flagged + Scenario: A user can destroy a draft Given "b...@domain.tld" has a mailbox "Drafts" And "b...@domain.tld" tries to create a draft message "mDraft" in mailbox "Drafts" http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/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 4d09810..763ba27 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 @@ -23,12 +23,15 @@ import java.util.Date; import java.util.Optional; import javax.inject.Inject; +import javax.mail.Flags; 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.CreationMessage; import org.apache.james.jmap.model.Keywords; import org.apache.james.jmap.model.MessageFactory; +import org.apache.james.jmap.model.OldKeyword; import org.apache.james.mailbox.AttachmentManager; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; @@ -71,16 +74,13 @@ public class MessageAppender { SharedByteArrayInputStream content = new SharedByteArrayInputStream(messageContent); Date internalDate = Date.from(createdEntry.getValue().getDate().toInstant()); - Keywords keywords = createdEntry.getValue() - .getKeywords() - .orElse(Keywords.DEFAULT_VALUE); boolean notRecent = false; - ComposedMessageId message = mailbox.appendMessage(content, internalDate, session, notRecent, keywords.asFlags()); + ComposedMessageId message = mailbox.appendMessage(content, internalDate, session, notRecent, getFlags(createdEntry.getValue())); return MessageFactory.MetaDataWithContent.builder() .uid(message.getUid()) - .keywords(keywords) + .keywords(keywordsOrDefault(createdEntry.getValue())) .internalDate(internalDate.toInstant()) .sharedContent(content) .size(messageContent.length) @@ -90,6 +90,18 @@ public class MessageAppender { .build(); } + private Flags getFlags(CreationMessage message) { + return message.getOldKeyword() + .map(OldKeyword::asFlags) + .orElseGet(() -> keywordsOrDefault(message) + .asFlags()); + } + + private Keywords keywordsOrDefault(CreationMessage message) { + return message.getKeywords() + .orElse(Keywords.DEFAULT_VALUE); + } + public MessageFactory.MetaDataWithContent appendMessageInMailbox(CreationMessageEntry createdEntry, MailboxId mailboxId, MailboxSession session) throws MailboxException { http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/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 964ceeb..2f60b82 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 @@ -189,32 +189,37 @@ public class SetMessagesCreationProcessor implements SetMessagesProcessor { private void sendMailViaOutbox(CreationMessageEntry entry, Builder responseBuilder, MailboxSession session) throws AttachmentsNotFoundException, MailboxException, MessagingException { validateArguments(entry, session); - assertNoDraftKeywords(entry); + assertNoDraftKeywords(entry.getValue()); 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); + assertDraftKeywords(entry.getValue()); MessageWithId created = handleDraftMessages(entry, session); responseBuilder.created(created.getCreationId(), created.getValue()); } - private void assertDraftKeywords(CreationMessageEntry entry) { - if (!isDraft(entry)) { + private void assertDraftKeywords(CreationMessage creationMessage) { + if (!isDraft(creationMessage)) { throw new InvalidDraftKeywordsException("A draft message should be flagged as Draft"); } } - private void assertNoDraftKeywords(CreationMessageEntry entry) { - if (isDraft(entry)) { + private void assertNoDraftKeywords(CreationMessage creationMessage) { + if (isDraft(creationMessage)) { throw new InvalidDraftKeywordsException("A draft message can not be created out of draft mailbox"); } } - private Boolean isDraft(CreationMessageEntry entry) { - return entry.getValue() + private Boolean isDraft(CreationMessage creationMessage) { + if (creationMessage.getOldKeyword().isPresent()) { + return creationMessage.getOldKeyword().get() + .isDraft() + .orElse(false); + } + return creationMessage .getKeywords() .map(keywords -> keywords.contains(Keyword.DRAFT)) .orElse(false); http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java index e9de2f9..f7cbe62 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/methods/SetMessagesUpdateProcessor.java @@ -40,6 +40,7 @@ import org.apache.james.core.MailAddress; import org.apache.james.jmap.exceptions.DraftMessageMailboxUpdateException; import org.apache.james.jmap.exceptions.InvalidOutboxMoveException; import org.apache.james.jmap.model.MessageProperties; +import org.apache.james.jmap.model.OldKeyword; import org.apache.james.jmap.model.SetError; import org.apache.james.jmap.model.SetMessagesRequest; import org.apache.james.jmap.model.SetMessagesResponse; @@ -49,6 +50,7 @@ import org.apache.james.jmap.utils.SystemMailboxesProvider; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MessageIdManager; import org.apache.james.mailbox.MessageManager; +import org.apache.james.mailbox.MessageManager.FlagsUpdateMode; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MailboxNotFoundException; import org.apache.james.mailbox.model.FetchGroupImpl; @@ -242,10 +244,23 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor { } private List<Flags> patchFlags(List<MessageResult> messagesToBeUpdated, UpdateMessagePatch updateMessagePatch) { + return updateMessagePatch.getOldKeyword() + .map(oldKeyword -> flagsFromOldKeyword(messagesToBeUpdated, oldKeyword)) + .orElse(flagsFromKeywords(messagesToBeUpdated, updateMessagePatch)); + } + + private List<Flags> flagsFromOldKeyword(List<MessageResult> messagesToBeUpdated, OldKeyword oldKeyword) { return messagesToBeUpdated.stream() - .map(MessageResult::getFlags) - .map(updateMessagePatch::applyToState) - .collect(Guavate.toImmutableList()); + .map(MessageResult::getFlags) + .map(flags -> oldKeyword.applyToState(flags)) + .collect(Guavate.toImmutableList()); + } + + private ImmutableList<Flags> flagsFromKeywords(List<MessageResult> messagesToBeUpdated, UpdateMessagePatch updateMessagePatch) { + return messagesToBeUpdated.stream() + .map(MessageResult::getFlags) + .map(updateMessagePatch::applyToState) + .collect(Guavate.toImmutableList()); } private List<MailboxId> mailboxIdFor(Role role, MailboxSession session) throws MailboxException { @@ -285,8 +300,7 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor { private Stream<MailboxException> updateFlags(MessageId messageId, UpdateMessagePatch updateMessagePatch, MailboxSession mailboxSession, MessageResult messageResult) { try { if (!updateMessagePatch.isFlagsIdentity()) { - Flags newState = updateMessagePatch.applyToState(messageResult.getFlags()); - messageIdManager.setFlags(newState, MessageManager.FlagsUpdateMode.REPLACE, messageId, ImmutableList.of(messageResult.getMailboxId()), mailboxSession); + messageIdManager.setFlags(newState(messageResult, updateMessagePatch), FlagsUpdateMode.REPLACE, messageId, ImmutableList.of(messageResult.getMailboxId()), mailboxSession); } return Stream.of(); } catch (MailboxException e) { @@ -294,6 +308,18 @@ public class SetMessagesUpdateProcessor implements SetMessagesProcessor { } } + private Flags newState(MessageResult messageResult, UpdateMessagePatch updateMessagePatch) { + return updateMessagePatch.getOldKeyword() + .map(oldKeyword -> firstFlagsFromOldKeyword(ImmutableList.of(messageResult), oldKeyword)) + .orElse(updateMessagePatch.applyToState(messageResult.getFlags())); + } + + private Flags firstFlagsFromOldKeyword(List<MessageResult> messagesToBeUpdated, OldKeyword oldKeyword) { + return flagsFromOldKeyword(messagesToBeUpdated, oldKeyword).stream() + .findFirst() + .orElse(null); + } + private void setInMailboxes(MessageId messageId, UpdateMessagePatch updateMessagePatch, Stream<Flags> originalFlags, MailboxSession mailboxSession) throws MailboxException { Optional<List<String>> serializedMailboxIds = updateMessagePatch.getMailboxIds(); if (serializedMailboxIds.isPresent()) { http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/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 097d172..1fca744 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 @@ -218,17 +218,22 @@ public class CreationMessage { ImmutableMap<BlobId, SubMessage> attachedMessages = this.attachedMessages.build(); Preconditions.checkState(areAttachedMessagesKeysInAttachments(attachments, attachedMessages), "'attachedMessages' keys must be in 'attachments'"); - Optional<Keywords> creationKeywords = Keywords.factory() - .throwOnImapNonExposedKeywords() - .fromMapOrOldKeyword(keywords, getOldKeywords()); - if (date == null) { date = ZonedDateTime.now(); } + Optional<Keywords> maybeKeywords = creationKeywords(); + Optional<OldKeyword> oldKeywords = getOldKeywords(); + Preconditions.checkArgument(!(maybeKeywords.isPresent() && oldKeywords.isPresent()), "Does not support keyword and is* at the same time"); return new CreationMessage(mailboxIds, Optional.ofNullable(inReplyToMessageId), headers.build(), from, to.build(), cc.build(), bcc.build(), replyTo.build(), subject, date, Optional.ofNullable(textBody), Optional.ofNullable(htmlBody), - attachments, attachedMessages, creationKeywords); + attachments, attachedMessages, maybeKeywords, oldKeywords); + } + + private Optional<Keywords> creationKeywords() { + return keywords.map(map -> Keywords.factory() + .throwOnImapNonExposedKeywords() + .fromMap(map)); } private Optional<OldKeyword> getOldKeywords() { @@ -254,11 +259,12 @@ public class CreationMessage { private final ImmutableList<Attachment> attachments; private final ImmutableMap<BlobId, SubMessage> attachedMessages; private final Optional<Keywords> keywords; + private final Optional<OldKeyword> oldKeyword; @VisibleForTesting CreationMessage(ImmutableList<String> mailboxIds, Optional<String> inReplyToMessageId, ImmutableMap<String, String> headers, Optional<DraftEmailer> from, ImmutableList<DraftEmailer> to, ImmutableList<DraftEmailer> cc, ImmutableList<DraftEmailer> bcc, ImmutableList<DraftEmailer> replyTo, String subject, ZonedDateTime date, Optional<String> textBody, Optional<String> htmlBody, ImmutableList<Attachment> attachments, - ImmutableMap<BlobId, SubMessage> attachedMessages, Optional<Keywords> keywords) { + ImmutableMap<BlobId, SubMessage> attachedMessages, Optional<Keywords> keywords, Optional<OldKeyword> oldKeyword) { this.mailboxIds = mailboxIds; this.inReplyToMessageId = inReplyToMessageId; this.headers = headers; @@ -274,6 +280,7 @@ public class CreationMessage { this.attachments = attachments; this.attachedMessages = attachedMessages; this.keywords = keywords; + this.oldKeyword = oldKeyword; } public ImmutableList<String> getMailboxIds() { @@ -340,6 +347,10 @@ public class CreationMessage { return keywords; } + public Optional<OldKeyword> getOldKeyword() { + return oldKeyword; + } + public List<ValidationResult> validate() { ImmutableList.Builder<ValidationResult> errors = ImmutableList.builder(); assertValidFromProvided(errors); http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java index 286468f..5df79fa 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Keywords.java @@ -43,7 +43,7 @@ import com.google.common.collect.ImmutableSet; public class Keywords { - public static final Keywords DEFAULT_VALUE = factory().fromSet(ImmutableSet.of()); + public static final Keywords DEFAULT_VALUE = factory().fromSet(ImmutableSet.of(), Optional.empty()); private static final Logger LOGGER = LoggerFactory.getLogger(Keywords.class); public interface KeywordsValidator { @@ -78,24 +78,27 @@ public class Keywords { return this; } - public Keywords fromSet(Set<Keyword> setKeywords) { + public Keywords fromSet(Set<Keyword> setKeywords, Optional<OldKeyword> oldKeyword) { validator.orElse(keywords -> {}) .validate(setKeywords); return new Keywords(setKeywords.stream() - .filter(filter.orElse(keyword -> true)) - .collect(Guavate.toImmutableSet())); + .filter(filter.orElse(keyword -> true)) + .collect(Guavate.toImmutableSet()), + oldKeyword); } public Keywords from(Keyword... keywords) { return fromSet(Arrays.stream(keywords) - .collect(Guavate.toImmutableSet())); + .collect(Guavate.toImmutableSet()), + Optional.empty()); } public Keywords fromList(List<String> keywords) { return fromSet(keywords.stream() - .map(Keyword::new) - .collect(Guavate.toImmutableSet())); + .map(Keyword::new) + .collect(Guavate.toImmutableSet()), + Optional.empty()); } @VisibleForTesting @@ -108,37 +111,17 @@ public class Keywords { .map(Keyword::new) .collect(Guavate.toImmutableSet()); - return fromSet(setKeywords); - } - - @VisibleForTesting - Keywords fromOldKeyword(OldKeyword oldKeyword) { - ImmutableSet.Builder<Keyword> builder = ImmutableSet.builder(); - if (oldKeyword.isAnswered().orElse(false)) { - builder.add(Keyword.ANSWERED); - } - if (oldKeyword.isDraft().orElse(false)) { - builder.add(Keyword.DRAFT); - } - if (oldKeyword.isFlagged().orElse(false)) { - builder.add(Keyword.FLAGGED); - } - if (oldKeyword.isUnread().isPresent() && oldKeyword.isUnread().get() == false) { - builder.add(Keyword.SEEN); - } - if (oldKeyword.isForwarded().orElse(false)) { - builder.add(Keyword.FORWARDED); - } - return fromSet(builder.build()); + return fromSet(setKeywords, Optional.empty()); } public Keywords fromFlags(Flags flags) { return fromSet(Stream.concat( - Stream.of(flags.getUserFlags()) - .flatMap(this::asKeyword), - Stream.of(flags.getSystemFlags()) - .map(Keyword::fromFlag)) - .collect(Guavate.toImmutableSet())); + Stream.of(flags.getUserFlags()) + .flatMap(this::asKeyword), + Stream.of(flags.getSystemFlags()) + .map(Keyword::fromFlag)) + .collect(Guavate.toImmutableSet()), + Optional.empty()); } private Stream<Keyword> asKeyword(String flagName) { @@ -149,15 +132,6 @@ public class Keywords { return Stream.of(); } } - - public Optional<Keywords> fromMapOrOldKeyword(Optional<Map<String, Boolean>> mapKeyword, Optional<OldKeyword> oldKeyword) { - Preconditions.checkArgument(!(mapKeyword.isPresent() && oldKeyword.isPresent()), "Does not support keyword and is* at the same time"); - - Keywords keywords = mapKeyword.map(this::fromMap) - .orElse(oldKeyword.map(this::fromOldKeyword) - .orElse(null)); - return Optional.ofNullable(keywords); - } } public static KeywordsFactory factory() { @@ -165,9 +139,11 @@ public class Keywords { } private final ImmutableSet<Keyword> keywords; + private final Optional<OldKeyword> oldKeyword; - private Keywords(ImmutableSet<Keyword> keywords) { + private Keywords(ImmutableSet<Keyword> keywords, Optional<OldKeyword> oldKeyword) { this.keywords = keywords; + this.oldKeyword = oldKeyword; } public Flags asFlags() { @@ -205,20 +181,22 @@ public class Keywords { public final boolean equals(Object other) { if (other instanceof Keywords) { Keywords otherKeyword = (Keywords) other; - return Objects.equal(keywords, otherKeyword.keywords); + return Objects.equal(keywords, otherKeyword.keywords) + && Objects.equal(oldKeyword, otherKeyword.oldKeyword); } return false; } @Override public final int hashCode() { - return Objects.hashCode(keywords); + return Objects.hashCode(keywords, oldKeyword); } @Override public String toString() { return MoreObjects.toStringHelper(this) .add("keywords", keywords) + .add("oldKeyword", oldKeyword) .toString(); } http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java index 207b275..7620873 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/OldKeyword.java @@ -21,6 +21,8 @@ package org.apache.james.jmap.model; import java.util.Optional; +import javax.mail.Flags; + import com.google.common.annotations.VisibleForTesting; import com.google.common.base.MoreObjects; import com.google.common.base.Objects; @@ -70,6 +72,50 @@ public class OldKeyword { return isForwarded; } + public Flags applyToState(Flags currentFlags) { + Flags newStateFlags = new Flags(); + + if (isFlagged().orElse(currentFlags.contains(Flags.Flag.FLAGGED))) { + newStateFlags.add(Flags.Flag.FLAGGED); + } + if (isAnswered().orElse(currentFlags.contains(Flags.Flag.ANSWERED))) { + newStateFlags.add(Flags.Flag.ANSWERED); + } + if (isDraft().orElse(currentFlags.contains(Flags.Flag.DRAFT))) { + newStateFlags.add(Flags.Flag.DRAFT); + } + if (isForwarded().orElse(currentFlags.contains(new Flags("$Forwarded")))) { + newStateFlags.add(new Flags("$Forwarded")); + } + boolean shouldMessageBeMarkSeen = isUnread().map(b -> !b).orElse(currentFlags.contains(Flags.Flag.SEEN)); + if (shouldMessageBeMarkSeen) { + newStateFlags.add(Flags.Flag.SEEN); + } + return newStateFlags; + } + + public Flags asFlags() { + Flags newStateFlags = new Flags(); + + if (isFlagged().orElse(false)) { + newStateFlags.add(Flags.Flag.FLAGGED); + } + if (isAnswered().orElse(false)) { + newStateFlags.add(Flags.Flag.ANSWERED); + } + if (isDraft().orElse(false)) { + newStateFlags.add(Flags.Flag.DRAFT); + } + if (isForwarded().orElse(false)) { + newStateFlags.add(new Flags("$Forwarded")); + } + boolean shouldMessageBeMarkSeen = isUnread().map(b -> !b).orElse(false); + if (shouldMessageBeMarkSeen) { + newStateFlags.add(Flags.Flag.SEEN); + } + return newStateFlags; + } + @Override public final boolean equals(Object other) { if (other instanceof OldKeyword) { http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java index e0b73e2..e735778 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/UpdateMessagePatch.java @@ -31,6 +31,7 @@ import org.apache.james.jmap.methods.ValidationResult; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -96,11 +97,17 @@ public class UpdateMessagePatch { .build())); } - Optional<Keywords> updateKeywords = Keywords.factory() - .throwOnImapNonExposedKeywords() - .fromMapOrOldKeyword(keywords, getOldKeywords()); + Optional<Keywords> mayBeKeywords = creationKeywords(); + Optional<OldKeyword> oldKeywords = getOldKeywords(); + Preconditions.checkArgument(!(mayBeKeywords.isPresent() && oldKeywords.isPresent()), "Does not support keyword and is* at the same time"); - return new UpdateMessagePatch(mailboxIds, updateKeywords, ImmutableList.copyOf(validationResult)); + return new UpdateMessagePatch(mailboxIds, mayBeKeywords, oldKeywords, ImmutableList.copyOf(validationResult)); + } + + private Optional<Keywords> creationKeywords() { + return keywords.map(map -> Keywords.factory() + .throwOnImapNonExposedKeywords() + .fromMap(map)); } private Optional<OldKeyword> getOldKeywords() { @@ -115,16 +122,18 @@ public class UpdateMessagePatch { private final Optional<List<String>> mailboxIds; private final Optional<Keywords> keywords; - + private final Optional<OldKeyword> oldKeywords; private final ImmutableList<ValidationResult> validationErrors; @VisibleForTesting UpdateMessagePatch(Optional<List<String>> mailboxIds, Optional<Keywords> keywords, + Optional<OldKeyword> oldKeywords, ImmutableList<ValidationResult> validationResults) { this.mailboxIds = mailboxIds; this.keywords = keywords; + this.oldKeywords = oldKeywords; this.validationErrors = validationResults; } @@ -136,8 +145,12 @@ public class UpdateMessagePatch { return keywords; } + public Optional<OldKeyword> getOldKeyword() { + return oldKeywords; + } + public boolean isFlagsIdentity() { - return !keywords.isPresent(); + return !oldKeywords.isPresent() && !keywords.isPresent(); } public ImmutableList<ValidationResult> getValidationErrors() { http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/KeywordsCombiner.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/KeywordsCombiner.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/KeywordsCombiner.java index 258f5df..2dba575 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/KeywordsCombiner.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/utils/KeywordsCombiner.java @@ -20,6 +20,7 @@ package org.apache.james.jmap.utils; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.function.BinaryOperator; @@ -45,8 +46,9 @@ public class KeywordsCombiner implements BinaryOperator<Keywords> { public Keywords apply(Keywords keywords, Keywords keywords2) { return keywordsFactory .fromSet(Sets.union( - union(keywords.getKeywords(), keywords2.getKeywords(), KEYWORD_NOT_TO_UNION), - intersect(keywords.getKeywords(), keywords2.getKeywords(), KEYWORD_TO_INTERSECT))); + union(keywords.getKeywords(), keywords2.getKeywords(), KEYWORD_NOT_TO_UNION), + intersect(keywords.getKeywords(), keywords2.getKeywords(), KEYWORD_TO_INTERSECT)), + Optional.empty()); } public Set<Keyword> union(Set<Keyword> set1, Set<Keyword> set2, List<Keyword> exceptKeywords) { http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ParsingWritingObjects.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ParsingWritingObjects.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ParsingWritingObjects.java index b81731a..427a6d9 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ParsingWritingObjects.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/json/ParsingWritingObjects.java @@ -77,7 +77,7 @@ public interface ParsingWritingObjects { .threadId(Common.THREAD_ID) .mailboxIds(Common.MAILBOX_IDS) .inReplyToMessageId(Common.IN_REPLY_TO_MESSAGE_ID) - .keywords(Keywords.factory().fromSet(Common.KEYWORDS)) + .keywords(Keywords.factory().fromSet(Common.KEYWORDS, Optional.empty())) .headers(Common.HEADERS) .from(Common.FROM) .to(Common.TO) http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/CreationMessageTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/CreationMessageTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/CreationMessageTest.java index e7762aa..d31e0a1 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/CreationMessageTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/CreationMessageTest.java @@ -20,6 +20,9 @@ package org.apache.james.jmap.model; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Optional; import org.apache.james.jmap.methods.ValidationResult; import org.apache.james.jmap.model.CreationMessage.DraftEmailer; @@ -42,6 +45,18 @@ public class CreationMessageTest { } @Test + public void buildShouldThrowWhenBothMapAndOldKeyword() { + assertThatThrownBy(() -> CreationMessage.builder() + .mailboxIds(ImmutableList.of("ba9-0f-dead-beef")) + .headers(ImmutableMap.of()) + .keywords(ImmutableMap.of("$Draft", true)) + .isAnswered(Optional.of(true)) + .build()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Does not support keyword and is* at the same time"); + } + + @Test public void validateShouldReturnErrorWhenFromIsMissing() { testedBuilder = testedBuilder .subject("anything"); http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java index 5fa1d56..4c3ca03 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/KeywordsTest.java @@ -77,172 +77,16 @@ public class KeywordsTest { @Test public void fromSetShouldReturnKeywordsFromSetOfKeywords() throws Exception { Keywords keywords = Keywords.factory() - .fromSet(ImmutableSet.of(Keyword.ANSWERED)); + .fromSet(ImmutableSet.of(Keyword.ANSWERED), Optional.empty()); assertThat(keywords.getKeywords()) .containsOnly(Keyword.ANSWERED); } @Test - public void fromOldKeywordsShouldReturnKeywordsFromIsAnswered() throws Exception { - Optional<Boolean> isAnswered = Optional.of(true); - Optional<Boolean> isUnread = Optional.empty(); - Optional<Boolean> isFlagged = Optional.empty(); - Optional<Boolean> isDraft = Optional.empty(); - Optional<Boolean> isForwarded = Optional.empty(); - Keywords keywords = Keywords.factory() - .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded)); - - assertThat(keywords.getKeywords()) - .containsOnly(Keyword.ANSWERED); - } - - @Test - public void fromOldKeywordsShouldReturnKeywordsFromIsForwarded() throws Exception { - Optional<Boolean> isAnswered = Optional.empty(); - Optional<Boolean> isUnread = Optional.empty(); - Optional<Boolean> isFlagged = Optional.empty(); - Optional<Boolean> isDraft = Optional.empty(); - Optional<Boolean> isForwarded = Optional.of(true); - Keywords keywords = Keywords.factory() - .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded)); - - assertThat(keywords.getKeywords()) - .containsOnly(Keyword.FORWARDED); - } - - @Test - public void fromOldKeywordsShouldReturnKeywordsFromIsDraft() throws Exception { - Optional<Boolean> isAnswered = Optional.empty(); - Optional<Boolean> isUnread = Optional.empty(); - Optional<Boolean> isFlagged = Optional.empty(); - Optional<Boolean> isDraft = Optional.of(true); - Optional<Boolean> isForwarded = Optional.empty(); - Keywords keywords = Keywords.factory() - .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded)); - - assertThat(keywords.getKeywords()) - .containsOnly(Keyword.DRAFT); - } - - @Test - public void fromOldKeywordsShouldReturnKeywordsFromIsFlagged() throws Exception { - Optional<Boolean> isAnswered = Optional.empty(); - Optional<Boolean> isUnread = Optional.empty(); - Optional<Boolean> isFlagged = Optional.of(true); - Optional<Boolean> isDraft = Optional.empty(); - Optional<Boolean> isForwarded = Optional.empty(); - Keywords keywords = Keywords.factory() - .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded)); - - assertThat(keywords.getKeywords()) - .containsOnly(Keyword.FLAGGED); - } - - @Test - public void fromOldKeywordsShouldReturnKeywordsFromIsUnread() throws Exception { - Optional<Boolean> isAnswered = Optional.empty(); - Optional<Boolean> isUnread = Optional.of(false); - Optional<Boolean> isFlagged = Optional.empty(); - Optional<Boolean> isDraft = Optional.empty(); - Optional<Boolean> isForwarded = Optional.empty(); - Keywords keywords = Keywords.factory() - .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded)); - - assertThat(keywords.getKeywords()) - .containsOnly(Keyword.SEEN); - } - - @Test - public void fromOldKeywordsShouldReturnKeywordsFromIsFlag() throws Exception { - Optional<Boolean> isAnswered = Optional.of(true); - Optional<Boolean> isUnread = Optional.of(true); - Optional<Boolean> isFlagged = Optional.of(true); - Optional<Boolean> isDraft = Optional.of(true); - Optional<Boolean> isForwarded = Optional.empty(); - Keywords keywords = Keywords.factory() - .fromOldKeyword(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded)); - - assertThat(keywords.getKeywords()) - .containsOnly(Keyword.ANSWERED, Keyword.DRAFT, Keyword.FLAGGED); - } - - @Test - public void fromMapOrOldKeywordShouldReturnEmptyWhenBothAreEmpty() throws Exception { - assertThat(Keywords.factory() - .fromMapOrOldKeyword(Optional.empty(), Optional.empty())) - .isEmpty(); - } - - @Test - public void fromMapOrOldKeywordShouldReturnKeywordsWhenMap() throws Exception { - assertThat(Keywords.factory() - .fromMapOrOldKeyword(Optional.of(ImmutableMap.of()), Optional.empty())) - .isPresent(); - } - - @Test - public void fromMapOrOldKeywordShouldReturnKeywordsWhenOldKeyword() throws Exception { - Optional<Boolean> isAnswered = Optional.of(true); - Optional<Boolean> isUnread = Optional.of(true); - Optional<Boolean> isFlagged = Optional.of(true); - Optional<Boolean> isDraft = Optional.of(true); - Optional<Boolean> isForwarded = Optional.of(true); - - OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded); - - assertThat(Keywords.factory() - .fromMapOrOldKeyword(Optional.empty(), Optional.of(oldKeyword))) - .isPresent(); - } - - @Test - public void fromMapOrOldKeywordShouldThrownWhenBothMapAndOldKeyword() throws Exception { - expectedException.expect(IllegalArgumentException.class); - Optional<Boolean> isAnswered = Optional.of(true); - Optional<Boolean> isUnread = Optional.of(true); - Optional<Boolean> isFlagged = Optional.of(true); - Optional<Boolean> isDraft = Optional.of(true); - Optional<Boolean> isForwarded = Optional.of(true); - - OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded); - - Keywords.factory() - .fromMapOrOldKeyword(Optional.of(ImmutableMap.of()), Optional.of(oldKeyword)); - } - - @Test - public void fromMapOrOldKeywordShouldReturnKeywordsFromMap() throws Exception { - ImmutableMap<String, Boolean> mapKeywords = ImmutableMap.of(ANY_KEYWORD, Keyword.FLAG_VALUE); - - assertThat(Keywords.factory() - .fromMapOrOldKeyword(Optional.of(mapKeywords), Optional.empty()) - .get() - .getKeywords()) - .containsOnly(new Keyword(ANY_KEYWORD)); - } - - @Test - public void fromMapOrOldKeywordShouldReturnKeywordsFromOldKeyword() throws Exception { - Optional<Boolean> isAnswered = Optional.of(true); - Optional<Boolean> isUnread = Optional.of(true); - Optional<Boolean> isFlagged = Optional.of(true); - Optional<Boolean> isDraft = Optional.of(true); - Optional<Boolean> isForwarded = Optional.of(true); - - OldKeyword oldKeyword = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded); - - Keywords keywords = Keywords.factory() - .fromMapOrOldKeyword(Optional.empty(), Optional.of(oldKeyword)) - .get(); - assertThat(keywords.getKeywords()) - .containsOnly(Keyword.ANSWERED, Keyword.DRAFT, Keyword.FLAGGED, Keyword.FORWARDED); - } - - @Test public void asFlagsShouldBuildFlagsFromKeywords() throws Exception { assertThat(Keywords.factory() - .fromSet(ImmutableSet.of(Keyword.ANSWERED)) + .fromSet(ImmutableSet.of(Keyword.ANSWERED), Optional.empty()) .asFlags()) .isEqualTo(new Flags(Flags.Flag.ANSWERED)); } @@ -258,7 +102,7 @@ public class KeywordsTest { .build(); assertThat(Keywords.factory() - .fromSet(ImmutableSet.of(Keyword.ANSWERED)) + .fromSet(ImmutableSet.of(Keyword.ANSWERED), Optional.empty()) .asFlagsWithRecentAndDeletedFrom(originFlags)) .isEqualTo(expectedFlags); } @@ -274,7 +118,7 @@ public class KeywordsTest { .build(); assertThat(Keywords.factory() - .fromSet(ImmutableSet.of(Keyword.ANSWERED)) + .fromSet(ImmutableSet.of(Keyword.ANSWERED), Optional.empty()) .asFlagsWithRecentAndDeletedFrom(originFlags)) .isEqualTo(expectedFlags); } @@ -282,7 +126,7 @@ public class KeywordsTest { @Test public void asMapShouldReturnEmptyWhenEmptyMapOfStringAndBoolean() throws Exception { assertThat(Keywords.factory() - .fromSet(ImmutableSet.of()) + .fromSet(ImmutableSet.of(), Optional.empty()) .asMap()) .isEmpty();; } @@ -291,7 +135,7 @@ public class KeywordsTest { public void asMapShouldReturnMapOfStringAndBoolean() throws Exception { Map<String, Boolean> expectedMap = ImmutableMap.of("$Answered", Keyword.FLAG_VALUE); assertThat(Keywords.factory() - .fromSet(ImmutableSet.of(Keyword.ANSWERED)) + .fromSet(ImmutableSet.of(Keyword.ANSWERED), Optional.empty()) .asMap()) .isEqualTo(expectedMap); } @@ -302,14 +146,14 @@ public class KeywordsTest { Keywords.factory() .throwOnImapNonExposedKeywords() - .fromSet(ImmutableSet.of(Keyword.DRAFT, Keyword.DELETED)); + .fromSet(ImmutableSet.of(Keyword.DRAFT, Keyword.DELETED), Optional.empty()); } @Test public void throwWhenUnsupportedKeywordShouldNotThrowWhenHaveDraft() throws Exception { Keywords keywords = Keywords.factory() .throwOnImapNonExposedKeywords() - .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DRAFT)); + .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DRAFT), Optional.empty()); assertThat(keywords.getKeywords()) .containsOnly(Keyword.ANSWERED, Keyword.DRAFT); @@ -319,7 +163,7 @@ public class KeywordsTest { public void filterUnsupportedShouldFilter() throws Exception { Keywords keywords = Keywords.factory() .filterImapNonExposedKeywords() - .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DELETED, Keyword.RECENT, Keyword.DRAFT)); + .fromSet(ImmutableSet.of(Keyword.ANSWERED, Keyword.DELETED, Keyword.RECENT, Keyword.DRAFT), Optional.empty()); assertThat(keywords.getKeywords()) .containsOnly(Keyword.ANSWERED, Keyword.DRAFT); @@ -328,7 +172,7 @@ public class KeywordsTest { @Test public void containsShouldReturnTrueWhenKeywordsContainKeyword() { Keywords keywords = Keywords.factory() - .fromSet(ImmutableSet.of(Keyword.SEEN)); + .fromSet(ImmutableSet.of(Keyword.SEEN), Optional.empty()); assertThat(keywords.contains(Keyword.SEEN)).isTrue(); } @@ -336,7 +180,7 @@ public class KeywordsTest { @Test public void containsShouldReturnFalseWhenKeywordsDoNotContainKeyword() { Keywords keywords = Keywords.factory() - .fromSet(ImmutableSet.of()); + .fromSet(ImmutableSet.of(), Optional.empty()); assertThat(keywords.contains(Keyword.SEEN)).isFalse(); } http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java index 024271a..46db6b8 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/OldKeywordTest.java @@ -7,7 +7,7 @@ * "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 * + * 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 * @@ -19,13 +19,68 @@ package org.apache.james.jmap.model; -import nl.jqno.equalsverifier.EqualsVerifier; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Optional; + +import javax.mail.Flags; +import javax.mail.Flags.Flag; + import org.junit.Test; +import nl.jqno.equalsverifier.EqualsVerifier; + public class OldKeywordTest { @Test public void shouldRespectBeanContract() { EqualsVerifier.forClass(OldKeyword.class).verify(); } + @Test + public void applyStateShouldSetFlaggedOnlyWhenIsFlagged() { + Optional<Boolean> isUnread = Optional.empty(); + Optional<Boolean> isFlagged = Optional.of(true); + Optional<Boolean> isAnswered = Optional.empty(); + Optional<Boolean> isDraft = Optional.empty(); + Optional<Boolean> isForwarded = Optional.empty(); + OldKeyword testee = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded); + + assertThat(testee.applyToState(new Flags())).isEqualTo(new Flags(Flag.FLAGGED)); + } + + @Test + public void applyStateShouldRemoveFlaggedWhenEmptyIsFlaggedOnFlaggedMessage() { + Optional<Boolean> isUnread = Optional.empty(); + Optional<Boolean> isFlagged = Optional.of(false); + Optional<Boolean> isAnswered = Optional.empty(); + Optional<Boolean> isDraft = Optional.empty(); + Optional<Boolean> isForwarded = Optional.empty(); + OldKeyword testee = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded); + + assertThat(testee.applyToState(new Flags(Flag.FLAGGED))).isEqualTo(new Flags()); + } + + @Test + public void applyStateShouldReturnUnreadFlagWhenUnreadSetOnSeenMessage() { + Optional<Boolean> isUnread = Optional.of(true); + Optional<Boolean> isFlagged = Optional.empty(); + Optional<Boolean> isAnswered = Optional.empty(); + Optional<Boolean> isDraft = Optional.empty(); + Optional<Boolean> isForwarded = Optional.empty(); + OldKeyword testee = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded); + + assertThat(testee.applyToState(new Flags(Flag.SEEN))).isEqualTo(new Flags()); + } + + @Test + public void applyStateShouldReturnSeenWhenPatchSetsSeenOnSeenMessage() { + Optional<Boolean> isUnread = Optional.of(false); + Optional<Boolean> isFlagged = Optional.empty(); + Optional<Boolean> isAnswered = Optional.empty(); + Optional<Boolean> isDraft = Optional.empty(); + Optional<Boolean> isForwarded = Optional.empty(); + OldKeyword testee = new OldKeyword(isUnread, isFlagged, isAnswered, isDraft, isForwarded); + + assertThat(testee.applyToState(new Flags(Flag.SEEN))).isEqualTo(new Flags(Flag.SEEN)); + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/james-project/blob/35a6e923/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java index c556ce6..5b8c4a1 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/UpdateMessagePatchTest.java @@ -21,9 +21,6 @@ package org.apache.james.jmap.model; import static org.assertj.core.api.Assertions.assertThat; -import java.util.Arrays; -import java.util.List; - import javax.mail.Flags; import org.apache.james.mailbox.FlagsBuilder; @@ -50,38 +47,6 @@ public class UpdateMessagePatchTest { } @Test - public void applyStateShouldSetFlaggedOnlyWhenIsFlagged() { - UpdateMessagePatch testee = UpdateMessagePatch.builder().isFlagged(true).build(); - List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(new Flags()).getSystemFlags()); - assertThat(updatedFlags).containsExactly(Flags.Flag.FLAGGED); - } - - - @Test - public void applyStateShouldRemoveFlaggedWhenEmptyIsFlaggedOnFlaggedMessage() { - UpdateMessagePatch testee = UpdateMessagePatch.builder().isAnswered(true).build(); - Flags isFlagged = new Flags(Flags.Flag.FLAGGED); - List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(isFlagged).getSystemFlags()); - assertThat(updatedFlags).doesNotContain(Flags.Flag.FLAGGED); - } - - @Test - public void applyStateShouldReturnUnreadFlagWhenUnreadSetOnSeenMessage() { - UpdateMessagePatch testee = UpdateMessagePatch.builder().isUnread(true).build(); - Flags isSeen = new Flags(Flags.Flag.SEEN); - List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(isSeen).getSystemFlags()); - assertThat(updatedFlags).doesNotContain(Flags.Flag.SEEN); - } - - @Test - public void applyStateShouldReturnSeenWhenPatchSetsSeenOnSeenMessage() { - UpdateMessagePatch testee = UpdateMessagePatch.builder().isUnread(false).build(); - Flags isSeen = new Flags(Flags.Flag.SEEN); - List<Flags.Flag> updatedFlags = Arrays.asList(testee.applyToState(isSeen).getSystemFlags()); - assertThat(updatedFlags).containsExactly(Flags.Flag.SEEN); - } - - @Test public void applyStateShouldReturnNewFlagsWhenKeywords() { ImmutableMap<String, Boolean> keywords = ImmutableMap.of( "$Answered", true, --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org