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/12/06 02:34:24 UTC
[james-project] 14/21: JAMES-2991 Add a compute message preview
listener for JMAP
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 433061a246cf52dc1eb669f8b989f24bc7618d89
Author: Rene Cordier <rc...@linagora.com>
AuthorDate: Tue Dec 3 14:12:35 2019 +0700
JAMES-2991 Add a compute message preview listener for JMAP
---
.../ComputeMessageFastViewProjectionListener.java | 111 +++++++++++
...mputeMessageFastViewProjectionListenerTest.java | 203 +++++++++++++++++++++
2 files changed, 314 insertions(+)
diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/ComputeMessageFastViewProjectionListener.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/ComputeMessageFastViewProjectionListener.java
new file mode 100644
index 0000000..5548448
--- /dev/null
+++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/event/ComputeMessageFastViewProjectionListener.java
@@ -0,0 +1,111 @@
+/****************************************************************
+ * 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.event;
+
+import java.io.IOException;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.james.jmap.api.model.Preview;
+import org.apache.james.jmap.api.projections.MessageFastViewPrecomputedProperties;
+import org.apache.james.jmap.api.projections.MessageFastViewProjection;
+import org.apache.james.jmap.draft.model.message.view.MessageFullView;
+import org.apache.james.jmap.draft.model.message.view.MessageFullViewFactory;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageIdManager;
+import org.apache.james.mailbox.events.Event;
+import org.apache.james.mailbox.events.Group;
+import org.apache.james.mailbox.events.MailboxListener;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.FetchGroup;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.MessageResult;
+import org.apache.james.mailbox.store.SessionProvider;
+import org.parboiled.common.ImmutableList;
+
+import com.github.fge.lambdas.Throwing;
+import com.github.steveash.guavate.Guavate;
+
+import reactor.core.publisher.Flux;
+import reactor.core.scheduler.Schedulers;
+
+public class ComputeMessageFastViewProjectionListener implements MailboxListener.GroupMailboxListener {
+ public static class ComputeMessageFastViewProjectionListenerGroup extends Group {
+
+ }
+
+ static final Group GROUP = new ComputeMessageFastViewProjectionListenerGroup();
+
+ private final MessageIdManager messageIdManager;
+ private final MessageFastViewProjection messageFastViewProjection;
+ private final SessionProvider sessionProvider;
+ private final MessageFullViewFactory messageFullViewFactory;
+
+ @Inject
+ public ComputeMessageFastViewProjectionListener(SessionProvider sessionProvider, MessageIdManager messageIdManager,
+ MessageFastViewProjection messageFastViewProjection,
+ MessageFullViewFactory messageFullViewFactory) {
+ this.sessionProvider = sessionProvider;
+ this.messageIdManager = messageIdManager;
+ this.messageFastViewProjection = messageFastViewProjection;
+ this.messageFullViewFactory = messageFullViewFactory;
+ }
+
+ @Override
+ public Group getDefaultGroup() {
+ return GROUP;
+ }
+
+ @Override
+ public void event(Event event) throws MailboxException {
+ if (event instanceof Added) {
+ MailboxSession session = sessionProvider.createSystemSession(event.getUsername());
+ handleAddedEvent((Added) event, session);
+ }
+ }
+
+ private void handleAddedEvent(Added addedEvent, MailboxSession session) throws MailboxException {
+ Flux.fromIterable(messageIdManager.getMessages(getEventMessageIds(addedEvent), FetchGroup.BODY_CONTENT, session))
+ .publishOn(Schedulers.boundedElastic())
+ .map(Throwing.function(messageResult -> Pair.of(messageResult.getMessageId(), computeFastViewPrecomputedProperties(messageResult))))
+ .flatMap(message -> messageFastViewProjection.store(message.getKey(), message.getValue()))
+ .then()
+ .block();
+ }
+
+ private List<MessageId> getEventMessageIds(Added addedEvent) {
+ return addedEvent.getUids()
+ .stream()
+ .map(uid -> addedEvent.getMetaData(uid).getMessageId())
+ .collect(Guavate.toImmutableSet())
+ .asList();
+ }
+
+ private MessageFastViewPrecomputedProperties computeFastViewPrecomputedProperties(MessageResult messageResult) throws MailboxException, IOException {
+ MessageFullView message = messageFullViewFactory.fromMessageResults(ImmutableList.of(messageResult));
+
+ return MessageFastViewPrecomputedProperties.builder()
+ .preview(Preview.from(message.getPreview().getValue()))
+ .hasAttachment(message.isHasAttachment())
+ .build();
+ }
+}
diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/ComputeMessageFastViewProjectionListenerTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/ComputeMessageFastViewProjectionListenerTest.java
new file mode 100644
index 0000000..2e58340
--- /dev/null
+++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/event/ComputeMessageFastViewProjectionListenerTest.java
@@ -0,0 +1,203 @@
+/****************************************************************
+ * 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.event;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Optional;
+
+import org.apache.james.core.Username;
+import org.apache.james.jmap.api.model.Preview;
+import org.apache.james.jmap.api.projections.MessageFastViewPrecomputedProperties;
+import org.apache.james.jmap.api.projections.MessageFastViewProjection;
+import org.apache.james.jmap.draft.model.PreviewDTO;
+import org.apache.james.jmap.draft.model.message.view.MessageFullViewFactory;
+import org.apache.james.jmap.draft.utils.JsoupHtmlTextExtractor;
+import org.apache.james.jmap.memory.projections.MemoryMessageFastViewProjection;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MailboxSessionUtil;
+import org.apache.james.mailbox.MessageIdManager;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.events.Group;
+import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources;
+import org.apache.james.mailbox.model.ComposedMessageId;
+import org.apache.james.mailbox.model.FetchGroup;
+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.apache.james.mailbox.store.FakeAuthenticator;
+import org.apache.james.mailbox.store.FakeAuthorizator;
+import org.apache.james.mailbox.store.SessionProvider;
+import org.apache.james.mailbox.store.StoreMailboxManager;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.util.html.HtmlTextExtractor;
+import org.apache.james.util.mime.MessageContentExtractor;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import reactor.core.publisher.Mono;
+
+class ComputeMessageFastViewProjectionListenerTest {
+ private static final Username BOB = Username.of("bob");
+ private static final Preview PREVIEW = Preview.from("This should be the preview of the message...");
+ private static final MailboxPath BOB_INBOX_PATH = MailboxPath.inbox(BOB);
+ private static final MailboxPath BOB_OTHER_BOX_PATH = MailboxPath.forUser(BOB, "otherBox");
+ private static final MessageFastViewPrecomputedProperties MESSAGE_FAST_VIEW_PRECOMPUTED_PROPERTIES = MessageFastViewPrecomputedProperties.builder()
+ .preview(PREVIEW)
+ .noAttachments()
+ .build();
+ private static final MessageFastViewPrecomputedProperties MESSAGE_FAST_VIEW_PRECOMPUTED_PROPERTIES_EMPTY = MessageFastViewPrecomputedProperties.builder()
+ .preview(Preview.from(PreviewDTO.from(Optional.empty()).getValue()))
+ .noAttachments()
+ .build();
+
+ MessageFastViewProjection messageFastViewProjection;
+ MessageFullViewFactory messageFullViewFactory;
+ MailboxSession mailboxSession;
+ StoreMailboxManager mailboxManager;
+
+ private MessageManager inboxMessageManager;
+ private MessageManager otherBoxMessageManager;
+
+ @BeforeEach
+ void setup() throws Exception {
+ InMemoryIntegrationResources resources = InMemoryIntegrationResources.defaultResources();
+ mailboxManager = resources.getMailboxManager();
+ MessageIdManager messageIdManager = resources.getMessageIdManager();
+
+ messageFastViewProjection = new MemoryMessageFastViewProjection();
+
+ MessageContentExtractor messageContentExtractor = new MessageContentExtractor();
+ HtmlTextExtractor htmlTextExtractor = new JsoupHtmlTextExtractor();
+
+ messageFullViewFactory = new MessageFullViewFactory(resources.getBlobManager(), messageContentExtractor, htmlTextExtractor);
+
+ FakeAuthenticator authenticator = new FakeAuthenticator();
+ authenticator.addUser(BOB, "12345");
+
+ SessionProvider sessionProvider = new SessionProvider(authenticator, FakeAuthorizator.defaultReject());
+
+ ComputeMessageFastViewProjectionListener listener = new ComputeMessageFastViewProjectionListener(sessionProvider, messageIdManager,
+ messageFastViewProjection, messageFullViewFactory);
+
+ resources.getEventBus().register(listener);
+
+ mailboxSession = MailboxSessionUtil.create(BOB);
+
+ MailboxId inboxId = mailboxManager.createMailbox(BOB_INBOX_PATH, mailboxSession).get();
+ inboxMessageManager = mailboxManager.getMailbox(inboxId, mailboxSession);
+
+ MailboxId otherBoxId = mailboxManager.createMailbox(BOB_OTHER_BOX_PATH, mailboxSession).get();
+ otherBoxMessageManager = mailboxManager.getMailbox(otherBoxId, mailboxSession);
+ }
+
+ @Test
+ void deserializeMailboxAnnotationListenerGroup() throws Exception {
+ assertThat(Group.deserialize("org.apache.james.jmap.event.ComputeMessageFastViewProjectionListener$ComputeMessageFastViewProjectionListenerGroup"))
+ .isEqualTo(new ComputeMessageFastViewProjectionListener.ComputeMessageFastViewProjectionListenerGroup());
+ }
+
+ @Test
+ void shouldStorePreviewWhenBodyMessageNotEmpty() throws Exception {
+ ComposedMessageId composedId = inboxMessageManager.appendMessage(
+ MessageManager.AppendCommand.builder()
+ .build(previewMessage()),
+ mailboxSession);
+
+ assertThat(Mono.from(messageFastViewProjection.retrieve(composedId.getMessageId())).block())
+ .isEqualTo(MESSAGE_FAST_VIEW_PRECOMPUTED_PROPERTIES);
+ }
+
+ @Test
+ void shouldStoreEmptyPreviewWhenEmptyBodyMessage() throws Exception {
+ ComposedMessageId composedId = inboxMessageManager.appendMessage(
+ MessageManager.AppendCommand.builder()
+ .build(emptyMessage()),
+ mailboxSession);
+
+ assertThat(Mono.from(messageFastViewProjection.retrieve(composedId.getMessageId())).block())
+ .isEqualTo(MESSAGE_FAST_VIEW_PRECOMPUTED_PROPERTIES_EMPTY);
+ }
+
+ @Test
+ void shouldStoreMultiplePreviewsWhenMultipleMessagesAdded() throws Exception {
+ ComposedMessageId composedId1 = inboxMessageManager.appendMessage(
+ MessageManager.AppendCommand.builder()
+ .build(previewMessage()),
+ mailboxSession);
+
+ ComposedMessageId composedId2 = inboxMessageManager.appendMessage(
+ MessageManager.AppendCommand.builder()
+ .build(emptyMessage()),
+ mailboxSession);
+
+ SoftAssertions.assertSoftly(softly -> {
+ softly.assertThat(Mono.from(messageFastViewProjection.retrieve(composedId1.getMessageId())).block())
+ .isEqualTo(MESSAGE_FAST_VIEW_PRECOMPUTED_PROPERTIES);
+ softly.assertThat(Mono.from(messageFastViewProjection.retrieve(composedId2.getMessageId())).block())
+ .isEqualTo(MESSAGE_FAST_VIEW_PRECOMPUTED_PROPERTIES_EMPTY);
+ });
+ }
+
+ @Test
+ void shouldKeepPreviewWhenMovingMessage() throws Exception {
+ inboxMessageManager.appendMessage(
+ MessageManager.AppendCommand.builder()
+ .build(previewMessage()),
+ mailboxSession);
+
+ mailboxManager.moveMessages(MessageRange.all(), BOB_INBOX_PATH, BOB_OTHER_BOX_PATH, mailboxSession);
+
+ MessageResult result = otherBoxMessageManager.getMessages(MessageRange.all(), FetchGroup.MINIMAL, mailboxSession).next();
+ assertThat(Mono.from(messageFastViewProjection.retrieve(result.getMessageId())).block())
+ .isEqualTo(MESSAGE_FAST_VIEW_PRECOMPUTED_PROPERTIES);
+ }
+
+ @Test
+ void shouldKeepPreviewWhenCopyingMessage() throws Exception {
+ inboxMessageManager.appendMessage(
+ MessageManager.AppendCommand.builder()
+ .build(previewMessage()),
+ mailboxSession);
+
+ mailboxManager.copyMessages(MessageRange.all(), BOB_INBOX_PATH, BOB_OTHER_BOX_PATH, mailboxSession);
+
+ MessageResult result = otherBoxMessageManager.getMessages(MessageRange.all(), FetchGroup.MINIMAL, mailboxSession).next();
+ assertThat(Mono.from(messageFastViewProjection.retrieve(result.getMessageId())).block())
+ .isEqualTo(MESSAGE_FAST_VIEW_PRECOMPUTED_PROPERTIES);
+ }
+
+ private Message previewMessage() throws Exception {
+ return Message.Builder.of()
+ .setSubject("Preview message")
+ .setBody(PREVIEW.getValue(), StandardCharsets.UTF_8)
+ .build();
+ }
+
+ private Message emptyMessage() throws Exception {
+ return Message.Builder.of()
+ .setSubject("Empty message")
+ .setBody("", StandardCharsets.UTF_8)
+ .build();
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org