You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2019/03/08 01:13:46 UTC
[james-project] 02/14: JAMES-2662 Introduce converter DeletedMessage
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 6eb2e9fcc6d833ef1acf485ab2c45a6783e9612f
Author: datph <dp...@linagora.com>
AuthorDate: Fri Mar 1 16:24:37 2019 +0700
JAMES-2662 Introduce converter DeletedMessage
---
.../james/vault/DeletedMessageConverter.java | 139 ++++++++++++++++
.../james/vault/DeletedMessageConverterTest.java | 185 +++++++++++++++++++++
2 files changed, 324 insertions(+)
diff --git a/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageConverter.java b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageConverter.java
new file mode 100644
index 0000000..9c9516b
--- /dev/null
+++ b/mailbox/plugin/deleted-messages-vault/src/main/java/org/apache/james/vault/DeletedMessageConverter.java
@@ -0,0 +1,139 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.vault;
+
+import java.io.IOException;
+import java.time.Clock;
+import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.mail.internet.AddressException;
+
+import org.apache.james.core.MailAddress;
+import org.apache.james.core.MaybeSender;
+import org.apache.james.core.User;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mime4j.MimeIOException;
+import org.apache.james.mime4j.codec.DecodeMonitor;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.address.AddressList;
+import org.apache.james.mime4j.dom.address.Mailbox;
+import org.apache.james.mime4j.dom.address.MailboxList;
+import org.apache.james.mime4j.message.DefaultMessageBuilder;
+import org.apache.james.mime4j.stream.MimeConfig;
+import org.apache.james.util.StreamUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
+
+class DeletedMessageConverter {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DeletedMessageConverter.class);
+
+ private final Clock clock;
+
+ DeletedMessageConverter(Clock clock) {
+ this.clock = clock;
+ }
+
+ DeletedMessage convert(DeletedMessageMetadata deletedMessageMetadata, MailboxMessage mailboxMessage) throws IOException {
+ Preconditions.checkNotNull(deletedMessageMetadata);
+ Preconditions.checkNotNull(mailboxMessage);
+
+ Optional<Message> mimeMessage = parseMessage(mailboxMessage);
+
+ return DeletedMessage.builder()
+ .messageId(deletedMessageMetadata.getMessageId())
+ .originMailboxes(deletedMessageMetadata.getOwnerMailboxes())
+ .user(retrieveOwner(deletedMessageMetadata))
+ .deliveryDate(retrieveDeliveryDate(mimeMessage, mailboxMessage))
+ .deletionDate(ZonedDateTime.ofInstant(clock.instant(), ZoneOffset.UTC))
+ .sender(retrieveSender(mimeMessage))
+ .recipients(retrieveRecipients(mimeMessage))
+ .hasAttachment(mailboxMessage.getAttachments().iterator().hasNext())
+ .subject(mimeMessage.map(Message::getSubject))
+ .build();
+ }
+
+ private Optional<Message> parseMessage(MailboxMessage mailboxMessage) throws IOException {
+ DefaultMessageBuilder messageBuilder = new DefaultMessageBuilder();
+ messageBuilder.setMimeEntityConfig(MimeConfig.PERMISSIVE);
+ messageBuilder.setDecodeMonitor(DecodeMonitor.SILENT);
+ try {
+ return Optional.ofNullable(messageBuilder.parseMessage(mailboxMessage.getFullContent()));
+ } catch (MimeIOException e) {
+ LOGGER.warn("Can not parse the message {}", mailboxMessage.getUid(), e);
+ return Optional.empty();
+ }
+ }
+
+ private User retrieveOwner(DeletedMessageMetadata metadata) {
+ Preconditions.checkNotNull(metadata.getOwner(), "Deleted mail is missing owner");
+ return metadata.getOwner();
+ }
+
+ private ZonedDateTime retrieveDeliveryDate(Optional<Message> mimeMessage, MailboxMessage mailboxMessage) {
+ return mimeMessage.map(Message::getDate)
+ .map(Date::toInstant)
+ .map(instant -> ZonedDateTime.ofInstant(instant, ZoneOffset.UTC))
+ .orElse(ZonedDateTime.ofInstant(mailboxMessage.getInternalDate().toInstant(), ZoneOffset.UTC));
+ }
+
+ private MaybeSender retrieveSender(Optional<Message> mimeMessage) {
+ return mimeMessage
+ .map(Message::getSender)
+ .map(Mailbox::getAddress)
+ .map(MaybeSender::getMailSender)
+ .orElse(MaybeSender.nullSender());
+ }
+
+ private List<MailAddress> retrieveRecipients(Optional<Message> message) {
+ return StreamUtils.flatten(combineRecipients(message)
+ .filter(Objects::nonNull)
+ .map(AddressList::flatten)
+ .flatMap(MailboxList::stream)
+ .map(Mailbox::getAddress)
+ .map(this::retrieveAddress))
+ .collect(Guavate.toImmutableList());
+ }
+
+ private Stream<MailAddress> retrieveAddress(String address) {
+ try {
+ return Stream.of(new MailAddress(address));
+ } catch (AddressException e) {
+ LOGGER.warn("Can not create the mailAddress from {}", address, e);
+ return Stream.of();
+ }
+ }
+
+ private Stream<AddressList> combineRecipients(Optional<Message> message) {
+ return message.map(mimeMessage -> Stream.of(mimeMessage.getTo(),
+ mimeMessage.getCc(),
+ mimeMessage.getBcc()))
+ .orElse(Stream.of());
+ }
+}
diff --git a/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageConverterTest.java b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageConverterTest.java
new file mode 100644
index 0000000..74f7f88
--- /dev/null
+++ b/mailbox/plugin/deleted-messages-vault/src/test/java/org/apache/james/vault/DeletedMessageConverterTest.java
@@ -0,0 +1,185 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one *
+ * or more contributor license agreements. See the NOTICE file *
+ * distributed with this work for additional information *
+ * regarding copyright ownership. The ASF licenses this file *
+ * to you under the Apache License, Version 2.0 (the *
+ * "License"); you may not use this file except in compliance *
+ * with the License. You may obtain a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, *
+ * software distributed under the License is distributed on an *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
+ * KIND, either express or implied. See the License for the *
+ * specific language governing permissions and limitations *
+ * under the License. *
+ ****************************************************************/
+
+package org.apache.james.vault;
+
+import static org.apache.james.vault.DeletedMessageFixture.DELETED_MESSAGE;
+import static org.apache.james.vault.DeletedMessageFixture.DELETED_MESSAGE_WITH_SUBJECT;
+import static org.apache.james.vault.DeletedMessageFixture.DELETION_DATE;
+import static org.apache.james.vault.DeletedMessageFixture.DELIVERY_DATE;
+import static org.apache.james.vault.DeletedMessageFixture.MAILBOX_ID_1;
+import static org.apache.james.vault.DeletedMessageFixture.MAILBOX_ID_2;
+import static org.apache.james.vault.DeletedMessageFixture.MESSAGE_ID;
+import static org.apache.james.vault.DeletedMessageFixture.SUBJECT;
+import static org.apache.james.vault.DeletedMessageFixture.USER;
+import static org.apache.mailet.base.MailAddressFixture.RECIPIENT1;
+import static org.apache.mailet.base.MailAddressFixture.RECIPIENT2;
+import static org.apache.mailet.base.MailAddressFixture.SENDER;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import java.nio.charset.StandardCharsets;
+import java.time.Clock;
+import java.time.ZoneOffset;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.james.core.MaybeSender;
+import org.apache.james.core.User;
+import org.apache.james.mailbox.model.Attachment;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MessageAttachment;
+import org.apache.james.mailbox.store.MessageBuilder;
+import org.apache.james.mailbox.store.mail.model.MailboxMessage;
+import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableList;
+
+class DeletedMessageConverterTest {
+
+ private static final String FROM_FIELD = "From";
+ private static final String TO_FIELD = "To";
+ private static final String DATE_FIELD = "Date";
+ private static final String SUBJECT_FIELD = "Subject";
+ private static final String SENDER_FIELD = "Sender";
+ private static final String CC_FIELD = "cc";
+
+ private static final List<MailboxId> ORIGIN_MAILBOXES = ImmutableList.of(MAILBOX_ID_1, MAILBOX_ID_2);
+ private static final DeletedMessageMetadata DELETED_MESSAGE_METADATA = new DeletedMessageMetadata(
+ DeletedMessageFixture.MESSAGE_ID,
+ USER,
+ ORIGIN_MAILBOXES);
+
+ private static final User EMPTY_OWNER = null;
+
+ private static final Collection<MessageAttachment> NO_ATTACHMENT = ImmutableList.of();
+ private static final Collection<MessageAttachment> ATTACHMENTS = ImmutableList.of(MessageAttachment.builder()
+ .attachment(Attachment.builder()
+ .bytes("content".getBytes(StandardCharsets.UTF_8))
+ .type("type")
+ .build())
+ .build());
+
+ private DeletedMessageConverter deletedMessageConverter;
+
+ private MessageBuilder getMessageBuilder() {
+ MessageBuilder builder = new MessageBuilder();
+ builder.header(SENDER_FIELD, SENDER.asString());
+ builder.header(FROM_FIELD, "alice@james.com");
+ builder.header(TO_FIELD, RECIPIENT1.asString());
+ builder.header(CC_FIELD, RECIPIENT2.asString());
+ builder.header(SUBJECT_FIELD, SUBJECT);
+ builder.header(DATE_FIELD, "Thu, 30 Oct 2014 14:12:00 +0000 (GMT)");
+ return builder;
+ }
+
+ private MailboxMessage buildMessage(MessageBuilder messageBuilder, Collection<MessageAttachment> attachments) throws Exception {
+ MailboxMessage mailboxMessage = messageBuilder.build(MESSAGE_ID);
+ return SimpleMailboxMessage.fromWithoutAttachments(mailboxMessage)
+ .mailboxId(mailboxMessage.getMailboxId())
+ .internalDate(Date.from(DELIVERY_DATE.toInstant()))
+ .addAttachments(attachments).build();
+ }
+
+ @BeforeEach
+ void setUp() {
+ Clock clock = Clock.fixed(DELETION_DATE.toInstant(), ZoneOffset.UTC);
+ deletedMessageConverter = new DeletedMessageConverter(clock);
+ }
+
+ @Test
+ void convertShouldThrowWhenNoOwner() {
+ DeletedMessageMetadata deletedMessageMetadata = new DeletedMessageMetadata(MESSAGE_ID, EMPTY_OWNER, ORIGIN_MAILBOXES);
+ assertThatThrownBy(() -> deletedMessageConverter.convert(deletedMessageMetadata, buildMessage(getMessageBuilder(), ATTACHMENTS)))
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ void convertShouldReturnCorrespondingDeletedMessageWhenNoDeliveryDateInMimeMessageButInMailboxMessage() throws Exception {
+ MessageBuilder builder = getMessageBuilder();
+ builder.headers.remove(DATE_FIELD);
+
+ assertThat(deletedMessageConverter.convert(DELETED_MESSAGE_METADATA, buildMessage(builder, NO_ATTACHMENT)))
+ .isEqualTo(DELETED_MESSAGE_WITH_SUBJECT);
+ }
+
+ @Test
+ void convertShouldReturnCorrespondingDeletedMessageWhenNoSubject() throws Exception {
+ MessageBuilder builder = getMessageBuilder();
+ builder.headers.remove(SUBJECT_FIELD);
+
+ assertThat(deletedMessageConverter.convert(DELETED_MESSAGE_METADATA, buildMessage(builder, NO_ATTACHMENT)))
+ .isEqualTo(DELETED_MESSAGE);
+ }
+
+ @Test
+ void convertShouldReturnCorrespondingDeletedMessage() throws Exception {
+ MessageBuilder builder = getMessageBuilder();
+
+ assertThat(deletedMessageConverter.convert(DELETED_MESSAGE_METADATA, buildMessage(builder, NO_ATTACHMENT)))
+ .isEqualTo(DELETED_MESSAGE_WITH_SUBJECT);
+ }
+
+ @Test
+ void convertShouldReturnCorrespondingDeletedMessageWhenNoRecipient() throws Exception {
+ MessageBuilder builder = getMessageBuilder();
+ builder.headers.remove(TO_FIELD);
+ builder.headers.remove(CC_FIELD);
+
+ DeletedMessage deletedMessage = deletedMessageConverter.convert(DELETED_MESSAGE_METADATA, buildMessage(builder, NO_ATTACHMENT));
+
+ assertThat(deletedMessage.getRecipients())
+ .isEmpty();
+ }
+
+ @Test
+ void convertShouldReturnCorrespondingDeletedMessageWhenInvalidRecipient() throws Exception {
+ MessageBuilder builder = getMessageBuilder();
+ builder.header(TO_FIELD, "bad@bad@bad");
+ builder.header(CC_FIELD, "dad@");
+
+ DeletedMessage deletedMessage = deletedMessageConverter.convert(DELETED_MESSAGE_METADATA, buildMessage(builder, NO_ATTACHMENT));
+ assertThat(deletedMessage.getRecipients())
+ .isEmpty();
+ }
+
+ @Test
+ void convertShouldReturnCorrespondingDeletedMessageWhenInvalidHaveOneOfBadRecipient() throws Exception {
+ MessageBuilder builder = getMessageBuilder();
+ builder.header(TO_FIELD, "bad@bad@bad");
+
+ DeletedMessage deletedMessage = deletedMessageConverter.convert(DELETED_MESSAGE_METADATA, buildMessage(builder, NO_ATTACHMENT));
+ assertThat(deletedMessage.getRecipients())
+ .containsOnly(RECIPIENT2);
+ }
+
+ @Test
+ void convertShouldReturnCorrespondingDeletedMessageWhenNoSender() throws Exception {
+ MessageBuilder builder = getMessageBuilder();
+ builder.headers.remove(SENDER_FIELD);
+
+ DeletedMessage deletedMessage = deletedMessageConverter.convert(DELETED_MESSAGE_METADATA, buildMessage(builder, NO_ATTACHMENT));
+
+ assertThat(deletedMessage.getSender())
+ .isEqualTo(MaybeSender.nullSender());
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org