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 rc...@apache.org on 2019/11/25 09:06:51 UTC

[james-project] 08/22: JAMES-2987 Add MessageMetadataViewFactory

This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 4cfab0f2b3db546c30911f749fcfedc4989dd269
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Thu Nov 21 12:00:07 2019 +0700

    JAMES-2987 Add MessageMetadataViewFactory
    
    MessageViewFactory becomes the generic class converting MessageResults
    into MessageView.
    
    By avoiding the intermediate MessageContentWithMetadata, we can really
    simplify new MessageView factories.
---
 .../manager/InMemoryIntegrationResources.java      |  12 +++
 .../model/message/view/MessageFullViewFactory.java |  29 +-----
 .../message/view/MessageMetadataViewFactory.java   |  57 +++++++++++
 .../model/message/view/MessageViewFactory.java     |  67 +++++++++++++
 .../view/MessageMetadataViewFactoryTest.java       | 110 +++++++++++++++++++++
 5 files changed, 251 insertions(+), 24 deletions(-)

diff --git a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java
index 29c4ae3..59e1a88 100644
--- a/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java
+++ b/mailbox/memory/src/test/java/org/apache/james/mailbox/inmemory/manager/InMemoryIntegrationResources.java
@@ -50,6 +50,8 @@ import org.apache.james.mailbox.store.FakeAuthorizator;
 import org.apache.james.mailbox.store.JVMMailboxPathLocker;
 import org.apache.james.mailbox.store.PreDeletionHooks;
 import org.apache.james.mailbox.store.SessionProvider;
+import org.apache.james.mailbox.store.StoreAttachmentManager;
+import org.apache.james.mailbox.store.StoreBlobManager;
 import org.apache.james.mailbox.store.StoreMailboxAnnotationManager;
 import org.apache.james.mailbox.store.StoreMailboxManager;
 import org.apache.james.mailbox.store.StoreMessageIdManager;
@@ -384,6 +386,7 @@ public class InMemoryIntegrationResources implements IntegrationResources<StoreM
     private final StoreMessageIdManager storeMessageIdManager;
     private final MessageSearchIndex searchIndex;
     private final EventBus eventBus;
+    private final StoreBlobManager blobManager;
 
     InMemoryIntegrationResources(InMemoryMailboxManager mailboxManager, StoreRightManager storeRightManager, MessageId.Factory messageIdFactory, InMemoryCurrentQuotaManager currentQuotaManager, DefaultUserQuotaRootResolver defaultUserQuotaRootResolver, InMemoryPerUserMaxQuotaManager maxQuotaManager, QuotaManager quotaManager, MessageSearchIndex searchIndex, EventBus eventBus) {
         this.mailboxManager = mailboxManager;
@@ -404,6 +407,11 @@ public class InMemoryIntegrationResources implements IntegrationResources<StoreM
             quotaManager,
             defaultUserQuotaRootResolver,
             mailboxManager.getPreDeletionHooks());
+
+        this.blobManager = new StoreBlobManager(
+            new StoreAttachmentManager((InMemoryMailboxSessionMapperFactory) mailboxManager.getMapperFactory(), storeMessageIdManager),
+            storeMessageIdManager,
+            messageIdFactory);
     }
 
     public DefaultUserQuotaRootResolver getDefaultUserQuotaRootResolver() {
@@ -450,4 +458,8 @@ public class InMemoryIntegrationResources implements IntegrationResources<StoreM
     public EventBus getEventBus() {
         return eventBus;
     }
+
+    public StoreBlobManager getBlobManager() {
+        return blobManager;
+    }
 }
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFullViewFactory.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFullViewFactory.java
index 2f7d15b..ebef606 100644
--- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFullViewFactory.java
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageFullViewFactory.java
@@ -40,7 +40,6 @@ import org.apache.james.jmap.draft.model.Emailer;
 import org.apache.james.jmap.draft.model.Keywords;
 import org.apache.james.jmap.draft.model.MessagePreviewGenerator;
 import org.apache.james.jmap.draft.utils.HtmlTextExtractor;
-import org.apache.james.jmap.draft.utils.KeywordsCombiner;
 import org.apache.james.mailbox.BlobManager;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.exception.MailboxException;
@@ -66,11 +65,9 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Multimaps;
 import com.google.common.collect.Sets;
 
