JAMES-2110 GetMessage should support keywords
Project: http://git-wip-us.apache.org/repos/asf/james-project/repo Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/c1387efa Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/c1387efa Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/c1387efa Branch: refs/heads/master Commit: c1387efaea253d6f29577d31f8938f1ec6008841 Parents: 53fdcbd Author: quynhn <qngu...@linagora.com> Authored: Tue Aug 15 16:40:11 2017 +0700 Committer: Raphael Ouazana <raphael.ouaz...@linagora.com> Committed: Thu Aug 24 15:47:27 2017 +0200 ---------------------------------------------------------------------- .../org/apache/james/jmap/model/Message.java | 65 +++++--------- .../apache/james/jmap/model/MessageFactory.java | 6 +- .../james/jmap/model/MessageProperties.java | 1 + .../james/jmap/model/MessageFactoryTest.java | 94 ++++++++++++++++++-- .../james/jmap/model/MessagePropertiesTest.java | 10 ++- .../apache/james/jmap/model/MessageTest.java | 65 +++++++------- 6 files changed, 156 insertions(+), 85 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/james-project/blob/c1387efa/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java index 969525d..86762fe 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/Message.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.function.Predicate; +import javax.mail.Flags; import org.apache.james.jmap.methods.GetMessagesMethod; import org.apache.james.jmap.methods.JmapResponseWriterImpl; @@ -57,10 +58,6 @@ public class Message { private String threadId; private ImmutableList<MailboxId> mailboxIds; private String inReplyToMessageId; - private boolean isUnread; - private boolean isFlagged; - private boolean isAnswered; - private boolean isDraft; private ImmutableMap<String, String> headers; private Emailer from; private final ImmutableList.Builder<Emailer> to; @@ -75,6 +72,7 @@ public class Message { private Optional<String> htmlBody = Optional.empty(); private final ImmutableList.Builder<Attachment> attachments; private final ImmutableMap.Builder<BlobId, SubMessage> attachedMessages; + private Optional<Flags> flags = Optional.empty(); private Builder() { to = ImmutableList.builder(); @@ -120,23 +118,8 @@ public class Message { return this; } - public Builder isUnread(boolean isUnread) { - this.isUnread = isUnread; - return this; - } - - public Builder isFlagged(boolean isFlagged) { - this.isFlagged = isFlagged; - return this; - } - - public Builder isAnswered(boolean isAnswered) { - this.isAnswered = isAnswered; - return this; - } - - public Builder isDraft(boolean isDraft) { - this.isDraft = isDraft; + public Builder flags(Flags flags) { + this.flags = Optional.ofNullable(flags); return this; } @@ -223,9 +206,15 @@ public class Message { ImmutableMap<BlobId, SubMessage> attachedMessages = this.attachedMessages.build(); Preconditions.checkState(areAttachedMessagesKeysInAttachments(attachments, attachedMessages), "'attachedMessages' keys must be in 'attachements'"); boolean hasAttachment = hasAttachment(attachments); - - return new Message(id, blobId, threadId, mailboxIds, Optional.ofNullable(inReplyToMessageId), isUnread, isFlagged, isAnswered, isDraft, hasAttachment, headers, Optional.ofNullable(from), - to.build(), cc.build(), bcc.build(), replyTo.build(), subject, date, size, preview, textBody, htmlBody, attachments, attachedMessages); + Keywords keywords = flags.map(flag -> Keywords.factory() + .filterImapNonExposedKeywords() + .fromFlags(flag)) + .orElse(Keywords.DEFAULT_VALUE); + + return new Message(id, blobId, threadId, mailboxIds, Optional.ofNullable(inReplyToMessageId), + hasAttachment, headers, Optional.ofNullable(from), + to.build(), cc.build(), bcc.build(), replyTo.build(), subject, date, size, preview, textBody, htmlBody, attachments, attachedMessages, + keywords); } } @@ -250,10 +239,6 @@ public class Message { private final String threadId; private final ImmutableList<MailboxId> mailboxIds; private final Optional<String> inReplyToMessageId; - private final boolean isUnread; - private final boolean isFlagged; - private final boolean isAnswered; - private final boolean isDraft; private final boolean hasAttachment; @JsonFilter(GetMessagesMethod.HEADERS_FILTER) private final ImmutableMap<String, String> headers; @@ -270,16 +255,13 @@ public class Message { private final Optional<String> htmlBody; private final ImmutableList<Attachment> attachments; private final ImmutableMap<BlobId, SubMessage> attachedMessages; + private final Keywords keywords; @VisibleForTesting Message(MessageId id, BlobId blobId, String threadId, ImmutableList<MailboxId> mailboxIds, Optional<String> inReplyToMessageId, - boolean isUnread, - boolean isFlagged, - boolean isAnswered, - boolean isDraft, boolean hasAttachment, ImmutableMap<String, String> headers, Optional<Emailer> from, @@ -294,16 +276,13 @@ public class Message { Optional<String> textBody, Optional<String> htmlBody, ImmutableList<Attachment> attachments, - ImmutableMap<BlobId, SubMessage> attachedMessages) { + ImmutableMap<BlobId, SubMessage> attachedMessages, + Keywords keywords) { this.id = id; this.blobId = blobId; this.threadId = threadId; this.mailboxIds = mailboxIds; this.inReplyToMessageId = inReplyToMessageId; - this.isUnread = isUnread; - this.isFlagged = isFlagged; - this.isAnswered = isAnswered; - this.isDraft = isDraft; this.hasAttachment = hasAttachment; this.headers = headers; this.from = from; @@ -319,6 +298,7 @@ public class Message { this.htmlBody = htmlBody; this.attachments = attachments; this.attachedMessages = attachedMessages; + this.keywords = keywords; } public MessageId getId() { @@ -342,19 +322,19 @@ public class Message { } public boolean isIsUnread() { - return isUnread; + return !keywords.contains(Keyword.SEEN); } public boolean isIsFlagged() { - return isFlagged; + return keywords.contains(Keyword.FLAGGED); } public boolean isIsAnswered() { - return isAnswered; + return keywords.contains(Keyword.ANSWERED); } public boolean isIsDraft() { - return isDraft; + return keywords.contains(Keyword.DRAFT); } public boolean isHasAttachment() { @@ -417,4 +397,7 @@ public class Message { return attachedMessages; } + public ImmutableMap<String, Boolean> getKeywords() { + return keywords.asMap(); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/c1387efa/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java index b5e5e1d..a84085b 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageFactory.java @@ -30,7 +30,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; - import javax.inject.Inject; import javax.mail.Flags; import javax.mail.internet.SharedInputStream; @@ -94,10 +93,7 @@ public class MessageFactory { .threadId(message.getMessageId().serialize()) .mailboxIds(message.getMailboxIds()) .inReplyToMessageId(getHeader(mimeMessage, "in-reply-to")) - .isUnread(! message.getFlags().contains(Flags.Flag.SEEN)) - .isFlagged(message.getFlags().contains(Flags.Flag.FLAGGED)) - .isAnswered(message.getFlags().contains(Flags.Flag.ANSWERED)) - .isDraft(message.getFlags().contains(Flags.Flag.DRAFT)) + .flags(message.getFlags()) .subject(Strings.nullToEmpty(mimeMessage.getSubject()).trim()) .headers(toMap(mimeMessage.getHeader().getFields())) .from(firstFromMailboxList(mimeMessage.getFrom())) http://git-wip-us.apache.org/repos/asf/james-project/blob/c1387efa/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageProperties.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageProperties.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageProperties.java index d8b23db..bf1bff5 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageProperties.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/model/MessageProperties.java @@ -143,6 +143,7 @@ public class MessageProperties { htmlBody("htmlBody"), attachments("attachments"), attachedMessages("attachedMessages"), + keywords("keywords"), body("body", PropertyType.INPUTONLY); private final String property; http://git-wip-us.apache.org/repos/asf/james-project/blob/c1387efa/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java index 4e0141d..76a5e14 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageFactoryTest.java @@ -19,19 +19,16 @@ package org.apache.james.jmap.model; import static org.assertj.core.api.Assertions.assertThat; - import java.io.ByteArrayInputStream; import java.time.Instant; import java.util.Optional; - import javax.mail.Flags; import javax.mail.Flags.Flag; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; import org.apache.james.jmap.model.MessageFactory.MetaDataWithContent; import org.apache.james.jmap.utils.HtmlTextExtractor; import org.apache.james.jmap.utils.JsoupHtmlTextExtractor; +import org.apache.james.mailbox.FlagsBuilder; import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.inmemory.InMemoryId; import org.apache.james.mailbox.model.AttachmentId; @@ -39,14 +36,18 @@ import org.apache.james.mailbox.model.Cid; import org.apache.james.mailbox.model.MessageAttachment; import org.apache.james.mailbox.model.TestMessageId; import org.apache.james.util.mime.MessageContentExtractor; -import org.junit.Before; -import org.junit.Test; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import com.google.common.base.Charsets; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import org.junit.Before; +import org.junit.Test; + public class MessageFactoryTest { + private static final String FORWARDED = "forwarded"; private static final InMemoryId MAILBOX_ID = InMemoryId.of(18L); private static final Instant INTERNAL_DATE = Instant.parse("2012-02-03T14:30:42Z"); @@ -107,6 +108,7 @@ public class MessageFactoryTest { @Test public void headersShouldBeSetIntoMessage() throws Exception { + Flags flags = new Flags(Flag.SEEN); String headers = "From: user <user@domain>\n" + "Subject: test subject\n" + "To: user1 <user1@domain>, user2 <user2@domain>\n" @@ -118,7 +120,7 @@ public class MessageFactoryTest { + "Other-header: other header value"; MetaDataWithContent testMail = MetaDataWithContent.builder() .uid(MessageUid.of(2)) - .flags(new Flags(Flag.SEEN)) + .flags(flags) .size(headers.length()) .internalDate(INTERNAL_DATE) .content(new ByteArrayInputStream(headers.getBytes(Charsets.UTF_8))) @@ -145,6 +147,7 @@ public class MessageFactoryTest { .put("Date", "Tue, 14 Jul 2015 12:30:42 +0000") .put("MIME-Version", "1.0") .build(); + Message testee = messageFactory.fromMetaDataWithContent(testMail); Message expected = Message.builder() .id(TestMessageId.of(2)) @@ -164,12 +167,14 @@ public class MessageFactoryTest { .preview("(Empty)") .textBody(Optional.of("")) .htmlBody(Optional.empty()) + .flags(flags) .build(); assertThat(testee).isEqualToComparingFieldByField(expected); } @Test public void headersShouldBeUnfoldedAndDecoded() throws Exception { + Flags flags = new Flags(Flag.SEEN); String headers = "From: user <user@domain>\n" + "Subject: test subject\n" + "To: user1 <user1@domain>,\r\n" @@ -177,7 +182,7 @@ public class MessageFactoryTest { + "Cc: =?UTF-8?Q?Beno=c3=aet_TELLIER?= <tell...@linagora.com>"; MetaDataWithContent testMail = MetaDataWithContent.builder() .uid(MessageUid.of(2)) - .flags(new Flags(Flag.SEEN)) + .flags(flags) .size(headers.length()) .internalDate(INTERNAL_DATE) .content(new ByteArrayInputStream(headers.getBytes(Charsets.UTF_8))) @@ -213,7 +218,9 @@ public class MessageFactoryTest { .preview("(Empty)") .textBody(Optional.of("")) .htmlBody(Optional.empty()) + .flags(flags) .build(); + assertThat(testee).isEqualToComparingFieldByField(expected); } @@ -674,4 +681,75 @@ public class MessageFactoryTest { .extracting(Message::getPreview, Message::getHtmlBody, Message::getTextBody) .containsExactly("My plain message", Optional.of("<html></html>"), Optional.of("My plain message")); } + + @Test + public void keywordShouldBeSetIntoMessage() throws Exception { + Flags flags = FlagsBuilder.builder() + .add(Flag.ANSWERED, Flag.DRAFT) + .build(); + ImmutableMap<String, Boolean> keywords = Keywords.factory() + .fromFlags(flags) + .asMap(); + + MetaDataWithContent testMail = MetaDataWithContent.builder() + .uid(MessageUid.of(2)) + .flags(flags) + .size(0) + .internalDate(INTERNAL_DATE) + .content(new ByteArrayInputStream("".getBytes(Charsets.UTF_8))) + .attachments(ImmutableList.of()) + .mailboxId(MAILBOX_ID) + .messageId(TestMessageId.of(2)) + .build(); + Message testee = messageFactory.fromMetaDataWithContent(testMail); + assertThat(testee.getKeywords()).containsAllEntriesOf(keywords); + } + + @Test + public void keywordWithUserFlagShouldBeSetIntoMessage() throws Exception { + Flags flags = FlagsBuilder.builder() + .add(Flag.ANSWERED) + .add(FORWARDED) + .build(); + ImmutableMap<String, Boolean> keywords = Keywords.factory() + .fromFlags(flags) + .asMap(); + MetaDataWithContent testMail = MetaDataWithContent.builder() + .uid(MessageUid.of(2)) + .flags(flags) + .size(0) + .internalDate(INTERNAL_DATE) + .content(new ByteArrayInputStream("".getBytes(Charsets.UTF_8))) + .attachments(ImmutableList.of()) + .mailboxId(MAILBOX_ID) + .messageId(TestMessageId.of(2)) + .build(); + Message testee = messageFactory.fromMetaDataWithContent(testMail); + assertThat(testee.getKeywords()).containsAllEntriesOf(keywords); + } + + @Test + public void fromMetaDataWithContentShouldRemoveUnsupportedKeyword() throws Exception { + Flags flags = FlagsBuilder.builder() + .add(Flag.ANSWERED, Flag.DELETED, Flag.RECENT) + .add(FORWARDED) + .build(); + ImmutableMap<String, Boolean> keywords = Keywords.factory() + .filterImapNonExposedKeywords() + .fromFlags(flags) + .asMap(); + + MetaDataWithContent testMail = MetaDataWithContent.builder() + .uid(MessageUid.of(2)) + .flags(flags) + .size(0) + .internalDate(INTERNAL_DATE) + .content(new ByteArrayInputStream("".getBytes(Charsets.UTF_8))) + .attachments(ImmutableList.of()) + .mailboxId(MAILBOX_ID) + .messageId(TestMessageId.of(2)) + .build(); + Message testee = messageFactory.fromMetaDataWithContent(testMail); + assertThat(testee.getKeywords()).containsAllEntriesOf(keywords); + } } http://git-wip-us.apache.org/repos/asf/james-project/blob/c1387efa/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePropertiesTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePropertiesTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePropertiesTest.java index 89c5e41..3e38e60 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePropertiesTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessagePropertiesTest.java @@ -50,7 +50,15 @@ public class MessagePropertiesTest { .hasValueSatisfying(value -> assertThat(value).contains(MessageProperty.textBody).doesNotContain(MessageProperty.body)); } - + + @Test + public void toOutputPropertiesShouldReturnIsUnread() { + MessageProperties actual = new MessageProperties(Optional.of(ImmutableSet.of("isUnread"))).toOutputProperties(); + assertThat(actual.getOptionalMessageProperties()) + .hasValueSatisfying(value -> + assertThat(value).contains(MessageProperty.isUnread)); + } + @Test public void toOutputPropertiesShouldReturnMandatoryPropertiesWhenEmptyRequest() { MessageProperties actual = new MessageProperties(Optional.of(ImmutableSet.of())).toOutputProperties(); http://git-wip-us.apache.org/repos/asf/james-project/blob/c1387efa/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageTest.java ---------------------------------------------------------------------- diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageTest.java index 494ae77..d1c0914 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/model/MessageTest.java @@ -23,6 +23,9 @@ import static org.assertj.core.api.Assertions.assertThat; import java.time.Instant; import java.util.Optional; +import javax.mail.Flags; + +import org.apache.james.mailbox.FlagsBuilder; import org.apache.james.mailbox.inmemory.InMemoryId; import org.apache.james.mailbox.model.TestMessageId; import org.junit.Test; @@ -101,8 +104,9 @@ public class MessageTest { @Test public void buildShouldWorkWhenMandatoryFieldsArePresent() { Instant currentDate = Instant.now(); - Message expected = new Message(TestMessageId.of(1), BlobId.of("blobId"), "threadId", ImmutableList.of(InMemoryId.of(456)), Optional.empty(), false, false, false, false, false, ImmutableMap.of("key", "value"), Optional.empty(), - ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), "subject", currentDate, 123, "preview", Optional.empty(), Optional.empty(), ImmutableList.of(), ImmutableMap.of()); + Message expected = new Message(TestMessageId.of(1), BlobId.of("blobId"), "threadId", ImmutableList.of(InMemoryId.of(456)), Optional.empty(), false, ImmutableMap.of("key", "value"), Optional.empty(), + ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), ImmutableList.of(), "subject", currentDate, 123, "preview", Optional.empty(), Optional.empty(), + ImmutableList.of(), ImmutableMap.of(), Keywords.DEFAULT_VALUE); Message tested = Message.builder() .id(TestMessageId.of(1)) .blobId(BlobId.of("blobId")) @@ -158,41 +162,42 @@ public class MessageTest { .date(currentDate) .build(); ImmutableMap<BlobId, SubMessage> attachedMessages = ImmutableMap.of(BlobId.of("blobId"), simpleMessage); + Flags flags = FlagsBuilder.builder() + .add(Flags.Flag.DRAFT, Flags.Flag.ANSWERED, Flags.Flag.FLAGGED) + .build(); + + Keywords keywords = Keywords.factory() + .fromFlags(flags); + Message expected = new Message( - TestMessageId.of(1), - BlobId.of("blobId"), - "threadId", - ImmutableList.of(InMemoryId.of(456)), - Optional.of("inReplyToMessageId"), - true, - true, - true, - true, - true, - ImmutableMap.of("key", "value"), - Optional.of(from), - to, - cc, - bcc, - replyTo, - "subject", - currentDate, - 123, - "preview", - Optional.of("textBody"), - Optional.of("htmlBody"), - attachments, - attachedMessages); + TestMessageId.of(1), + BlobId.of("blobId"), + "threadId", + ImmutableList.of(InMemoryId.of(456)), + Optional.of("inReplyToMessageId"), + true, + ImmutableMap.of("key", "value"), + Optional.of(from), + to, + cc, + bcc, + replyTo, + "subject", + currentDate, + 123, + "preview", + Optional.of("textBody"), + Optional.of("htmlBody"), + attachments, + attachedMessages, + keywords); Message tested = Message.builder() .id(TestMessageId.of(1)) .blobId(BlobId.of("blobId")) .threadId("threadId") .mailboxId(InMemoryId.of(456)) .inReplyToMessageId("inReplyToMessageId") - .isUnread(true) - .isFlagged(true) - .isAnswered(true) - .isDraft(true) + .flags(flags) .headers(ImmutableMap.of("key", "value")) .from(from) .to(to) --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org