Skip to content

Commit

Permalink
JAMES-2110 SetMessage with creation should support keywords for flags
Browse files Browse the repository at this point in the history
  • Loading branch information
quynhn authored and Raphael Ouazana committed Aug 24, 2017
1 parent c1387ef commit 78a4a57
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 59 deletions.
Expand Up @@ -20,14 +20,12 @@
package org.apache.james.jmap.methods;

import static org.apache.james.jmap.methods.Method.JMAP_PREFIX;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import javax.inject.Inject;
import javax.mail.Flags;
import javax.mail.MessagingException;
Expand All @@ -40,6 +38,7 @@
import org.apache.james.jmap.model.BlobId;
import org.apache.james.jmap.model.CreationMessage;
import org.apache.james.jmap.model.CreationMessageId;
import org.apache.james.jmap.model.Keywords;
import org.apache.james.jmap.model.Message;
import org.apache.james.jmap.model.MessageFactory;
import org.apache.james.jmap.model.MessageFactory.MetaDataWithContent;
Expand Down Expand Up @@ -70,6 +69,7 @@
import org.apache.james.metrics.api.TimeMetric;
import org.apache.james.util.OptionalConverter;
import org.apache.mailet.Mail;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -82,6 +82,7 @@
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;


public class SetMessagesCreationProcessor implements SetMessagesProcessor {

private static final Logger LOG = LoggerFactory.getLogger(SetMailboxesCreationProcessor.class);
Expand Down Expand Up @@ -290,16 +291,20 @@ private MetaDataWithContent createMessageInOutbox(MessageWithId.CreationMessageE
byte[] messageContent = mimeMessageConverter.convert(createdEntry, messageAttachments);
SharedByteArrayInputStream content = new SharedByteArrayInputStream(messageContent);
Date internalDate = Date.from(createdEntry.getValue().getDate().toInstant());
Flags flags = getMessageFlags(createdEntry.getValue());

Flags flags = createdEntry.getValue()
.getKeywords()
.map(Keywords::asFlags)
.orElse(new Flags());

ComposedMessageId message = outbox.appendMessage(content, internalDate, session, flags.contains(Flags.Flag.RECENT), flags);

return MetaDataWithContent.builder()
.uid(message.getUid())
.flags(flags)
.size(messageContent.length)
.internalDate(internalDate.toInstant())
.sharedContent(content)
.size(messageContent.length)
.attachments(messageAttachments)
.mailboxId(outbox.getId())
.messageId(message.getMessageId())
Expand Down Expand Up @@ -332,23 +337,6 @@ private Optional<MessageAttachment> messageAttachment(MailboxSession session, At
}
}

private Flags getMessageFlags(CreationMessage message) {
Flags result = new Flags();
if (!message.isIsUnread()) {
result.add(Flags.Flag.SEEN);
}
if (message.isIsFlagged()) {
result.add(Flags.Flag.FLAGGED);
}
if (message.isIsAnswered() || message.getInReplyToMessageId().isPresent()) {
result.add(Flags.Flag.ANSWERED);
}
if (message.isIsDraft()) {
result.add(Flags.Flag.DRAFT);
}
return result;
}

private MessageWithId sendMessage(CreationMessageId creationId, MetaDataWithContent message, MailboxSession session) throws MailboxException, MessagingException {
Message jmapMessage = messageFactory.fromMetaDataWithContent(message);
sendMessage(message, jmapMessage, session);
Expand Down
Expand Up @@ -27,7 +27,6 @@
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;

Expand Down Expand Up @@ -59,10 +58,10 @@ public static Builder builder() {
public static class Builder {
private ImmutableList<String> mailboxIds;
private String inReplyToMessageId;
private boolean isUnread;
private boolean isFlagged;
private boolean isAnswered;
private boolean isDraft;
private Optional<Boolean> isUnread = Optional.empty();
private Optional<Boolean> isFlagged = Optional.empty();
private Optional<Boolean> isAnswered = Optional.empty();
private Optional<Boolean> isDraft = Optional.empty();
private final ImmutableMap.Builder<String, String> headers;
private Optional<DraftEmailer> from = Optional.empty();
private final ImmutableList.Builder<DraftEmailer> to;
Expand All @@ -75,6 +74,7 @@ public static class Builder {
private String htmlBody;
private final ImmutableList.Builder<Attachment> attachments;
private final ImmutableMap.Builder<BlobId, SubMessage> attachedMessages;
private Optional<Map<String, Boolean>> keywords = Optional.empty();

private Builder() {
to = ImmutableList.builder();
Expand All @@ -101,22 +101,22 @@ public Builder inReplyToMessageId(String inReplyToMessageId) {
return this;
}

public Builder isUnread(boolean isUnread) {
public Builder isUnread(Optional<Boolean> isUnread) {
this.isUnread = isUnread;
return this;
}

public Builder isFlagged(boolean isFlagged) {
public Builder isFlagged(Optional<Boolean> isFlagged) {
this.isFlagged = isFlagged;
return this;
}

public Builder isAnswered(boolean isAnswered) {
public Builder isAnswered(Optional<Boolean> isAnswered) {
this.isAnswered = isAnswered;
return this;
}

public Builder isDraft(boolean isDraft) {
public Builder isDraft(Optional<Boolean> isDraft) {
this.isDraft = isDraft;
return this;
}
Expand Down Expand Up @@ -186,6 +186,11 @@ public Builder attachedMessages(Map<BlobId, SubMessage> attachedMessages) {
return this;
}

public Builder keywords(Map<String, Boolean> keywords) {
this.keywords = Optional.of(ImmutableMap.copyOf(keywords));
return this;
}

private static boolean areAttachedMessagesKeysInAttachments(ImmutableList<Attachment> attachments, ImmutableMap<BlobId, SubMessage> attachedMessages) {
return attachedMessages.isEmpty() || attachedMessages.keySet().stream()
.anyMatch(inAttachments(attachments));
Expand All @@ -206,21 +211,29 @@ public CreationMessage build() {
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();
}

return new CreationMessage(mailboxIds, Optional.ofNullable(inReplyToMessageId), isUnread, isFlagged, isAnswered, isDraft, headers.build(), from,
to.build(), cc.build(), bcc.build(), replyTo.build(), subject, date, Optional.ofNullable(textBody), Optional.ofNullable(htmlBody), attachments, attachedMessages);
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);
}

private Optional<OldKeyword> getOldKeywords() {
if (isAnswered.isPresent() || isFlagged.isPresent() || isUnread.isPresent() || isDraft.isPresent()) {
return Optional.of(new OldKeyword(isUnread, isFlagged, isAnswered, isDraft));
}
return Optional.empty();
}
}

private final ImmutableList<String> mailboxIds;
private final Optional<String> inReplyToMessageId;
private final boolean isUnread;
private final boolean isFlagged;
private final boolean isAnswered;
private final boolean isDraft;
private final ImmutableMap<String, String> headers;
private final Optional<DraftEmailer> from;
private final ImmutableList<DraftEmailer> to;
Expand All @@ -233,17 +246,14 @@ public CreationMessage build() {
private final Optional<String> htmlBody;
private final ImmutableList<Attachment> attachments;
private final ImmutableMap<BlobId, SubMessage> attachedMessages;
private final Optional<Keywords> keywords;

@VisibleForTesting
CreationMessage(ImmutableList<String> mailboxIds, Optional<String> inReplyToMessageId, boolean isUnread, boolean isFlagged, boolean isAnswered, boolean isDraft, ImmutableMap<String, String> headers, Optional<DraftEmailer> from,
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) {
ImmutableMap<BlobId, SubMessage> attachedMessages, Optional<Keywords> keywords) {
this.mailboxIds = mailboxIds;
this.inReplyToMessageId = inReplyToMessageId;
this.isUnread = isUnread;
this.isFlagged = isFlagged;
this.isAnswered = isAnswered;
this.isDraft = isDraft;
this.headers = headers;
this.from = from;
this.to = to;
Expand All @@ -256,6 +266,7 @@ public CreationMessage build() {
this.htmlBody = htmlBody;
this.attachments = attachments;
this.attachedMessages = attachedMessages;
this.keywords = keywords;
}

public ImmutableList<String> getMailboxIds() {
Expand All @@ -266,22 +277,6 @@ public Optional<String> getInReplyToMessageId() {
return inReplyToMessageId;
}

public boolean isIsUnread() {
return isUnread;
}

public boolean isIsFlagged() {
return isFlagged;
}

public boolean isIsAnswered() {
return isAnswered;
}

public boolean isIsDraft() {
return isDraft;
}

public ImmutableMap<String, String> getHeaders() {
return headers;
}
Expand Down Expand Up @@ -334,6 +329,10 @@ public boolean isValid() {
return validate().isEmpty();
}

public Optional<Keywords> getKeywords() {
return keywords;
}

public List<ValidationResult> validate() {
ImmutableList.Builder<ValidationResult> errors = ImmutableList.builder();
assertValidFromProvided(errors);
Expand Down
Expand Up @@ -42,6 +42,7 @@
import org.apache.james.jmap.model.CreationMessage;
import org.apache.james.jmap.model.CreationMessage.DraftEmailer;
import org.apache.james.jmap.model.CreationMessageId;
import org.apache.james.jmap.model.Keyword;
import org.apache.james.jmap.model.MessageFactory;
import org.apache.james.jmap.model.MessagePreviewGenerator;
import org.apache.james.jmap.model.MessageProperties.MessageProperty;
Expand Down Expand Up @@ -72,9 +73,12 @@
import org.apache.mailet.Mail;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

public class SetMessagesCreationProcessorTest {
Expand Down Expand Up @@ -115,7 +119,8 @@ public class SetMessagesCreationProcessorTest {
private Optional<MessageManager> optionalOutbox;
private Optional<MessageManager> optionalDrafts;


@Rule
public ExpectedException expectedException = ExpectedException.none();
@Before
public void setUp() throws MailboxException {
HtmlTextExtractor htmlTextExtractor = mock(HtmlTextExtractor.class);
Expand Down Expand Up @@ -156,6 +161,56 @@ public void processShouldReturnEmptyCreatedWhenRequestHasEmptyCreate() {
assertThat(result.getNotCreated()).isEmpty();
}

@Test
public void processShouldThrowWhenBothIsFlagAndKeywords() {
expectedException.expect(IllegalArgumentException.class);
SetMessagesRequest createMessageWithError = SetMessagesRequest.builder()
.create(
creationMessageId,
creationMessageBuilder
.mailboxId(OUTBOX_ID.serialize())
.isAnswered(Optional.of(true))
.keywords(ImmutableMap.of("$Answered", true))
.build())
.build();

sut.process(createMessageWithError, session);
}

@Test
public void processShouldCreateWhenKeywords() {
SetMessagesRequest createMessageWithKeywords = SetMessagesRequest.builder()
.create(
creationMessageId,
creationMessageBuilder
.mailboxId(OUTBOX_ID.serialize())
.keywords(ImmutableMap.of("$Answered", true))
.build())
.build();

SetMessagesResponse result = sut.process(createMessageWithKeywords, session);

assertThat(result.getCreated()).isNotEmpty();
assertThat(result.getNotCreated()).isEmpty();
}

@Test
public void processShouldCreateWhenIsFlag() {
SetMessagesRequest createMessageWithKeywords = SetMessagesRequest.builder()
.create(
creationMessageId,
creationMessageBuilder
.mailboxId(OUTBOX_ID.serialize())
.isAnswered(Optional.of(true))
.build())
.build();

SetMessagesResponse result = sut.process(createMessageWithKeywords, session);

assertThat(result.getCreated()).isNotEmpty();
assertThat(result.getNotCreated()).isEmpty();
}

@Test
public void processShouldReturnNonEmptyCreatedWhenRequestHasNonEmptyCreate() throws MailboxException {
// Given
Expand Down

0 comments on commit 78a4a57

Please sign in to comment.