-public class MessageFullViewFactory {
+public class MessageFullViewFactory implements MessageViewFactory<MessageFullView> {
     public static final String JMAP_MULTIVALUED_FIELD_DELIMITER = "\n";
 
-    private static final KeywordsCombiner ACCUMULATOR = new KeywordsCombiner();
-
     private final BlobManager blobManager;
     private final MessagePreviewGenerator messagePreview;
     private final MessageContentExtractor messageContentExtractor;
@@ -87,6 +84,7 @@ public class MessageFullViewFactory {
         this.keywordsFactory = Keywords.lenientFactory();
     }
 
+    @Override
     public MessageFullView fromMessageResults(Collection<MessageResult> messageResults) throws MailboxException {
         return fromMetaDataWithContent(toMetaDataWithContent(messageResults));
     }
@@ -122,20 +120,11 @@ public class MessageFullViewFactory {
     }
 
     private MetaDataWithContent toMetaDataWithContent(Collection<MessageResult> messageResults) throws MailboxException {
-        Preconditions.checkArgument(!messageResults.isEmpty(), "MessageResults cannot be empty");
-        Preconditions.checkArgument(hasOnlyOneMessageId(messageResults), "MessageResults need to share the same messageId");
+        assertOneMessageId(messageResults);
 
         MessageResult firstMessageResult = messageResults.iterator().next();
-        List<MailboxId> mailboxIds = messageResults.stream()
-            .map(MessageResult::getMailboxId)
-            .distinct()
-            .collect(Guavate.toImmutableList());
-
-        Keywords keywords = messageResults.stream()
-            .map(MessageResult::getFlags)
-            .map(keywordsFactory::fromFlags)
-            .reduce(ACCUMULATOR)
-            .get();
+        List<MailboxId> mailboxIds = getMailboxIds(messageResults);
+        Keywords keywords = getKeywords(messageResults);
 
         return MetaDataWithContent.builderFromMessageResult(firstMessageResult)
             .messageId(firstMessageResult.getMessageId())
@@ -144,14 +133,6 @@ public class MessageFullViewFactory {
             .build();
     }
 
-    private boolean hasOnlyOneMessageId(Collection<MessageResult> messageResults) {
-        return messageResults
-            .stream()
-            .map(MessageResult::getMessageId)
-            .distinct()
-            .count() == 1;
-    }
-
     private Instant getDateFromHeaderOrInternalDateOtherwise(org.apache.james.mime4j.dom.Message mimeMessage, MetaDataWithContent message) {
         return Optional.ofNullable(mimeMessage.getDate())
             .map(Date::toInstant)
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageMetadataViewFactory.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageMetadataViewFactory.java
new file mode 100644
index 0000000..95a382f
--- /dev/null
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageMetadataViewFactory.java
@@ -0,0 +1,57 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.draft.model.message.view;
+
+import java.util.Collection;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.james.jmap.draft.model.BlobId;
+import org.apache.james.mailbox.BlobManager;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MessageResult;
+
+public class MessageMetadataViewFactory implements MessageViewFactory<MessageMetadataView> {
+    private final BlobManager blobManager;
+
+    @Inject
+    MessageMetadataViewFactory(BlobManager blobManager) {
+        this.blobManager = blobManager;
+    }
+
+    @Override
+    public MessageMetadataView fromMessageResults(Collection<MessageResult> messageResults) throws MailboxException {
+        assertOneMessageId(messageResults);
+
+        MessageResult firstMessageResult = messageResults.iterator().next();
+        List<MailboxId> mailboxIds = getMailboxIds(messageResults);
+
+        return MessageMetadataView.messageMetadataBuilder()
+            .id(firstMessageResult.getMessageId())
+            .mailboxIds(mailboxIds)
+            .blobId(BlobId.of(blobManager.toBlobId(firstMessageResult.getMessageId())))
+            .threadId(firstMessageResult.getMessageId().serialize())
+            .keywords(getKeywords(messageResults))
+            .size(firstMessageResult.getSize())
+            .build();
+    }
+}
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageViewFactory.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageViewFactory.java
new file mode 100644
index 0000000..5339c13
--- /dev/null
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/draft/model/message/view/MessageViewFactory.java
@@ -0,0 +1,67 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.draft.model.message.view;
+
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.james.jmap.draft.model.Keywords;
+import org.apache.james.jmap.draft.utils.KeywordsCombiner;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MessageResult;
+
+import com.github.steveash.guavate.Guavate;
+import com.google.common.base.Preconditions;
+
+public interface MessageViewFactory<T extends MessageView> {
+    KeywordsCombiner KEYWORDS_COMBINER = new KeywordsCombiner();
+    Keywords.KeywordsFactory KEYWORDS_FACTORY = Keywords.lenientFactory();
+
+    T fromMessageResults(Collection<MessageResult> messageResults) throws MailboxException;
+
+    default void assertOneMessageId(Collection<MessageResult> messageResults) {
+        Preconditions.checkArgument(!messageResults.isEmpty(), "MessageResults cannot be empty");
+        Preconditions.checkArgument(hasOnlyOneMessageId(messageResults), "MessageResults need to share the same messageId");
+    }
+
+    default boolean hasOnlyOneMessageId(Collection<MessageResult> messageResults) {
+        return messageResults
+            .stream()
+            .map(MessageResult::getMessageId)
+            .distinct()
+            .count() == 1;
+    }
+
+    default List<MailboxId> getMailboxIds(Collection<MessageResult> messageResults) {
+        return messageResults.stream()
+                .map(MessageResult::getMailboxId)
+                .distinct()
+                .collect(Guavate.toImmutableList());
+    }
+
+    default Keywords getKeywords(Collection<MessageResult> messageResults) {
+        return messageResults.stream()
+                .map(MessageResult::getFlags)
+                .map(KEYWORDS_FACTORY::fromFlags)
+                .reduce(KEYWORDS_COMBINER)
+                .get();
+    }
+}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/message/view/MessageMetadataViewFactoryTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/message/view/MessageMetadataViewFactoryTest.java
new file mode 100644
index 0000000..36cac92
--- /dev/null
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/draft/model/message/view/MessageMetadataViewFactoryTest.java
@@ -0,0 +1,110 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.jmap.draft.model.message.view;
+
+import java.util.List;
+
+import javax.mail.Flags;
+
+import org.apache.james.core.Username;
+import org.apache.james.jmap.draft.model.BlobId;
+import org.apache.james.jmap.draft.model.Keyword;
+import org.apache.james.jmap.draft.model.Keywords;
+import org.apache.james.jmap.draft.model.Number;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageIdManager;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.inmemory.InMemoryMailboxManager;
+import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
+import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.FetchGroupImpl;
+import org.apache.james.mailbox.model.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.MessageResult;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import com.google.common.collect.ImmutableList;
+
+class MessageMetadataViewFactoryTest {
+
+    public static final Username BOB = Username.of("bob");
+    private MessageIdManager messageIdManager;
+    private MessageMetadataViewFactory testee;
+    private MailboxSession session;
+    private MessageManager bobInbox;
+    private MessageManager bobMailbox;
+    private ComposedMessageId message1;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        InMemoryIntegrationResources resources = InMemoryIntegrationResources.defaultResources();
+        messageIdManager = resources.getMessageIdManager();
+        InMemoryMailboxManager mailboxManager = resources.getMailboxManager();
+
+        session = mailboxManager.createSystemSession(BOB);
+        MailboxId bobInboxId = mailboxManager.createMailbox(MailboxPath.inbox(session), session).get();
+        MailboxId bobMailboxId = mailboxManager.createMailbox(MailboxPath.forUser(BOB, "anotherMailbox"), session).get();
+
+        bobInbox = mailboxManager.getMailbox(bobInboxId, session);
+        bobMailbox = mailboxManager.getMailbox(bobMailboxId, session);
+
+        message1 = bobInbox.appendMessage(MessageManager.AppendCommand.builder()
+                .withFlags(new Flags(Flags.Flag.SEEN))
+                .build("header: value\r\n\r\nbody"),
+            session);
+
+        testee = new MessageMetadataViewFactory(resources.getBlobManager());
+    }
+
+    @Test
+    void fromMessageResultsShouldReturnCorrectView() throws Exception {
+        List<MessageResult> messages = messageIdManager
+            .getMessages(ImmutableList.of(message1.getMessageId()), FetchGroupImpl.MINIMAL, session);
+
+        MessageMetadataView actual = testee.fromMessageResults(messages);
+        SoftAssertions.assertSoftly(softly -> {
+           softly.assertThat(actual.getId()).isEqualTo(message1.getMessageId());
+           softly.assertThat(actual.getMailboxIds()).containsExactly(bobInbox.getId());
+           softly.assertThat(actual.getThreadId()).isEqualTo(message1.getMessageId().serialize());
+           softly.assertThat(actual.getSize()).isEqualTo(Number.fromLong(21));
+           softly.assertThat(actual.getKeywords()).isEqualTo(Keywords.strictFactory().from(Keyword.SEEN).asMap());
+           softly.assertThat(actual.getBlobId()).isEqualTo(BlobId.of(message1.getMessageId().serialize()));
+        });
+    }
+
+    @Test
+    void fromMessageResultsShouldCombineKeywords() throws Exception {
+        messageIdManager.setInMailboxes(message1.getMessageId(), ImmutableList.of(bobInbox.getId(), bobMailbox.getId()), session);
+        bobMailbox.setFlags(new Flags(Flags.Flag.FLAGGED), MessageManager.FlagsUpdateMode.REPLACE, MessageRange.all(), session);
+
+        List<MessageResult> messages = messageIdManager
+            .getMessages(ImmutableList.of(message1.getMessageId()), FetchGroupImpl.MINIMAL, session);
+
+        MessageMetadataView actual = testee.fromMessageResults(messages);
+        SoftAssertions.assertSoftly(softly -> {
+           softly.assertThat(actual.getId()).isEqualTo(message1.getMessageId());
+           softly.assertThat(actual.getKeywords()).isEqualTo(Keywords.strictFactory().from(Keyword.SEEN, Keyword.FLAGGED).asMap());
+        });
+    }
+
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org