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/20 08:34:20 UTC

[james-project] branch master updated (2a949a7 -> 4067084)

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

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


    from 2a949a7  [FIX] Retry WebAdmin startup upon BindException
     new a557d0a  JAMES-2993 Document webadmin endpoint for message fast view recomputation
     new 00ed3e2  JAMES-2993 Create maven module for webadmin-data-jmap
     new 6f92767  JAMES-2993 Implement recompute all JMAP message preview task
     new 293a993  JAMES-2993 Implement recompute all JMAP message preview request to task
     new c8ab2ff  JAMES-2993 Guice bindings for JMAP preview re-computing
     new cae5582  JAMES-2993 Simple webadmin integration test for JMAP routes
     new c1d00d1  JAMES-2993 Expose projecting fixing endpoint over memory
     new 4067084  JAMES-2993 Create maven module for webadmin-data-jmap

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 pom.xml                                            |   5 +
 server/container/guice/cassandra-guice/pom.xml     |   4 +
 .../org/apache/james/CassandraJamesServerMain.java |  10 +-
 .../james/modules/TaskSerializationModule.java     |  13 +
 .../java/org/apache/james/JPAJamesServerMain.java  |   2 -
 server/container/guice/memory-guice/pom.xml        |   4 +
 .../org/apache/james/MemoryJamesServerMain.java    |   8 +-
 server/container/guice/pom.xml                     |   6 +
 .../{webadmin-mailbox => webadmin-jmap}/pom.xml    |   9 +-
 .../james/modules/server/JmapTasksModule.java}     |  10 +-
 .../james/modules/server/MailboxRoutesModule.java  |   8 +
 .../modules/server/MailboxesRoutesModule.java      |   4 +
 .../james/modules/server/ReIndexingModule.java     |  16 +
 .../MemoryMessageFastViewProjection.java           |   7 +-
 .../RabbitMQWebAdminServerIntegrationTest.java     |   1 -
 .../integration/WebAdminServerIntegrationTest.java |  20 +-
 server/protocols/webadmin/pom.xml                  |   1 +
 .../protocols/webadmin/webadmin-jmap}/pom.xml      |  77 ++--
 .../james/webadmin/data/jmap}/Constants.java       |  11 +-
 .../jmap/MessageFastViewProjectionCorrector.java   | 178 +++++++++
 ...teAllFastViewProjectionItemsRequestToTask.java} |  18 +-
 .../RecomputeAllFastViewProjectionItemsTask.java   | 138 +++++++
 ...uteAllPreviewsTaskAdditionalInformationDTO.java |  98 +++++
 ...llFastViewProjectionItemsRequestToTaskTest.java | 353 +++++++++++++++++
 ...tionItemsTaskAdditionalInformationDTOTest.java} |  37 +-
 ...stViewProjectionItemsTaskSerializationTest.java |  30 +-
 .../json/recomputeAll.additionalInformation.json   |   8 +
 .../src/test/resources/json/recomputeAll.task.json |   1 +
 .../james/webadmin/routes/MailboxesRoutes.java     | 420 ++++++++++++---------
 ...onTest.java => MailboxesRoutesNoTasksTest.java} |  70 ++--
 .../james/webadmin/routes/MailboxesRoutesTest.java |  18 +-
 src/site/markdown/server/manage-webadmin.md        |  37 ++
 32 files changed, 1275 insertions(+), 347 deletions(-)
 copy server/container/guice/protocols/{webadmin-mailbox => webadmin-jmap}/pom.xml (85%)
 copy server/container/guice/protocols/{webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxesRoutesModule.java => webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTasksModule.java} (75%)
 copy {mailbox/event/event-rabbitmq => server/protocols/webadmin/webadmin-jmap}/pom.xml (70%)
 copy server/protocols/webadmin/{webadmin-core/src/main/java/org/apache/james/webadmin => webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap}/Constants.java (85%)
 create mode 100644 server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java
 copy server/{container/guice/protocols/webadmin/src/main/java/org/apache/james/utils/WebAdminGuiceProbe.java => protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java} (73%)
 create mode 100644 server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTask.java
 create mode 100644 server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java
 create mode 100644 server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java
 copy server/{mailrepository/mailrepository-api/src/test/java/org/apache/james/mailrepository/api/MailKeyTest.java => protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskAdditionalInformationDTOTest.java} (60%)
 copy metrics/metrics-logger/src/test/java/org/apache/james/metrics/logger/DefaultMetricTest.java => server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskSerializationTest.java (64%)
 create mode 100644 server/protocols/webadmin/webadmin-jmap/src/test/resources/json/recomputeAll.additionalInformation.json
 create mode 100644 server/protocols/webadmin/webadmin-jmap/src/test/resources/json/recomputeAll.task.json
 copy server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/{UserMailboxesRoutesNoIndexationTest.java => MailboxesRoutesNoTasksTest.java} (57%)


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


[james-project] 04/08: JAMES-2993 Implement recompute all JMAP message preview request to task

Posted by bt...@apache.org.
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 293a9938b08a2656a82f5a47febb2fc6d3e22550
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Dec 16 14:38:45 2019 +0700

    JAMES-2993 Implement recompute all JMAP message preview request to task
---
 .../protocols/webadmin/webadmin-data-jmap/pom.xml  |   5 +
 .../james/webadmin/data/jmap/Constants.java}       |  35 +-
 .../jmap/MessageFastViewProjectionCorrector.java   |  48 ++-
 ...teAllFastViewProjectionItemsRequestToTask.java} |  36 +--
 .../data/jmap/RecomputeAllPreviewsTask.java        |  29 --
 ...llFastViewProjectionItemsRequestToTaskTest.java | 353 +++++++++++++++++++++
 ...llPreviewsTaskAdditionalInformationDTOTest.java |   6 -
 .../RecomputeAllPreviewsTaskSerializationTest.java |   7 -
 .../json/recomputeAll.additionalInformation.json   |   2 +-
 .../src/test/resources/json/recomputeAll.task.json |   2 +-
 10 files changed, 412 insertions(+), 111 deletions(-)

diff --git a/server/protocols/webadmin/webadmin-data-jmap/pom.xml b/server/protocols/webadmin/webadmin-data-jmap/pom.xml
index e42b17e..7c9330f 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/pom.xml
+++ b/server/protocols/webadmin/webadmin-data-jmap/pom.xml
@@ -96,6 +96,11 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>metrics-tests</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>testing-base</artifactId>
             <scope>test</scope>
         </dependency>
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java
similarity index 54%
copy from server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
copy to server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java
index e56432a..b076804 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java
@@ -19,35 +19,8 @@
 
 package org.apache.james.webadmin.data.jmap;
 
-import static org.mockito.Mockito.mock;
+import org.apache.james.webadmin.tasks.TaskRegistrationKey;
 
-import org.apache.james.JsonSerializationVerifier;
-import org.apache.james.util.ClassLoaderUtils;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-class RecomputeAllPreviewsTaskSerializationTest {
-    MessageFastViewProjectionCorrector corrector;
-
-    @BeforeEach
-    void setUp() {
-        corrector = mock(MessageFastViewProjectionCorrector.class);
-    }
-
-    @Test
-    void shouldMatchJsonSerializationContract() throws Exception {
-        JsonSerializationVerifier.dtoModule(RecomputeAllPreviewsTask.module(corrector))
-            .bean(new RecomputeAllPreviewsTask(corrector))
-            .json(ClassLoaderUtils.getSystemResourceAsString("json/recomputeAll.task.json"))
-            .verify();
-    }
-
-    @Test
-    void shouldMatchBeanContract() {
-        EqualsVerifier.forClass(RecomputeAllPreviewsTask.class)
-            .withIgnoredFields("corrector", "progress")
-            .verify();
-    }
-}
\ No newline at end of file
+public interface Constants {
+    TaskRegistrationKey TASK_REGISTRATION_KEY = TaskRegistrationKey.of("recomputeFastViewProjectionItems");
+}
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java
index 6fc4c86..0eb7f08 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.webadmin.data.jmap;
 
+import java.io.IOException;
 import java.util.concurrent.atomic.AtomicLong;
 
 import javax.inject.Inject;
@@ -32,7 +33,10 @@ import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.FetchGroup;
+import org.apache.james.mailbox.model.MailboxMetaData;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.apache.james.user.api.UsersRepository;
 import org.apache.james.user.api.UsersRepositoryException;
@@ -111,8 +115,8 @@ public class MessageFastViewProjectionCorrector {
     private Mono<Void> correctUsersProjectionItems(Progress progress, Username username) {
         try {
             MailboxSession session = mailboxManager.createSystemSession(username);
-            return Flux.fromIterable(mailboxManager.search(MailboxQuery.privateMailboxesBuilder(session).build(), session))
-                .concatMap(mailboxMetadata -> Mono.fromCallable(() -> mailboxManager.getMailbox(mailboxMetadata.getId(), session)))
+            return listUsersMailboxes(session)
+                .concatMap(mailboxMetadata -> retrieveMailbox(session, mailboxMetadata))
                 .concatMap(Throwing.function(messageManager -> correctMailboxProjectionItems(progress, messageManager, session)))
                 .doOnComplete(progress.processedUserCount::incrementAndGet)
                 .onErrorContinue((error, o) -> {
@@ -128,10 +132,10 @@ public class MessageFastViewProjectionCorrector {
     }
 
     private Mono<Void> correctMailboxProjectionItems(Progress progress, MessageManager messageManager, MailboxSession session) throws MailboxException {
-        return Iterators.toFlux(messageManager.getMessages(MessageRange.all(), FetchGroup.MINIMAL, session))
-            .concatMap(Throwing.function(messageResult -> Iterators.toFlux(messageManager.getMessages(MessageRange.all(), FetchGroup.BODY_CONTENT, session))))
-            .map(Throwing.function(messageResult -> Pair.of(messageResult.getMessageId(), projectionItemFactory.from(messageResult))))
-            .concatMap(pair -> Mono.from(messageFastViewProjection.store(pair.getKey(), pair.getValue()))
+        return listAllMailboxMessages(messageManager, session)
+            .concatMap(messageResult -> retrieveContent(messageManager, session, messageResult))
+            .map(this::computeProjectionEntry)
+            .concatMap(pair -> storeProjectionEntry(pair)
                 .doOnSuccess(any -> progress.processedMessageCount.incrementAndGet()))
             .onErrorContinue((error, triggeringValue) -> {
                 LOGGER.error("JMAP preview re-computation aborted for {} - {}", session.getUser(), triggeringValue, error);
@@ -139,4 +143,36 @@ public class MessageFastViewProjectionCorrector {
             })
             .then();
     }
+
+    private Flux<MailboxMetaData> listUsersMailboxes(MailboxSession session) throws MailboxException {
+        return Flux.fromIterable(mailboxManager.search(MailboxQuery.privateMailboxesBuilder(session).build(), session));
+    }
+
+    private Mono<MessageManager> retrieveMailbox(MailboxSession session, MailboxMetaData mailboxMetadata) {
+        return Mono.fromCallable(() -> mailboxManager.getMailbox(mailboxMetadata.getId(), session));
+    }
+
+    private Flux<MessageResult> listAllMailboxMessages(MessageManager messageManager, MailboxSession session) throws MailboxException {
+        return Iterators.toFlux(messageManager.getMessages(MessageRange.all(), FetchGroup.MINIMAL, session));
+    }
+
+    private Flux<MessageResult> retrieveContent(MessageManager messageManager, MailboxSession session, MessageResult messageResult) {
+        try {
+            return Iterators.toFlux(messageManager.getMessages(MessageRange.one(messageResult.getUid()), FetchGroup.FULL_CONTENT, session));
+        } catch (MailboxException e) {
+            return Flux.error(e);
+        }
+    }
+
+    private Pair<MessageId, MessageFastViewPrecomputedProperties> computeProjectionEntry(MessageResult messageResult) {
+        try {
+            return Pair.of(messageResult.getMessageId(), projectionItemFactory.from(messageResult));
+        } catch (MailboxException | IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private Mono<Void> storeProjectionEntry(Pair<MessageId, MessageFastViewPrecomputedProperties> pair) {
+        return Mono.from(messageFastViewProjection.store(pair.getKey(), pair.getValue()));
+    }
 }
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java
similarity index 55%
copy from server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
copy to server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java
index e56432a..a5810e1 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java
@@ -19,35 +19,11 @@
 
 package org.apache.james.webadmin.data.jmap;
 
-import static org.mockito.Mockito.mock;
+import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
 
-import org.apache.james.JsonSerializationVerifier;
-import org.apache.james.util.ClassLoaderUtils;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-class RecomputeAllPreviewsTaskSerializationTest {
-    MessageFastViewProjectionCorrector corrector;
-
-    @BeforeEach
-    void setUp() {
-        corrector = mock(MessageFastViewProjectionCorrector.class);
-    }
-
-    @Test
-    void shouldMatchJsonSerializationContract() throws Exception {
-        JsonSerializationVerifier.dtoModule(RecomputeAllPreviewsTask.module(corrector))
-            .bean(new RecomputeAllPreviewsTask(corrector))
-            .json(ClassLoaderUtils.getSystemResourceAsString("json/recomputeAll.task.json"))
-            .verify();
-    }
-
-    @Test
-    void shouldMatchBeanContract() {
-        EqualsVerifier.forClass(RecomputeAllPreviewsTask.class)
-            .withIgnoredFields("corrector", "progress")
-            .verify();
+public class RecomputeAllFastViewProjectionItemsRequestToTask extends TaskFromRequestRegistry.TaskRegistration {
+    public RecomputeAllFastViewProjectionItemsRequestToTask(MessageFastViewProjectionCorrector corrector) {
+        super(Constants.TASK_REGISTRATION_KEY,
+            request -> new RecomputeAllPreviewsTask(corrector));
     }
-}
\ No newline at end of file
+}
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTask.java b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTask.java
index bacf8ad..99b5b8d 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTask.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTask.java
@@ -82,25 +82,6 @@ public class RecomputeAllPreviewsTask implements Task {
         public Instant timestamp() {
             return timestamp;
         }
-
-        @Override
-        public final boolean equals(Object o) {
-            if (o instanceof AdditionalInformation) {
-                AdditionalInformation that = (AdditionalInformation) o;
-
-                return Objects.equals(this.processedUserCount, that.processedUserCount)
-                    && Objects.equals(this.processedMessageCount, that.processedMessageCount)
-                    && Objects.equals(this.failedUserCount, that.failedUserCount)
-                    && Objects.equals(this.failedMessageCount, that.failedMessageCount)
-                    && Objects.equals(this.timestamp, that.timestamp);
-            }
-            return false;
-        }
-
-        @Override
-        public final int hashCode() {
-            return Objects.hash(processedUserCount, processedMessageCount, failedUserCount, failedMessageCount, timestamp);
-        }
     }
 
     public static class RecomputeAllPreviousTaskDTO implements TaskDTO {
@@ -155,14 +136,4 @@ public class RecomputeAllPreviewsTask implements Task {
     public Optional<TaskExecutionDetails.AdditionalInformation> details() {
         return Optional.of(AdditionalInformation.from(progress));
     }
-
-    @Override
-    public final boolean equals(Object o) {
-        return o instanceof RecomputeAllPreviewsTask;
-    }
-
-    @Override
-    public final int hashCode() {
-        return Objects.hash(RecomputeAllPreviewsTask.class);
-    }
 }
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java
new file mode 100644
index 0000000..a351095
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java
@@ -0,0 +1,353 @@
+/****************************************************************
+ * 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.webadmin.data.jmap;
+
+import static io.restassured.RestAssured.given;
+import static io.restassured.RestAssured.when;
+import static io.restassured.RestAssured.with;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+import java.util.Optional;
+
+import org.apache.james.core.Username;
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.jmap.api.model.Preview;
+import org.apache.james.jmap.api.projections.MessageFastViewPrecomputedProperties;
+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.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.MailboxId;
+import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.metrics.tests.RecordingMetricFactory;
+import org.apache.james.task.Hostname;
+import org.apache.james.task.MemoryTaskManager;
+import org.apache.james.task.TaskManager;
+import org.apache.james.user.memory.MemoryUsersRepository;
+import org.apache.james.util.html.HtmlTextExtractor;
+import org.apache.james.util.mime.MessageContentExtractor;
+import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.WebAdminServer;
+import org.apache.james.webadmin.WebAdminUtils;
+import org.apache.james.webadmin.routes.TasksRoutes;
+import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
+import org.apache.james.webadmin.utils.ErrorResponder;
+import org.apache.james.webadmin.utils.JsonTransformer;
+import org.eclipse.jetty.http.HttpStatus;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import io.restassured.RestAssured;
+import io.restassured.filter.log.LogDetail;
+import spark.Service;
+
+class RecomputeAllFastViewProjectionItemsRequestToTaskTest {
+    private final class JMAPRoutes implements Routes {
+        private final MessageFastViewProjectionCorrector corrector;
+        private final TaskManager taskManager;
+
+        private JMAPRoutes(MessageFastViewProjectionCorrector corrector, TaskManager taskManager) {
+            this.corrector = corrector;
+            this.taskManager = taskManager;
+        }
+
+        @Override
+        public String getBasePath() {
+            return BASE_PATH;
+        }
+
+        @Override
+        public void define(Service service) {
+            service.post(BASE_PATH,
+                TaskFromRequestRegistry.builder()
+                    .registrations(new RecomputeAllFastViewProjectionItemsRequestToTask(corrector))
+                    .buildAsRoute(taskManager),
+                new JsonTransformer());
+        }
+    }
+
+    static final String BASE_PATH = "/mailboxes";
+
+
+    static final MessageFastViewPrecomputedProperties PROJECTION_ITEM = MessageFastViewPrecomputedProperties.builder()
+        .preview(Preview.from("body"))
+        .hasAttachment(false)
+        .build();
+
+    static final DomainList NO_DOMAIN_LIST = null;
+    static final Username BOB = Username.of("bob");
+
+    private WebAdminServer webAdminServer;
+    private MemoryTaskManager taskManager;
+    private MemoryMessageFastViewProjection messageFastViewProjection;
+    private InMemoryMailboxManager mailboxManager;
+    private MemoryUsersRepository usersRepository;
+
+    @BeforeEach
+    void setUp() {
+        JsonTransformer jsonTransformer = new JsonTransformer();
+        taskManager = new MemoryTaskManager(new Hostname("foo"));
+
+        messageFastViewProjection = new MemoryMessageFastViewProjection(new RecordingMetricFactory());
+        mailboxManager = InMemoryIntegrationResources.defaultResources().getMailboxManager();
+        usersRepository = MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST);
+        MessageContentExtractor messageContentExtractor = new MessageContentExtractor();
+        HtmlTextExtractor htmlTextExtractor = new JsoupHtmlTextExtractor();
+        Preview.Factory previewFactory = new Preview.Factory(messageContentExtractor, htmlTextExtractor);
+        MessageFastViewPrecomputedProperties.Factory projectionItemFactory = new MessageFastViewPrecomputedProperties.Factory(previewFactory);
+        webAdminServer = WebAdminUtils.createWebAdminServer(
+            new TasksRoutes(taskManager, jsonTransformer),
+            new JMAPRoutes(
+                new MessageFastViewProjectionCorrector(usersRepository, mailboxManager, messageFastViewProjection, projectionItemFactory),
+                taskManager))
+            .start();
+
+        RestAssured.requestSpecification = WebAdminUtils.buildRequestSpecification(webAdminServer)
+            .setBasePath(BASE_PATH)
+            .log(LogDetail.URI)
+            .build();
+    }
+
+    @AfterEach
+    void afterEach() {
+        webAdminServer.destroy();
+        taskManager.stop();
+    }
+
+    @Test
+    void actionRequestParameterShouldBeCompulsory() {
+        when()
+            .post()
+        .then()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .body("statusCode", is(400))
+            .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+            .body("message", is("Invalid arguments supplied in the user request"))
+            .body("details", is("'action' query parameter is compulsory. Supported values are [recomputeFastViewProjectionItems]"));
+    }
+
+    @Test
+    void postShouldFailUponEmptyAction() {
+        given()
+            .queryParam("action", "")
+            .post()
+        .then()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .body("statusCode", is(400))
+            .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+            .body("message", is("Invalid arguments supplied in the user request"))
+            .body("details", is("'action' query parameter cannot be empty or blank. Supported values are [recomputeFastViewProjectionItems]"));
+    }
+
+    @Test
+    void postShouldFailUponInvalidAction() {
+        given()
+            .queryParam("action", "invalid")
+            .post()
+        .then()
+            .statusCode(HttpStatus.BAD_REQUEST_400)
+            .body("statusCode", is(400))
+            .body("type", is(ErrorResponder.ErrorType.INVALID_ARGUMENT.getType()))
+            .body("message", is("Invalid arguments supplied in the user request"))
+            .body("details", is("Invalid value supplied for query parameter 'action': invalid. Supported values are [recomputeFastViewProjectionItems]"));
+    }
+
+    @Test
+    void postShouldCreateANewTask() {
+        given()
+            .queryParam("action", "recomputeFastViewProjectionItems")
+            .post()
+        .then()
+            .statusCode(HttpStatus.CREATED_201)
+            .body("taskId", notNullValue());
+    }
+
+    @Test
+    void recomputeAllShouldCompleteWhenNoUser() {
+        String taskId = with()
+            .queryParam("action", "recomputeFastViewProjectionItems")
+            .post()
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"))
+            .body("taskId", is(taskId))
+            .body("type", is("RecomputeAllPreviewsTask"))
+            .body("additionalInformation.processedUserCount", is(0))
+            .body("additionalInformation.processedMessageCount", is(0))
+            .body("additionalInformation.failedUserCount", is(0))
+            .body("additionalInformation.failedMessageCount", is(0))
+            .body("startedDate", is(notNullValue()))
+            .body("submitDate", is(notNullValue()))
+            .body("completedDate", is(notNullValue()));
+    }
+
+    @Test
+    void recomputeAllShouldCompleteWhenUserWithNoMailbox()throws Exception {
+        usersRepository.addUser(BOB, "pass");
+
+        String taskId = with()
+            .queryParam("action", "recomputeFastViewProjectionItems")
+            .post()
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"))
+            .body("taskId", is(taskId))
+            .body("type", is("RecomputeAllPreviewsTask"))
+            .body("additionalInformation.processedUserCount", is(1))
+            .body("additionalInformation.processedMessageCount", is(0))
+            .body("additionalInformation.failedUserCount", is(0))
+            .body("additionalInformation.failedMessageCount", is(0))
+            .body("startedDate", is(notNullValue()))
+            .body("submitDate", is(notNullValue()))
+            .body("completedDate", is(notNullValue()));
+    }
+
+    @Test
+    void recomputeAllShouldCompleteWhenUserWithNoMessage()throws Exception {
+        usersRepository.addUser(BOB, "pass");
+        mailboxManager.createMailbox(MailboxPath.inbox(BOB), mailboxManager.createSystemSession(BOB));
+
+        String taskId = with()
+            .queryParam("action", "recomputeFastViewProjectionItems")
+            .post()
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"))
+            .body("taskId", is(taskId))
+            .body("type", is("RecomputeAllPreviewsTask"))
+            .body("additionalInformation.processedUserCount", is(1))
+            .body("additionalInformation.processedMessageCount", is(0))
+            .body("additionalInformation.failedUserCount", is(0))
+            .body("additionalInformation.failedMessageCount", is(0))
+            .body("startedDate", is(notNullValue()))
+            .body("submitDate", is(notNullValue()))
+            .body("completedDate", is(notNullValue()));
+    }
+
+    @Test
+    void recomputeAllShouldCompleteWhenOneMessage()throws Exception {
+        usersRepository.addUser(BOB, "pass");
+        MailboxSession session = mailboxManager.createSystemSession(BOB);
+        Optional<MailboxId> mailboxId = mailboxManager.createMailbox(MailboxPath.inbox(BOB), session);
+        mailboxManager.getMailbox(mailboxId.get(), session).appendMessage(
+            MessageManager.AppendCommand.builder().build("header: value\r\n\r\nbody"),
+            session);
+
+        String taskId = with()
+            .queryParam("action", "recomputeFastViewProjectionItems")
+            .post()
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"))
+            .body("taskId", is(taskId))
+            .body("type", is("RecomputeAllPreviewsTask"))
+            .body("additionalInformation.processedUserCount", is(1))
+            .body("additionalInformation.processedMessageCount", is(1))
+            .body("additionalInformation.failedUserCount", is(0))
+            .body("additionalInformation.failedMessageCount", is(0))
+            .body("startedDate", is(notNullValue()))
+            .body("submitDate", is(notNullValue()))
+            .body("completedDate", is(notNullValue()));
+    }
+
+    @Test
+    void recomputeAllShouldUpdateProjection()throws Exception {
+        usersRepository.addUser(BOB, "pass");
+        MailboxSession session = mailboxManager.createSystemSession(BOB);
+        Optional<MailboxId> mailboxId = mailboxManager.createMailbox(MailboxPath.inbox(BOB), session);
+        ComposedMessageId messageId = mailboxManager.getMailbox(mailboxId.get(), session).appendMessage(
+            MessageManager.AppendCommand.builder().build("header: value\r\n\r\nbody"),
+            session);
+
+        String taskId = with()
+            .queryParam("action", "recomputeFastViewProjectionItems")
+            .post()
+            .jsonPath()
+            .get("taskId");
+
+        with()
+            .basePath(TasksRoutes.BASE)
+            .get(taskId + "/await");
+
+        assertThat(messageFastViewProjection.retrieve(messageId.getMessageId()).block())
+            .isEqualTo(PROJECTION_ITEM);
+    }
+
+    @Test
+    void recomputeAllShouldBeIdempotent()throws Exception {
+        usersRepository.addUser(BOB, "pass");
+        MailboxSession session = mailboxManager.createSystemSession(BOB);
+        Optional<MailboxId> mailboxId = mailboxManager.createMailbox(MailboxPath.inbox(BOB), session);
+        ComposedMessageId messageId = mailboxManager.getMailbox(mailboxId.get(), session).appendMessage(
+            MessageManager.AppendCommand.builder().build("header: value\r\n\r\nbody"),
+            session);
+
+        String taskId1 = with()
+            .queryParam("action", "recomputeFastViewProjectionItems")
+            .post()
+            .jsonPath()
+            .get("taskId");
+        with()
+            .basePath(TasksRoutes.BASE)
+            .get(taskId1 + "/await");
+
+        String taskId2 = with()
+            .queryParam("action", "recomputeFastViewProjectionItems")
+            .post()
+            .jsonPath()
+            .get("taskId");
+        with()
+            .basePath(TasksRoutes.BASE)
+            .get(taskId2 + "/await");
+
+        assertThat(messageFastViewProjection.retrieve(messageId.getMessageId()).block())
+            .isEqualTo(PROJECTION_ITEM);
+    }
+}
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTOTest.java b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTOTest.java
index 125464f..5fad6d9 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTOTest.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTOTest.java
@@ -38,10 +38,4 @@ class RecomputeAllPreviewsTaskAdditionalInformationDTOTest {
             .json(ClassLoaderUtils.getSystemResourceAsString("json/recomputeAll.additionalInformation.json"))
             .verify();
     }
-
-    @Test
-    void shouldMatchBeanContract() {
-        EqualsVerifier.forClass(RecomputeAllPreviewsTask.AdditionalInformation.class)
-            .verify();
-    }
 }
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
index e56432a..177a2b6 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
@@ -43,11 +43,4 @@ class RecomputeAllPreviewsTaskSerializationTest {
             .json(ClassLoaderUtils.getSystemResourceAsString("json/recomputeAll.task.json"))
             .verify();
     }
-
-    @Test
-    void shouldMatchBeanContract() {
-        EqualsVerifier.forClass(RecomputeAllPreviewsTask.class)
-            .withIgnoredFields("corrector", "progress")
-            .verify();
-    }
 }
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.additionalInformation.json b/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.additionalInformation.json
index bc3d81f..6d130f9 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.additionalInformation.json
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.additionalInformation.json
@@ -1,5 +1,5 @@
 {
-  "type":"RecomputeAllPreviewsTask",
+  "type":"RecomputeAllFastViewProjectionItemsTask",
   "timestamp":"2007-12-03T10:15:30Z",
   "processedUserCount":1,
   "processedMessageCount":2,
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.task.json b/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.task.json
index a447293..a76c799 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.task.json
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.task.json
@@ -1 +1 @@
-{"type":"RecomputeAllPreviewsTask"}
\ No newline at end of file
+{"type":"RecomputeAllFastViewProjectionItemsTask"}
\ 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


[james-project] 02/08: JAMES-2993 Create maven module for webadmin-data-jmap

Posted by bt...@apache.org.
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 00ed3e278e1e6800513997208893b3091509ccb8
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Nov 27 11:54:07 2019 +0700

    JAMES-2993 Create maven module for webadmin-data-jmap
---
 server/protocols/webadmin/pom.xml                  |  1 +
 .../webadmin/{ => webadmin-data-jmap}/pom.xml      | 36 +++++++++++-----------
 2 files changed, 19 insertions(+), 18 deletions(-)

diff --git a/server/protocols/webadmin/pom.xml b/server/protocols/webadmin/pom.xml
index 2758ade..82edadd 100644
--- a/server/protocols/webadmin/pom.xml
+++ b/server/protocols/webadmin/pom.xml
@@ -37,6 +37,7 @@
         <module>webadmin-cassandra-data</module>
         <module>webadmin-core</module>
         <module>webadmin-data</module>
+        <module>webadmin-data-jmap</module>
         <module>webadmin-mailbox</module>
         <module>webadmin-mailbox-deleted-message-vault</module>
         <module>webadmin-mailqueue</module>
diff --git a/server/protocols/webadmin/pom.xml b/server/protocols/webadmin/webadmin-data-jmap/pom.xml
similarity index 64%
copy from server/protocols/webadmin/pom.xml
copy to server/protocols/webadmin/webadmin-data-jmap/pom.xml
index 2758ade..9ac8a75 100644
--- a/server/protocols/webadmin/pom.xml
+++ b/server/protocols/webadmin/webadmin-data-jmap/pom.xml
@@ -19,29 +19,29 @@
 -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
     <modelVersion>4.0.0</modelVersion>
-
     <parent>
         <groupId>org.apache.james</groupId>
-        <artifactId>james-project</artifactId>
+        <artifactId>james-server</artifactId>
         <version>3.5.0-SNAPSHOT</version>
         <relativePath>../../../pom.xml</relativePath>
     </parent>
 
-    <artifactId>james-server-webadmin</artifactId>
-    <packaging>pom</packaging>
-
-    <name>Apache James :: Server :: Web Admin</name>
+    <artifactId>james-server-webadmin-data-jmap</artifactId>
 
-    <modules>
-        <module>webadmin-cassandra</module>
-        <module>webadmin-cassandra-data</module>
-        <module>webadmin-core</module>
-        <module>webadmin-data</module>
-        <module>webadmin-mailbox</module>
-        <module>webadmin-mailbox-deleted-message-vault</module>
-        <module>webadmin-mailqueue</module>
-        <module>webadmin-mailrepository</module>
-        <module>webadmin-swagger</module>
-    </modules>
+    <name>Apache James :: Server :: Web Admin :: data :: JMAP</name>
 
-</project>
+    <dependencies>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>james-server-data-jmap</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>james-server-jmap-draft</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-webadmin-core</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ 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


[james-project] 07/08: JAMES-2993 Expose projecting fixing endpoint over memory

Posted by bt...@apache.org.
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 c1d00d110362c3991d3c5655f8dd31e612f4b6f8
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Dec 18 10:05:32 2019 +0700

    JAMES-2993 Expose projecting fixing endpoint over memory
---
 .../org/apache/james/CassandraJamesServerMain.java |   2 -
 .../java/org/apache/james/JPAJamesServerMain.java  |   2 -
 .../org/apache/james/MemoryJamesServerMain.java    |   2 +-
 .../james/modules/server/MailboxRoutesModule.java  |   8 +
 .../james/modules/server/ReIndexingModule.java     |  16 +
 .../RabbitMQWebAdminServerIntegrationTest.java     |   1 -
 .../integration/WebAdminServerIntegrationTest.java |   3 +-
 .../james/webadmin/routes/MailboxesRoutes.java     | 422 +++++++++++----------
 .../routes/MailboxesRoutesNoTasksTest.java         | 102 +++++
 .../james/webadmin/routes/MailboxesRoutesTest.java |  18 +-
 10 files changed, 369 insertions(+), 207 deletions(-)

diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
index f62e1bd..05e07ea 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
@@ -63,7 +63,6 @@ import org.apache.james.modules.server.JmapTasksModule;
 import org.apache.james.modules.server.MailQueueRoutesModule;
 import org.apache.james.modules.server.MailRepositoriesRoutesModule;
 import org.apache.james.modules.server.MailboxRoutesModule;
-import org.apache.james.modules.server.MailboxesRoutesModule;
 import org.apache.james.modules.server.MessagesRoutesModule;
 import org.apache.james.modules.server.ReIndexingModule;
 import org.apache.james.modules.server.SieveRoutesModule;
@@ -89,7 +88,6 @@ public class CassandraJamesServerMain {
         new DeletedMessageVaultRoutesModule(),
         new DLPRoutesModule(),
         new JmapTasksModule(),
-        new MailboxesRoutesModule(),
         new MailboxRoutesModule(),
         new MailQueueRoutesModule(),
         new MailRepositoriesRoutesModule(),
diff --git a/server/container/guice/jpa-guice/src/main/java/org/apache/james/JPAJamesServerMain.java b/server/container/guice/jpa-guice/src/main/java/org/apache/james/JPAJamesServerMain.java
index b4f2b64..d03f0cf 100644
--- a/server/container/guice/jpa-guice/src/main/java/org/apache/james/JPAJamesServerMain.java
+++ b/server/container/guice/jpa-guice/src/main/java/org/apache/james/JPAJamesServerMain.java
@@ -41,7 +41,6 @@ import org.apache.james.modules.server.JMXServerModule;
 import org.apache.james.modules.server.MailQueueRoutesModule;
 import org.apache.james.modules.server.MailRepositoriesRoutesModule;
 import org.apache.james.modules.server.MailboxRoutesModule;
-import org.apache.james.modules.server.MailboxesRoutesModule;
 import org.apache.james.modules.server.NoJwtModule;
 import org.apache.james.modules.server.RawPostDequeueDecoratorModule;
 import org.apache.james.modules.server.ReIndexingModule;
@@ -61,7 +60,6 @@ public class JPAJamesServerMain {
         new WebAdminServerModule(),
         new DataRoutesModules(),
         new MailboxRoutesModule(),
-        new MailboxesRoutesModule(),
         new MailQueueRoutesModule(),
         new MailRepositoriesRoutesModule(),
         new ReIndexingModule(),
diff --git a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
index 489b055..21f65f3 100644
--- a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
+++ b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
@@ -70,7 +70,6 @@ public class MemoryJamesServerMain {
         new DataRoutesModules(),
         new DeletedMessageVaultRoutesModule(),
         new DLPRoutesModule(),
-        new JmapTasksModule(),
         new MailboxRoutesModule(),
         new MailQueueRoutesModule(),
         new MailRepositoriesRoutesModule(),
@@ -97,6 +96,7 @@ public class MemoryJamesServerMain {
         new SpamAssassinListenerModule());
 
     public static final Module JMAP = Modules.combine(
+        new JmapTasksModule(),
         new MemoryDataJmapModule(),
         new JMAPDraftServerModule());
 
diff --git a/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxRoutesModule.java b/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxRoutesModule.java
index 9b05566..42a7c88 100644
--- a/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxRoutesModule.java
+++ b/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxRoutesModule.java
@@ -19,6 +19,9 @@
 
 package org.apache.james.modules.server;
 
+import static org.apache.james.webadmin.routes.MailboxesRoutes.ALL_MAILBOXES_TASKS;
+import static org.apache.james.webadmin.routes.MailboxesRoutes.ONE_MAILBOX_TASKS;
+import static org.apache.james.webadmin.routes.MailboxesRoutes.ONE_MAIL_TASKS;
 import static org.apache.james.webadmin.routes.UserMailboxesRoutes.USER_MAILBOXES_OPERATIONS_INJECTION_KEY;
 
 import org.apache.james.webadmin.Routes;
@@ -26,6 +29,7 @@ import org.apache.james.webadmin.jackson.QuotaModule;
 import org.apache.james.webadmin.routes.DomainQuotaRoutes;
 import org.apache.james.webadmin.routes.EventDeadLettersRoutes;
 import org.apache.james.webadmin.routes.GlobalQuotaRoutes;
+import org.apache.james.webadmin.routes.MailboxesRoutes;
 import org.apache.james.webadmin.routes.UserMailboxesRoutes;
 import org.apache.james.webadmin.routes.UserQuotaRoutes;
 import org.apache.james.webadmin.tasks.TaskFromRequestRegistry.TaskRegistration;
@@ -45,10 +49,14 @@ public class MailboxRoutesModule extends AbstractModule {
         routesMultibinder.addBinding().to(GlobalQuotaRoutes.class);
         routesMultibinder.addBinding().to(UserQuotaRoutes.class);
         routesMultibinder.addBinding().to(UserMailboxesRoutes.class);
+        routesMultibinder.addBinding().to(MailboxesRoutes.class);
 
         Multibinder<JsonTransformerModule> jsonTransformerModuleMultibinder = Multibinder.newSetBinder(binder(), JsonTransformerModule.class);
         jsonTransformerModuleMultibinder.addBinding().to(QuotaModule.class);
 
         Multibinder.newSetBinder(binder(), TaskRegistration.class, Names.named(USER_MAILBOXES_OPERATIONS_INJECTION_KEY));
+        Multibinder.newSetBinder(binder(), TaskRegistration.class, Names.named(ALL_MAILBOXES_TASKS));
+        Multibinder.newSetBinder(binder(), TaskRegistration.class, Names.named(ONE_MAILBOX_TASKS));
+        Multibinder.newSetBinder(binder(), TaskRegistration.class, Names.named(ONE_MAIL_TASKS));
     }
 }
diff --git a/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/ReIndexingModule.java b/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/ReIndexingModule.java
index 8825cf6..c6de10e 100644
--- a/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/ReIndexingModule.java
+++ b/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/ReIndexingModule.java
@@ -19,8 +19,12 @@
 
 package org.apache.james.modules.server;
 
+import static org.apache.james.webadmin.routes.MailboxesRoutes.ALL_MAILBOXES_TASKS;
+import static org.apache.james.webadmin.routes.MailboxesRoutes.ONE_MAILBOX_TASKS;
+import static org.apache.james.webadmin.routes.MailboxesRoutes.ONE_MAIL_TASKS;
 import static org.apache.james.webadmin.routes.UserMailboxesRoutes.USER_MAILBOXES_OPERATIONS_INJECTION_KEY;
 
+import org.apache.james.webadmin.routes.MailboxesRoutes;
 import org.apache.james.webadmin.routes.UserMailboxesRoutes.UserReIndexingTaskRegistration;
 import org.apache.james.webadmin.tasks.TaskFromRequestRegistry.TaskRegistration;
 
@@ -34,5 +38,17 @@ public class ReIndexingModule extends AbstractModule {
         Multibinder.newSetBinder(binder(), TaskRegistration.class, Names.named(USER_MAILBOXES_OPERATIONS_INJECTION_KEY))
             .addBinding()
             .to(UserReIndexingTaskRegistration.class);
+
+        Multibinder.newSetBinder(binder(), TaskRegistration.class, Names.named(ALL_MAILBOXES_TASKS))
+            .addBinding()
+            .to(MailboxesRoutes.ReIndexAllMailboxesTaskRegistration.class);
+
+        Multibinder.newSetBinder(binder(), TaskRegistration.class, Names.named(ONE_MAILBOX_TASKS))
+            .addBinding()
+            .to(MailboxesRoutes.ReIndexOneMailboxTaskRegistration.class);
+
+        Multibinder.newSetBinder(binder(), TaskRegistration.class, Names.named(ONE_MAIL_TASKS))
+            .addBinding()
+            .to(MailboxesRoutes.ReIndexOneMailTaskRegistration.class);
     }
 }
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java
index 05228e1..65c1423 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java
@@ -165,7 +165,6 @@ public class RabbitMQWebAdminServerIntegrationTest extends WebAdminServerIntegra
             .then()
             .statusCode(HttpStatus.OK_200)
             .body(containsString("\"tags\":[\"Cassandra Mappings Operations\"]"))
-            .body(containsString("{\"name\":\"ReIndexing (mailboxes)\"}"))
             .body(containsString("{\"name\":\"MessageIdReIndexing\"}"));
     }
 }
\ No newline at end of file
diff --git a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java
index 7ab11e8..4e83ebe 100644
--- a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java
@@ -298,7 +298,8 @@ public abstract class WebAdminServerIntegrationTest {
             .body(containsString("\"tags\":[\"MailQueues\"]"))
             .body(containsString("\"tags\":[\"Address Forwards\"]"))
             .body(containsString("\"tags\":[\"Address Aliases\"]"))
-            .body(containsString("\"tags\":[\"Address Groups\"]"));
+            .body(containsString("\"tags\":[\"Address Groups\"]"))
+            .body(containsString("{\"name\":\"Mailboxes\"}"));
     }
 
     @Test
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/MailboxesRoutes.java b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/MailboxesRoutes.java
index f48f090..a26450e 100644
--- a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/MailboxesRoutes.java
+++ b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/MailboxesRoutes.java
@@ -19,6 +19,7 @@
 
 package org.apache.james.webadmin.routes;
 
+import java.util.Optional;
 import java.util.Set;
 
 import javax.inject.Inject;
@@ -60,204 +61,185 @@ import spark.Request;
 import spark.Route;
 import spark.Service;
 
-@Api(tags = "ReIndexing (mailboxes)")
+@Api(tags = "Mailboxes")
 @Path("/mailboxes")
 @Produces("application/json")
 public class MailboxesRoutes implements Routes {
-    private static final String BASE_PATH = "/mailboxes";
-    private static final String RE_INDEX_FAILED_MESSAGES_QUERY_PARAM = "reIndexFailedMessagesOf";
-    private static final String MAILBOX_PARAM = ":mailbox";
-    private static final String UID_PARAM = ":uid";
-    private static final String MAILBOX_PATH = BASE_PATH + "/" + MAILBOX_PARAM;
-    private static final String MESSAGE_PATH = MAILBOX_PATH + "/mails/" + UID_PARAM;
-    static final TaskRegistrationKey RE_INDEX = TaskRegistrationKey.of("reIndex");
-    static final String TASK_PARAMETER = "task";
-    public static final String ALL_MAILBOXES_TASKS = "allMailboxesTasks";
-
-    private final TaskManager taskManager;
-    private final PreviousReIndexingService previousReIndexingService;
-    private final MailboxId.Factory mailboxIdFactory;
-    private final ReIndexer reIndexer;
-    private final JsonTransformer jsonTransformer;
-    private final Set<TaskRegistration> allMailboxesTaskRegistration;
-
-    @Inject
-    MailboxesRoutes(TaskManager taskManager, PreviousReIndexingService previousReIndexingService,
-                    MailboxId.Factory mailboxIdFactory, ReIndexer reIndexer, JsonTransformer jsonTransformer,
-                    @Named(ALL_MAILBOXES_TASKS) Set<TaskRegistration> allMailboxesTaskRegistration) {
-        this.taskManager = taskManager;
-        this.previousReIndexingService = previousReIndexingService;
-        this.mailboxIdFactory = mailboxIdFactory;
-        this.reIndexer = reIndexer;
-        this.jsonTransformer = jsonTransformer;
-        this.allMailboxesTaskRegistration = allMailboxesTaskRegistration;
-    }
+    public static class ReIndexAllMailboxesTaskRegistration extends TaskRegistration {
+        @Inject
+        public ReIndexAllMailboxesTaskRegistration(ReIndexer reIndexer, PreviousReIndexingService previousReIndexingService, MailboxId.Factory mailboxIdFactory) {
+            super(RE_INDEX, wrap(request -> reIndexAll(previousReIndexingService, reIndexer, request)));
+        }
 
-    @Override
-    public String getBasePath() {
-        return BASE_PATH;
-    }
+        @POST
+        @Path("/")
+        @ApiOperation(value = "Re-indexes all the mails on this server")
+        @ApiImplicitParams({
+            @ApiImplicitParam(
+                required = true,
+                name = "task",
+                paramType = "query parameter",
+                dataType = "String",
+                defaultValue = "none",
+                example = "?task=reIndex",
+                value = "Compulsory. Only supported value is `reIndex`"),
+            @ApiImplicitParam(
+                name = "reIndexFailedMessagesOf",
+                paramType = "query parameter",
+                dataType = "String",
+                defaultValue = "none",
+                example = "?reIndexFailedMessagesOf=3294a976-ce63-491e-bd52-1b6f465ed7a2",
+                value = "optional. References a previously run reIndexing task. if present, the messages that this previous " +
+                    "task failed to index will be reIndexed.")
+        })
+        @ApiResponses(value = {
+            @ApiResponse(code = HttpStatus.CREATED_201, message = "Task is created", response = TaskIdDto.class),
+            @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Internal server error - Something went bad on the server side."),
+            @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Bad request - details in the returned error message")
+        })
+        private static Task reIndexAll(PreviousReIndexingService previousReIndexingService, ReIndexer reIndexer, Request request) throws MailboxException {
+            boolean indexingCorrection = !Strings.isNullOrEmpty(request.queryParams(RE_INDEX_FAILED_MESSAGES_QUERY_PARAM));
+            if (indexingCorrection) {
+                IndexingDetailInformation indexingDetailInformation = retrieveIndexingExecutionDetails(previousReIndexingService, request);
+                return reIndexer.reIndex(indexingDetailInformation.failures());
+            }
+            return reIndexer.reIndex();
+        }
 
-    @Override
-    public void define(Service service) {
-        service.post(BASE_PATH, reIndexAll(), jsonTransformer);
-        service.post(MAILBOX_PATH, reIndexMailbox(), jsonTransformer);
-        service.post(MESSAGE_PATH, reIndexMessage(), jsonTransformer);
-    }
+        private static IndexingDetailInformation retrieveIndexingExecutionDetails(PreviousReIndexingService previousReIndexingService, Request request) {
+            TaskId taskId = getTaskId(request);
+            try {
+                return previousReIndexingService.retrieveIndexingExecutionDetails(taskId);
+            } catch (PreviousReIndexingService.NotAnIndexingRetriableTask | PreviousReIndexingService.TaskNotYetFinishedException e) {
+                throw ErrorResponder.builder()
+                    .statusCode(HttpStatus.BAD_REQUEST_400)
+                    .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                    .message("Invalid task id")
+                    .cause(e)
+                    .haltError();
+            } catch (TaskNotFoundException e) {
+                throw ErrorResponder.builder()
+                    .statusCode(HttpStatus.BAD_REQUEST_400)
+                    .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                    .message("TaskId " + taskId.asString() + " does not exist")
+                    .cause(e)
+                    .haltError();
+            }
+        }
 
-    @POST
-    @Path("/")
-    @ApiOperation(value = "Re-indexes all the mails on this server")
-    @ApiImplicitParams({
-        @ApiImplicitParam(
-            required = true,
-            name = "task",
-            paramType = "query parameter",
-            dataType = "String",
-            defaultValue = "none",
-            example = "?task=reIndex",
-            value = "Compulsory. Only supported value is `reIndex`"),
-        @ApiImplicitParam(
-            name = "reIndexFailedMessagesOf",
-            paramType = "query parameter",
-            dataType = "String",
-            defaultValue = "none",
-            example = "?reIndexFailedMessagesOf=3294a976-ce63-491e-bd52-1b6f465ed7a2",
-            value = "optional. References a previously run reIndexing task. if present, the messages that this previous " +
-                "task failed to index will be reIndexed.")
-    })
-    @ApiResponses(value = {
-        @ApiResponse(code = HttpStatus.CREATED_201, message = "Task is created", response = TaskIdDto.class),
-        @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Internal server error - Something went bad on the server side."),
-        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Bad request - details in the returned error message")
-    })
-    private Route reIndexAll() {
-        return TaskFromRequestRegistry.builder()
-            .parameterName(TASK_PARAMETER)
-            .registrations(allMailboxesTaskRegistration)
-            .register(RE_INDEX, wrap(this::reIndexAll))
-            .buildAsRoute(taskManager);
+        private static TaskId getTaskId(Request request) {
+            try {
+                String id = request.queryParams(RE_INDEX_FAILED_MESSAGES_QUERY_PARAM);
+                return TaskId.fromString(id);
+            } catch (Exception e) {
+                throw ErrorResponder.builder()
+                    .statusCode(HttpStatus.BAD_REQUEST_400)
+                    .cause(e)
+                    .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                    .message("Invalid task id")
+                    .haltError();
+            }
+        }
     }
 
-    private Task reIndexAll(Request request) throws MailboxException {
-        boolean indexingCorrection = !Strings.isNullOrEmpty(request.queryParams(RE_INDEX_FAILED_MESSAGES_QUERY_PARAM));
-        if (indexingCorrection) {
-            IndexingDetailInformation indexingDetailInformation = retrieveIndexingExecutionDetails(request);
-            return reIndexer.reIndex(indexingDetailInformation.failures());
+    public static class ReIndexOneMailboxTaskRegistration extends TaskRegistration {
+        @Inject
+        public ReIndexOneMailboxTaskRegistration(ReIndexer reIndexer, MailboxId.Factory mailboxIdFactory) {
+            super(RE_INDEX, toTask(reIndexer, mailboxIdFactory));
         }
-        return reIndexer.reIndex();
-    }
 
-    private IndexingDetailInformation retrieveIndexingExecutionDetails(Request request) {
-        TaskId taskId = getTaskId(request);
-        try {
-            return previousReIndexingService.retrieveIndexingExecutionDetails(taskId);
-        } catch (PreviousReIndexingService.NotAnIndexingRetriableTask | PreviousReIndexingService.TaskNotYetFinishedException e) {
-            throw ErrorResponder.builder()
-                .statusCode(HttpStatus.BAD_REQUEST_400)
-                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
-                .message("Invalid task id")
-                .cause(e)
-                .haltError();
-        } catch (TaskNotFoundException e) {
-            throw ErrorResponder.builder()
-                .statusCode(HttpStatus.BAD_REQUEST_400)
-                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
-                .message("TaskId " + taskId.asString() + " does not exist")
-                .cause(e)
-                .haltError();
+        @POST
+        @Path("/{mailboxId}")
+        @ApiOperation(value = "Re-indexes all the mails in a mailbox")
+        @ApiImplicitParams({
+            @ApiImplicitParam(
+                required = true,
+                name = "task",
+                paramType = "query parameter",
+                dataType = "String",
+                defaultValue = "none",
+                example = "?task=reIndex",
+                value = "Compulsory. Only supported value is `reIndex`"),
+            @ApiImplicitParam(
+                required = true,
+                name = "mailboxId",
+                paramType = "path parameter",
+                dataType = "String",
+                defaultValue = "none",
+                value = "Compulsory. Needs to be a valid mailbox ID")
+        })
+        @ApiResponses(value = {
+            @ApiResponse(code = HttpStatus.CREATED_201, message = "Task is created", response = TaskIdDto.class),
+            @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Internal server error - Something went bad on the server side."),
+            @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Bad request - details in the returned error message")
+        })
+        private static TaskFromRequest toTask(ReIndexer reIndexer, MailboxId.Factory mailboxIdFactory) {
+            return wrap(request -> reIndexer.reIndex(extractMailboxId(mailboxIdFactory, request)));
         }
     }
 
-    private TaskId getTaskId(Request request) {
-        try {
-            String id = request.queryParams(RE_INDEX_FAILED_MESSAGES_QUERY_PARAM);
-            return TaskId.fromString(id);
-        } catch (Exception e) {
-            throw ErrorResponder.builder()
-                .statusCode(HttpStatus.BAD_REQUEST_400)
-                .cause(e)
-                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
-                .message("Invalid task id")
-                .haltError();
+    public static class ReIndexOneMailTaskRegistration extends TaskRegistration {
+        @Inject
+        public ReIndexOneMailTaskRegistration(ReIndexer reIndexer, MailboxId.Factory mailboxIdFactory) {
+            super(RE_INDEX, toTask(reIndexer, mailboxIdFactory));
         }
-    }
 
-    @POST
-    @Path("/{mailboxId}")
-    @ApiOperation(value = "Re-indexes all the mails in a mailbox")
-    @ApiImplicitParams({
-        @ApiImplicitParam(
-            required = true,
-            name = "task",
-            paramType = "query parameter",
-            dataType = "String",
-            defaultValue = "none",
-            example = "?task=reIndex",
-            value = "Compulsory. Only supported value is `reIndex`"),
-        @ApiImplicitParam(
-            required = true,
-            name = "mailboxId",
-            paramType = "path parameter",
-            dataType = "String",
-            defaultValue = "none",
-            value = "Compulsory. Needs to be a valid mailbox ID")
-    })
-    @ApiResponses(value = {
-        @ApiResponse(code = HttpStatus.CREATED_201, message = "Task is created", response = TaskIdDto.class),
-        @ApiResponse(code = HttpStatus.INTERNAL_SERVER_ERROR_500, message = "Internal server error - Something went bad on the server side."),
-        @ApiResponse(code = HttpStatus.BAD_REQUEST_400, message = "Bad request - details in the returned error message")
-    })
-    private Route reIndexMailbox() {
-        return TaskFromRequestRegistry.builder()
-            .parameterName(TASK_PARAMETER)
-            .register(RE_INDEX, wrap(request -> reIndexer.reIndex(extractMailboxId(request))))
-            .buildAsRoute(taskManager);
-    }
+        @POST
+        @Path("/{mailboxId}/mails/{uid}")
+        @ApiOperation(value = "Re-indexes a single email")
+        @ApiImplicitParams({
+            @ApiImplicitParam(
+                required = true,
+                name = "task",
+                paramType = "query parameter",
+                dataType = "String",
+                defaultValue = "none",
+                example = "?task=reIndex",
+                value = "Compulsory. Only supported value is `reIndex`"),
+            @ApiImplicitParam(
+                required = true,
+                name = "user",
+                paramType = "path parameter",
+                dataType = "String",
+                defaultValue = "none",
+                example = "benoit@apache.org",
+                value = "Compulsory. Needs to be a valid username"),
+            @ApiImplicitParam(
+                required = true,
+                name = "mailboxId",
+                paramType = "path parameter",
+                dataType = "String",
+                defaultValue = "none",
+                value = "Compulsory. Needs to be a valid mailbox ID"),
+            @ApiImplicitParam(
+                required = true,
+                name = "uid",
+                paramType = "path parameter",
+                dataType = "Integer",
+                defaultValue = "none",
+                value = "Compulsory. Needs to be a valid UID")
+        })
+        public static TaskFromRequest toTask(ReIndexer reIndexer, MailboxId.Factory mailboxIdFactory) {
+            return wrap(request -> reIndexer.reIndex(
+                extractMailboxId(mailboxIdFactory, request),
+                extractUid(request)));
+        }
 
-    @POST
-    @Path("/{mailboxId}/mails/{uid}")
-    @ApiOperation(value = "Re-indexes a single email")
-    @ApiImplicitParams({
-        @ApiImplicitParam(
-            required = true,
-            name = "task",
-            paramType = "query parameter",
-            dataType = "String",
-            defaultValue = "none",
-            example = "?task=reIndex",
-            value = "Compulsory. Only supported value is `reIndex`"),
-        @ApiImplicitParam(
-            required = true,
-            name = "user",
-            paramType = "path parameter",
-            dataType = "String",
-            defaultValue = "none",
-            example = "benoit@apache.org",
-            value = "Compulsory. Needs to be a valid username"),
-        @ApiImplicitParam(
-            required = true,
-            name = "mailboxId",
-            paramType = "path parameter",
-            dataType = "String",
-            defaultValue = "none",
-            value = "Compulsory. Needs to be a valid mailbox ID"),
-        @ApiImplicitParam(
-            required = true,
-            name = "uid",
-            paramType = "path parameter",
-            dataType = "Integer",
-            defaultValue = "none",
-            value = "Compulsory. Needs to be a valid UID")
-    })
-    private Route reIndexMessage() {
-        return TaskFromRequestRegistry.builder()
-            .parameterName(TASK_PARAMETER)
-            .register(RE_INDEX, wrap(request -> reIndexer.reIndex(extractMailboxId(request), extractUid(request))))
-            .buildAsRoute(taskManager);
+        private static MessageUid extractUid(Request request) {
+            try {
+                return MessageUid.of(Long.parseLong(request.params(UID_PARAM)));
+            } catch (NumberFormatException e) {
+                throw ErrorResponder.builder()
+                    .statusCode(HttpStatus.BAD_REQUEST_400)
+                    .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
+                    .message("'uid' needs to be a parsable long")
+                    .cause(e)
+                    .haltError();
+            }
+        }
     }
 
-    private TaskFromRequest wrap(TaskFromRequest toBeWrapped) {
+    private static TaskFromRequest wrap(TaskFromRequest toBeWrapped) {
         return request -> {
             try {
                 return toBeWrapped.fromRequest(request);
@@ -272,7 +254,7 @@ public class MailboxesRoutes implements Routes {
         };
     }
 
-    private MailboxId extractMailboxId(Request request) {
+    private static MailboxId extractMailboxId(MailboxId.Factory mailboxIdFactory, Request request) {
         try {
             return mailboxIdFactory.fromString(request.params(MAILBOX_PARAM));
         } catch (Exception e) {
@@ -285,16 +267,72 @@ public class MailboxesRoutes implements Routes {
         }
     }
 
-    private MessageUid extractUid(Request request) {
-        try {
-            return MessageUid.of(Long.parseLong(request.params(UID_PARAM)));
-        } catch (NumberFormatException e) {
-            throw ErrorResponder.builder()
-                .statusCode(HttpStatus.BAD_REQUEST_400)
-                .type(ErrorResponder.ErrorType.INVALID_ARGUMENT)
-                .message("'uid' needs to be a parsable long")
-                .cause(e)
-                .haltError();
-        }
+    private static final String BASE_PATH = "/mailboxes";
+    private static final String RE_INDEX_FAILED_MESSAGES_QUERY_PARAM = "reIndexFailedMessagesOf";
+    private static final String MAILBOX_PARAM = ":mailbox";
+    private static final String UID_PARAM = ":uid";
+    private static final String MAILBOX_PATH = BASE_PATH + "/" + MAILBOX_PARAM;
+    private static final String MESSAGE_PATH = MAILBOX_PATH + "/mails/" + UID_PARAM;
+    static final TaskRegistrationKey RE_INDEX = TaskRegistrationKey.of("reIndex");
+    static final String TASK_PARAMETER = "task";
+    public static final String ALL_MAILBOXES_TASKS = "allMailboxesTasks";
+    public static final String ONE_MAILBOX_TASKS = "oneMailboxTasks";
+    public static final String ONE_MAIL_TASKS = "oneMailTasks";
+
+    private final TaskManager taskManager;
+    private final JsonTransformer jsonTransformer;
+    private final Set<TaskRegistration> allMailboxesTaskRegistration;
+    private final Set<TaskRegistration> oneMailboxTaskRegistration;
+    private final Set<TaskRegistration> oneMailTaskRegistration;
+
+    @Inject
+    MailboxesRoutes(TaskManager taskManager,
+                    JsonTransformer jsonTransformer,
+                    @Named(ALL_MAILBOXES_TASKS) Set<TaskRegistration> allMailboxesTaskRegistration,
+                    @Named(ONE_MAILBOX_TASKS) Set<TaskRegistration> oneMailboxTaskRegistration,
+                    @Named(ONE_MAIL_TASKS) Set<TaskRegistration> oneMailTaskRegistration) {
+        this.taskManager = taskManager;
+        this.jsonTransformer = jsonTransformer;
+        this.allMailboxesTaskRegistration = allMailboxesTaskRegistration;
+        this.oneMailboxTaskRegistration = oneMailboxTaskRegistration;
+        this.oneMailTaskRegistration = oneMailTaskRegistration;
+    }
+
+    @Override
+    public String getBasePath() {
+        return BASE_PATH;
+    }
+
+    @Override
+    public void define(Service service) {
+        allMailboxesOperations()
+            .ifPresent(route -> service.post(BASE_PATH, route, jsonTransformer));
+
+        oneMailboxOperations()
+            .ifPresent(route -> service.post(MAILBOX_PATH, route, jsonTransformer));
+
+        oneMail()
+            .ifPresent(route -> service.post(MESSAGE_PATH, route, jsonTransformer));
+    }
+
+    private Optional<Route> allMailboxesOperations() {
+        return TaskFromRequestRegistry.builder()
+            .parameterName(TASK_PARAMETER)
+            .registrations(allMailboxesTaskRegistration)
+            .buildAsRouteOptional(taskManager);
+    }
+
+    private Optional<Route> oneMailboxOperations() {
+        return TaskFromRequestRegistry.builder()
+            .parameterName(TASK_PARAMETER)
+            .registrations(oneMailboxTaskRegistration)
+            .buildAsRouteOptional(taskManager);
+    }
+
+    private Optional<Route> oneMail() {
+        return TaskFromRequestRegistry.builder()
+            .parameterName(TASK_PARAMETER)
+            .registrations(oneMailTaskRegistration)
+            .buildAsRouteOptional(taskManager);
     }
 }
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesNoTasksTest.java b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesNoTasksTest.java
new file mode 100644
index 0000000..2a3b1d1
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesNoTasksTest.java
@@ -0,0 +1,102 @@
+/****************************************************************
+ * 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.webadmin.routes;
+
+import static io.restassured.RestAssured.when;
+
+import org.apache.james.task.Hostname;
+import org.apache.james.task.MemoryTaskManager;
+import org.apache.james.webadmin.WebAdminServer;
+import org.apache.james.webadmin.WebAdminUtils;
+import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
+import org.apache.james.webadmin.utils.ErrorResponder;
+import org.apache.james.webadmin.utils.JsonTransformer;
+import org.eclipse.jetty.http.HttpStatus;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
+
+import io.restassured.RestAssured;
+
+class MailboxesRoutesNoTasksTest {
+    private static final ImmutableSet<TaskFromRequestRegistry.TaskRegistration> NO_ADDITIONAL_REGISTRATION = ImmutableSet.of();
+
+    private WebAdminServer webAdminServer;
+    private MemoryTaskManager taskManager;
+
+    @BeforeEach
+    void beforeEach() {
+        taskManager = new MemoryTaskManager(new Hostname("foo"));
+        JsonTransformer jsonTransformer = new JsonTransformer();
+
+        webAdminServer = WebAdminUtils.createWebAdminServer(
+                new TasksRoutes(taskManager, jsonTransformer),
+                new MailboxesRoutes(taskManager,
+                    jsonTransformer,
+                    NO_ADDITIONAL_REGISTRATION,
+                    NO_ADDITIONAL_REGISTRATION,
+                    NO_ADDITIONAL_REGISTRATION))
+            .start();
+
+        RestAssured.requestSpecification = WebAdminUtils.buildRequestSpecification(webAdminServer).build();
+        RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
+    }
+
+    @AfterEach
+    void tearDown() {
+        webAdminServer.destroy();
+        taskManager.stop();
+    }
+
+    @Test
+    void allMailboxesEndpointShouldNotBeExposedWhenNoTasks() {
+        when()
+            .post("/mailboxes")
+        .then()
+            .statusCode(HttpStatus.NOT_FOUND_404)
+            .body("statusCode", Matchers.is(404))
+            .body("type", Matchers.is(ErrorResponder.ErrorType.NOT_FOUND.getType()))
+            .body("message", Matchers.is("POST /mailboxes can not be found"));
+    }
+
+    @Test
+    void oneMailboxEndpointShouldNotBeExposedWhenNoTasks() {
+        when()
+            .post("/mailboxes/36")
+        .then()
+            .statusCode(HttpStatus.NOT_FOUND_404)
+            .body("statusCode", Matchers.is(404))
+            .body("type", Matchers.is(ErrorResponder.ErrorType.NOT_FOUND.getType()))
+            .body("message", Matchers.is("POST /mailboxes/36 can not be found"));
+    }
+
+    @Test
+    void oneMailEndpointShouldNotBeExposedWhenNoTasks() {
+        when()
+            .post("/mailboxes/36/mails/7")
+        .then()
+            .statusCode(HttpStatus.NOT_FOUND_404)
+            .body("statusCode", Matchers.is(404))
+            .body("type", Matchers.is(ErrorResponder.ErrorType.NOT_FOUND.getType()))
+            .body("message", Matchers.is("POST /mailboxes/36/mails/7 can not be found"));
+    }
+}
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesTest.java b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesTest.java
index 552356a..50da3b0 100644
--- a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesTest.java
@@ -51,7 +51,6 @@ import org.apache.james.task.MemoryTaskManager;
 import org.apache.james.webadmin.WebAdminServer;
 import org.apache.james.webadmin.WebAdminUtils;
 import org.apache.james.webadmin.service.PreviousReIndexingService;
-import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
 import org.apache.james.webadmin.utils.ErrorResponder;
 import org.apache.james.webadmin.utils.JsonTransformer;
 import org.apache.mailbox.tools.indexer.FullReindexingTask;
@@ -72,7 +71,6 @@ import io.restassured.RestAssured;
 class MailboxesRoutesTest {
     private static final Username USERNAME = Username.of("benwa@apache.org");
     private static final MailboxPath INBOX = MailboxPath.inbox(USERNAME);
-    private static final ImmutableSet<TaskFromRequestRegistry.TaskRegistration> NO_ADDITIONAL_REGISTRATION = ImmutableSet.of();
 
     private WebAdminServer webAdminServer;
     private ListeningMessageSearchIndex searchIndex;
@@ -97,13 +95,17 @@ class MailboxesRoutesTest {
 
         webAdminServer = WebAdminUtils.createWebAdminServer(
                 new TasksRoutes(taskManager, jsonTransformer),
-                new MailboxesRoutes(
-                    taskManager,
-                    new PreviousReIndexingService(taskManager),
-                    mailboxIdFactory,
-                    reIndexer,
+                new MailboxesRoutes(taskManager,
                     jsonTransformer,
-                    NO_ADDITIONAL_REGISTRATION))
+                    ImmutableSet.of(
+                        new MailboxesRoutes.ReIndexAllMailboxesTaskRegistration(
+                            reIndexer, new PreviousReIndexingService(taskManager), mailboxIdFactory)),
+                    ImmutableSet.of(
+                        new MailboxesRoutes.ReIndexOneMailboxTaskRegistration(
+                            reIndexer, mailboxIdFactory)),
+                    ImmutableSet.of(
+                        new MailboxesRoutes.ReIndexOneMailTaskRegistration(
+                            reIndexer, mailboxIdFactory))))
             .start();
 
         RestAssured.requestSpecification = WebAdminUtils.buildRequestSpecification(webAdminServer).build();


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


[james-project] 05/08: JAMES-2993 Guice bindings for JMAP preview re-computing

Posted by bt...@apache.org.
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 c8ab2ff90046aa4bccee469d08fde59d1c66fc13
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Dec 16 14:06:03 2019 +0700

    JAMES-2993 Guice bindings for JMAP preview re-computing
---
 pom.xml                                            |  5 +++
 server/container/guice/cassandra-guice/pom.xml     |  4 ++
 .../org/apache/james/CassandraJamesServerMain.java | 10 +++--
 .../james/modules/TaskSerializationModule.java     | 13 ++++++
 server/container/guice/memory-guice/pom.xml        |  4 ++
 .../org/apache/james/MemoryJamesServerMain.java    |  8 ++--
 server/container/guice/pom.xml                     |  6 +++
 .../guice/protocols/webadmin-jmap/pom.xml          | 52 ++++++++++++++++++++++
 .../james/modules/server/JmapTasksModule.java}     | 10 +++--
 .../modules/server/MailboxesRoutesModule.java      |  4 ++
 ...uteAllFastViewProjectionItemsRequestToTask.java |  7 ++-
 ...> RecomputeAllFastViewProjectionItemsTask.java} | 13 +++---
 ...uteAllPreviewsTaskAdditionalInformationDTO.java |  8 ++--
 ...llFastViewProjectionItemsRequestToTaskTest.java |  8 ++--
 ...tionItemsTaskAdditionalInformationDTOTest.java} |  6 +--
 ...tViewProjectionItemsTaskSerializationTest.java} |  8 ++--
 .../james/webadmin/routes/MailboxesRoutes.java     | 12 ++++-
 .../james/webadmin/routes/MailboxesRoutesTest.java |  6 ++-
 18 files changed, 145 insertions(+), 39 deletions(-)

diff --git a/pom.xml b/pom.xml
index 1245413..bd6137e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1823,6 +1823,11 @@
             </dependency>
             <dependency>
                 <groupId>${james.groupId}</groupId>
+                <artifactId>james-server-webadmin-jmap</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${james.groupId}</groupId>
                 <artifactId>james-server-webadmin-mailbox</artifactId>
                 <version>${project.version}</version>
             </dependency>
diff --git a/server/container/guice/cassandra-guice/pom.xml b/server/container/guice/cassandra-guice/pom.xml
index 347c693..01219bb 100644
--- a/server/container/guice/cassandra-guice/pom.xml
+++ b/server/container/guice/cassandra-guice/pom.xml
@@ -232,6 +232,10 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-guice-webadmin-jmap</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-guice-webadmin-mailbox</artifactId>
         </dependency>
         <dependency>
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
index 67bf8b5..f62e1bd 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/CassandraJamesServerMain.java
@@ -59,6 +59,7 @@ import org.apache.james.modules.server.DLPRoutesModule;
 import org.apache.james.modules.server.DataRoutesModules;
 import org.apache.james.modules.server.ElasticSearchMetricReporterModule;
 import org.apache.james.modules.server.JMXServerModule;
+import org.apache.james.modules.server.JmapTasksModule;
 import org.apache.james.modules.server.MailQueueRoutesModule;
 import org.apache.james.modules.server.MailRepositoriesRoutesModule;
 import org.apache.james.modules.server.MailboxRoutesModule;
@@ -86,15 +87,16 @@ public class CassandraJamesServerMain {
         new CassandraDataRoutesModules(),
         new DataRoutesModules(),
         new DeletedMessageVaultRoutesModule(),
+        new DLPRoutesModule(),
+        new JmapTasksModule(),
+        new MailboxesRoutesModule(),
         new MailboxRoutesModule(),
         new MailQueueRoutesModule(),
         new MailRepositoriesRoutesModule(),
-        new SwaggerRoutesModule(),
-        new WebAdminServerModule(),
-        new DLPRoutesModule(),
         new ReIndexingModule(),
         new SieveRoutesModule(),
-        new MailboxesRoutesModule(),
+        new SwaggerRoutesModule(),
+        new WebAdminServerModule(),
         new MessagesRoutesModule());
 
     public static final Module PROTOCOLS = Modules.combine(
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/TaskSerializationModule.java b/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/TaskSerializationModule.java
index f75f0d8..6bf92c3 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/TaskSerializationModule.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/TaskSerializationModule.java
@@ -52,6 +52,9 @@ import org.apache.james.task.eventsourcing.distributed.TasksSerializationModule;
 import org.apache.james.vault.blob.BlobStoreVaultGarbageCollectionTask;
 import org.apache.james.vault.blob.BlobStoreVaultGarbageCollectionTaskAdditionalInformationDTO;
 import org.apache.james.vault.blob.BlobStoreVaultGarbageCollectionTaskDTO;
+import org.apache.james.webadmin.data.jmap.MessageFastViewProjectionCorrector;
+import org.apache.james.webadmin.data.jmap.RecomputeAllFastViewProjectionItemsTask;
+import org.apache.james.webadmin.data.jmap.RecomputeAllPreviewsTaskAdditionalInformationDTO;
 import org.apache.james.webadmin.service.CassandraMappingsSolveInconsistenciesTask;
 import org.apache.james.webadmin.service.ClearMailQueueTaskAdditionalInformationDTO;
 import org.apache.james.webadmin.service.ClearMailQueueTaskDTO;
@@ -174,6 +177,11 @@ public class TaskSerializationModule extends AbstractModule {
     }
 
     @ProvidesIntoSet
+    public TaskDTOModule<?, ?> recomputeAllJmapPreviewsTask(MessageFastViewProjectionCorrector corrector) {
+        return RecomputeAllFastViewProjectionItemsTask.module(corrector);
+    }
+
+    @ProvidesIntoSet
     public TaskDTOModule<?, ?> clearMailRepositoryTask(ClearMailRepositoryTask.Factory factory) {
         return ClearMailRepositoryTaskDTO.module(factory);
     }
@@ -368,6 +376,11 @@ public class TaskSerializationModule extends AbstractModule {
         return UserReindexingTaskAdditionalInformationDTO.serializationModule(mailboxIdFactory);
     }
 
+    @ProvidesIntoSet
+    public AdditionalInformationDTOModule<?, ?> recomputeAllJmapPreviewsAdditionalInformation() {
+        return RecomputeAllPreviewsTaskAdditionalInformationDTO.SERIALIZATION_MODULE;
+    }
+
     @Named(EVENT_NESTED_TYPES_INJECTION_NAME)
     @Provides
     public Set<DTOModule<?, ?>> eventNestedTypes(Set<AdditionalInformationDTOModule<?, ?>> additionalInformationDTOModules,
diff --git a/server/container/guice/memory-guice/pom.xml b/server/container/guice/memory-guice/pom.xml
index 84d5c99..34e4b4a 100644
--- a/server/container/guice/memory-guice/pom.xml
+++ b/server/container/guice/memory-guice/pom.xml
@@ -148,6 +148,10 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-guice-webadmin-jmap</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>james-server-guice-webadmin-mailbox</artifactId>
         </dependency>
         <dependency>
diff --git a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
index 87c8d4c..489b055 100644
--- a/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
+++ b/server/container/guice/memory-guice/src/main/java/org/apache/james/MemoryJamesServerMain.java
@@ -42,6 +42,7 @@ import org.apache.james.modules.server.DKIMMailetModule;
 import org.apache.james.modules.server.DLPRoutesModule;
 import org.apache.james.modules.server.DataRoutesModules;
 import org.apache.james.modules.server.JMXServerModule;
+import org.apache.james.modules.server.JmapTasksModule;
 import org.apache.james.modules.server.MailQueueRoutesModule;
 import org.apache.james.modules.server.MailRepositoriesRoutesModule;
 import org.apache.james.modules.server.MailboxRoutesModule;
@@ -68,12 +69,13 @@ public class MemoryJamesServerMain {
         new WebAdminServerModule(),
         new DataRoutesModules(),
         new DeletedMessageVaultRoutesModule(),
+        new DLPRoutesModule(),
+        new JmapTasksModule(),
         new MailboxRoutesModule(),
         new MailQueueRoutesModule(),
         new MailRepositoriesRoutesModule(),
-        new SwaggerRoutesModule(),
-        new DLPRoutesModule(),
-        new SieveRoutesModule());
+        new SieveRoutesModule(),
+        new SwaggerRoutesModule());
 
 
     public static final JwtConfiguration NO_JWT_CONFIGURATION = new JwtConfiguration(Optional.empty());
diff --git a/server/container/guice/pom.xml b/server/container/guice/pom.xml
index 0ac4ab6..9cefaf5 100644
--- a/server/container/guice/pom.xml
+++ b/server/container/guice/pom.xml
@@ -69,6 +69,7 @@
         <module>protocols/webadmin-cassandra</module>
         <module>protocols/webadmin-cassandra-data</module>
         <module>protocols/webadmin-data</module>
+        <module>protocols/webadmin-jmap</module>
         <module>protocols/webadmin-mailbox</module>
         <module>protocols/webadmin-mailqueue</module>
         <module>protocols/webadmin-mailrepository</module>
@@ -196,6 +197,11 @@
             </dependency>
             <dependency>
                 <groupId>${james.groupId}</groupId>
+                <artifactId>james-server-guice-webadmin-jmap</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>${james.groupId}</groupId>
                 <artifactId>james-server-guice-webadmin-mailbox</artifactId>
                 <version>${project.version}</version>
             </dependency>
diff --git a/server/container/guice/protocols/webadmin-jmap/pom.xml b/server/container/guice/protocols/webadmin-jmap/pom.xml
new file mode 100644
index 0000000..39cc21a
--- /dev/null
+++ b/server/container/guice/protocols/webadmin-jmap/pom.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.james</groupId>
+        <artifactId>james-server-guice</artifactId>
+        <version>3.5.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>james-server-guice-webadmin-jmap</artifactId>
+
+    <name>Apache James :: Server :: Guice :: Webadmin :: JMAP</name>
+    <description>Webadmin jmap modules for Guice implementation of James server</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-webadmin-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-webadmin-mailbox</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-webadmin-jmap</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.inject</groupId>
+            <artifactId>guice</artifactId>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxesRoutesModule.java b/server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTasksModule.java
similarity index 75%
copy from server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxesRoutesModule.java
copy to server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTasksModule.java
index 9075f34..9db7450 100644
--- a/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxesRoutesModule.java
+++ b/server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTasksModule.java
@@ -19,16 +19,18 @@
 
 package org.apache.james.modules.server;
 
-import org.apache.james.webadmin.Routes;
+import org.apache.james.webadmin.data.jmap.RecomputeAllFastViewProjectionItemsRequestToTask;
 import org.apache.james.webadmin.routes.MailboxesRoutes;
+import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Names;
 
-public class MailboxesRoutesModule extends AbstractModule {
+public class JmapTasksModule extends AbstractModule {
     @Override
     protected void configure() {
-        Multibinder<Routes> routesMultibinder = Multibinder.newSetBinder(binder(), Routes.class);
-        routesMultibinder.addBinding().to(MailboxesRoutes.class);
+        Multibinder.newSetBinder(binder(), TaskFromRequestRegistry.TaskRegistration.class, Names.named(MailboxesRoutes.ALL_MAILBOXES_TASKS))
+            .addBinding().to(RecomputeAllFastViewProjectionItemsRequestToTask.class);
     }
 }
diff --git a/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxesRoutesModule.java b/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxesRoutesModule.java
index 9075f34..a6d7e86 100644
--- a/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxesRoutesModule.java
+++ b/server/container/guice/protocols/webadmin-mailbox/src/main/java/org/apache/james/modules/server/MailboxesRoutesModule.java
@@ -21,14 +21,18 @@ package org.apache.james.modules.server;
 
 import org.apache.james.webadmin.Routes;
 import org.apache.james.webadmin.routes.MailboxesRoutes;
+import org.apache.james.webadmin.tasks.TaskFromRequestRegistry.TaskRegistration;
 
 import com.google.inject.AbstractModule;
 import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Names;
 
 public class MailboxesRoutesModule extends AbstractModule {
     @Override
     protected void configure() {
         Multibinder<Routes> routesMultibinder = Multibinder.newSetBinder(binder(), Routes.class);
         routesMultibinder.addBinding().to(MailboxesRoutes.class);
+
+        Multibinder.newSetBinder(binder(), TaskRegistration.class, Names.named(MailboxesRoutes.ALL_MAILBOXES_TASKS));
     }
 }
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java
index a5810e1..e8d8a23 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java
@@ -19,11 +19,14 @@
 
 package org.apache.james.webadmin.data.jmap;
 
+import javax.inject.Inject;
+
 import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
 
 public class RecomputeAllFastViewProjectionItemsRequestToTask extends TaskFromRequestRegistry.TaskRegistration {
-    public RecomputeAllFastViewProjectionItemsRequestToTask(MessageFastViewProjectionCorrector corrector) {
+    @Inject
+    RecomputeAllFastViewProjectionItemsRequestToTask(MessageFastViewProjectionCorrector corrector) {
         super(Constants.TASK_REGISTRATION_KEY,
-            request -> new RecomputeAllPreviewsTask(corrector));
+            request -> new RecomputeAllFastViewProjectionItemsTask(corrector));
     }
 }
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTask.java b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTask.java
similarity index 88%
rename from server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTask.java
rename to server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTask.java
index 99b5b8d..88f4a64 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTask.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTask.java
@@ -21,7 +21,6 @@ package org.apache.james.webadmin.data.jmap;
 
 import java.time.Clock;
 import java.time.Instant;
-import java.util.Objects;
 import java.util.Optional;
 
 import org.apache.james.json.DTOModule;
@@ -35,8 +34,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 
 import reactor.core.scheduler.Schedulers;
 
-public class RecomputeAllPreviewsTask implements Task {
-    static final TaskType TASK_TYPE = TaskType.of("RecomputeAllPreviewsTask");
+public class RecomputeAllFastViewProjectionItemsTask implements Task {
+    static final TaskType TASK_TYPE = TaskType.of("RecomputeAllFastViewProjectionItemsTask");
 
     public static class AdditionalInformation implements TaskExecutionDetails.AdditionalInformation {
         private static AdditionalInformation from(MessageFastViewProjectionCorrector.Progress progress) {
@@ -97,11 +96,11 @@ public class RecomputeAllPreviewsTask implements Task {
         }
     }
 
-    public static TaskDTOModule<RecomputeAllPreviewsTask, RecomputeAllPreviousTaskDTO> module(MessageFastViewProjectionCorrector corrector) {
+    public static TaskDTOModule<RecomputeAllFastViewProjectionItemsTask, RecomputeAllPreviousTaskDTO> module(MessageFastViewProjectionCorrector corrector) {
         return DTOModule
-            .forDomainObject(RecomputeAllPreviewsTask.class)
+            .forDomainObject(RecomputeAllFastViewProjectionItemsTask.class)
             .convertToDTO(RecomputeAllPreviousTaskDTO.class)
-            .toDomainObjectConverter(dto -> new RecomputeAllPreviewsTask(corrector))
+            .toDomainObjectConverter(dto -> new RecomputeAllFastViewProjectionItemsTask(corrector))
             .toDTOConverter((task, type) -> new RecomputeAllPreviousTaskDTO(type))
             .typeName(TASK_TYPE.asString())
             .withFactory(TaskDTOModule::new);
@@ -110,7 +109,7 @@ public class RecomputeAllPreviewsTask implements Task {
     private final MessageFastViewProjectionCorrector corrector;
     private final MessageFastViewProjectionCorrector.Progress progress;
 
-    RecomputeAllPreviewsTask(MessageFastViewProjectionCorrector corrector) {
+    RecomputeAllFastViewProjectionItemsTask(MessageFastViewProjectionCorrector corrector) {
         this.corrector = corrector;
         this.progress = new MessageFastViewProjectionCorrector.Progress();
     }
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java
index aab6aa7..56478e6 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java
@@ -29,10 +29,10 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.annotations.VisibleForTesting;
 
 public class RecomputeAllPreviewsTaskAdditionalInformationDTO implements AdditionalInformationDTO {
-    public static final AdditionalInformationDTOModule<RecomputeAllPreviewsTask.AdditionalInformation, RecomputeAllPreviewsTaskAdditionalInformationDTO> SERIALIZATION_MODULE =
-        DTOModule.forDomainObject(RecomputeAllPreviewsTask.AdditionalInformation.class)
+    public static final AdditionalInformationDTOModule<RecomputeAllFastViewProjectionItemsTask.AdditionalInformation, RecomputeAllPreviewsTaskAdditionalInformationDTO> SERIALIZATION_MODULE =
+        DTOModule.forDomainObject(RecomputeAllFastViewProjectionItemsTask.AdditionalInformation.class)
             .convertToDTO(RecomputeAllPreviewsTaskAdditionalInformationDTO.class)
-            .toDomainObjectConverter(dto -> new RecomputeAllPreviewsTask.AdditionalInformation(
+            .toDomainObjectConverter(dto -> new RecomputeAllFastViewProjectionItemsTask.AdditionalInformation(
                 dto.getProcessedUserCount(),
                 dto.getProcessedMessageCount(),
                 dto.getFailedUserCount(),
@@ -45,7 +45,7 @@ public class RecomputeAllPreviewsTaskAdditionalInformationDTO implements Additio
                 details.getProcessedMessageCount(),
                 details.getFailedUserCount(),
                 details.getFailedMessageCount()))
-            .typeName(RecomputeAllPreviewsTask.TASK_TYPE.asString())
+            .typeName(RecomputeAllFastViewProjectionItemsTask.TASK_TYPE.asString())
             .withFactory(AdditionalInformationDTOModule::new);
 
     private final String type;
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java
index a351095..1013e63 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java
@@ -200,7 +200,7 @@ class RecomputeAllFastViewProjectionItemsRequestToTaskTest {
         .then()
             .body("status", is("completed"))
             .body("taskId", is(taskId))
-            .body("type", is("RecomputeAllPreviewsTask"))
+            .body("type", is("RecomputeAllFastViewProjectionItemsTask"))
             .body("additionalInformation.processedUserCount", is(0))
             .body("additionalInformation.processedMessageCount", is(0))
             .body("additionalInformation.failedUserCount", is(0))
@@ -227,7 +227,7 @@ class RecomputeAllFastViewProjectionItemsRequestToTaskTest {
         .then()
             .body("status", is("completed"))
             .body("taskId", is(taskId))
-            .body("type", is("RecomputeAllPreviewsTask"))
+            .body("type", is("RecomputeAllFastViewProjectionItemsTask"))
             .body("additionalInformation.processedUserCount", is(1))
             .body("additionalInformation.processedMessageCount", is(0))
             .body("additionalInformation.failedUserCount", is(0))
@@ -255,7 +255,7 @@ class RecomputeAllFastViewProjectionItemsRequestToTaskTest {
         .then()
             .body("status", is("completed"))
             .body("taskId", is(taskId))
-            .body("type", is("RecomputeAllPreviewsTask"))
+            .body("type", is("RecomputeAllFastViewProjectionItemsTask"))
             .body("additionalInformation.processedUserCount", is(1))
             .body("additionalInformation.processedMessageCount", is(0))
             .body("additionalInformation.failedUserCount", is(0))
@@ -287,7 +287,7 @@ class RecomputeAllFastViewProjectionItemsRequestToTaskTest {
         .then()
             .body("status", is("completed"))
             .body("taskId", is(taskId))
-            .body("type", is("RecomputeAllPreviewsTask"))
+            .body("type", is("RecomputeAllFastViewProjectionItemsTask"))
             .body("additionalInformation.processedUserCount", is(1))
             .body("additionalInformation.processedMessageCount", is(1))
             .body("additionalInformation.failedUserCount", is(0))
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTOTest.java b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskAdditionalInformationDTOTest.java
similarity index 87%
rename from server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTOTest.java
rename to server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskAdditionalInformationDTOTest.java
index 5fad6d9..d1111bb 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTOTest.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskAdditionalInformationDTOTest.java
@@ -25,11 +25,9 @@ import org.apache.james.JsonSerializationVerifier;
 import org.apache.james.util.ClassLoaderUtils;
 import org.junit.jupiter.api.Test;
 
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-class RecomputeAllPreviewsTaskAdditionalInformationDTOTest {
+class RecomputeAllFastViewProjectionItemsTaskAdditionalInformationDTOTest {
     private static final Instant INSTANT = Instant.parse("2007-12-03T10:15:30.00Z");
-    private static final RecomputeAllPreviewsTask.AdditionalInformation DOMAIN_OBJECT = new RecomputeAllPreviewsTask.AdditionalInformation(1, 2, 3, 4, INSTANT);
+    private static final RecomputeAllFastViewProjectionItemsTask.AdditionalInformation DOMAIN_OBJECT = new RecomputeAllFastViewProjectionItemsTask.AdditionalInformation(1, 2, 3, 4, INSTANT);
 
     @Test
     void shouldMatchJsonSerializationContract() throws Exception {
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskSerializationTest.java
similarity index 88%
rename from server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
rename to server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskSerializationTest.java
index 177a2b6..3d1b949 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskSerializationTest.java
@@ -26,9 +26,7 @@ import org.apache.james.util.ClassLoaderUtils;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
-import nl.jqno.equalsverifier.EqualsVerifier;
-
-class RecomputeAllPreviewsTaskSerializationTest {
+class RecomputeAllFastViewProjectionItemsTaskSerializationTest {
     MessageFastViewProjectionCorrector corrector;
 
     @BeforeEach
@@ -38,8 +36,8 @@ class RecomputeAllPreviewsTaskSerializationTest {
 
     @Test
     void shouldMatchJsonSerializationContract() throws Exception {
-        JsonSerializationVerifier.dtoModule(RecomputeAllPreviewsTask.module(corrector))
-            .bean(new RecomputeAllPreviewsTask(corrector))
+        JsonSerializationVerifier.dtoModule(RecomputeAllFastViewProjectionItemsTask.module(corrector))
+            .bean(new RecomputeAllFastViewProjectionItemsTask(corrector))
             .json(ClassLoaderUtils.getSystemResourceAsString("json/recomputeAll.task.json"))
             .verify();
     }
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/MailboxesRoutes.java b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/MailboxesRoutes.java
index 5d644fd..f48f090 100644
--- a/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/MailboxesRoutes.java
+++ b/server/protocols/webadmin/webadmin-mailbox/src/main/java/org/apache/james/webadmin/routes/MailboxesRoutes.java
@@ -19,7 +19,10 @@
 
 package org.apache.james.webadmin.routes;
 
+import java.util.Set;
+
 import javax.inject.Inject;
+import javax.inject.Named;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
@@ -38,6 +41,7 @@ import org.apache.james.webadmin.Routes;
 import org.apache.james.webadmin.service.PreviousReIndexingService;
 import org.apache.james.webadmin.tasks.TaskFromRequest;
 import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
+import org.apache.james.webadmin.tasks.TaskFromRequestRegistry.TaskRegistration;
 import org.apache.james.webadmin.tasks.TaskIdDto;
 import org.apache.james.webadmin.tasks.TaskRegistrationKey;
 import org.apache.james.webadmin.utils.ErrorResponder;
@@ -68,20 +72,25 @@ public class MailboxesRoutes implements Routes {
     private static final String MESSAGE_PATH = MAILBOX_PATH + "/mails/" + UID_PARAM;
     static final TaskRegistrationKey RE_INDEX = TaskRegistrationKey.of("reIndex");
     static final String TASK_PARAMETER = "task";
+    public static final String ALL_MAILBOXES_TASKS = "allMailboxesTasks";
 
     private final TaskManager taskManager;
     private final PreviousReIndexingService previousReIndexingService;
     private final MailboxId.Factory mailboxIdFactory;
     private final ReIndexer reIndexer;
     private final JsonTransformer jsonTransformer;
+    private final Set<TaskRegistration> allMailboxesTaskRegistration;
 
     @Inject
-    MailboxesRoutes(TaskManager taskManager, PreviousReIndexingService previousReIndexingService, MailboxId.Factory mailboxIdFactory, ReIndexer reIndexer, JsonTransformer jsonTransformer) {
+    MailboxesRoutes(TaskManager taskManager, PreviousReIndexingService previousReIndexingService,
+                    MailboxId.Factory mailboxIdFactory, ReIndexer reIndexer, JsonTransformer jsonTransformer,
+                    @Named(ALL_MAILBOXES_TASKS) Set<TaskRegistration> allMailboxesTaskRegistration) {
         this.taskManager = taskManager;
         this.previousReIndexingService = previousReIndexingService;
         this.mailboxIdFactory = mailboxIdFactory;
         this.reIndexer = reIndexer;
         this.jsonTransformer = jsonTransformer;
+        this.allMailboxesTaskRegistration = allMailboxesTaskRegistration;
     }
 
     @Override
@@ -125,6 +134,7 @@ public class MailboxesRoutes implements Routes {
     private Route reIndexAll() {
         return TaskFromRequestRegistry.builder()
             .parameterName(TASK_PARAMETER)
+            .registrations(allMailboxesTaskRegistration)
             .register(RE_INDEX, wrap(this::reIndexAll))
             .buildAsRoute(taskManager);
     }
diff --git a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesTest.java b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesTest.java
index 5f0a977..552356a 100644
--- a/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesTest.java
+++ b/server/protocols/webadmin/webadmin-mailbox/src/test/java/org/apache/james/webadmin/routes/MailboxesRoutesTest.java
@@ -51,6 +51,7 @@ import org.apache.james.task.MemoryTaskManager;
 import org.apache.james.webadmin.WebAdminServer;
 import org.apache.james.webadmin.WebAdminUtils;
 import org.apache.james.webadmin.service.PreviousReIndexingService;
+import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
 import org.apache.james.webadmin.utils.ErrorResponder;
 import org.apache.james.webadmin.utils.JsonTransformer;
 import org.apache.mailbox.tools.indexer.FullReindexingTask;
@@ -64,12 +65,14 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentCaptor;
+import org.testcontainers.shaded.com.google.common.collect.ImmutableSet;
 
 import io.restassured.RestAssured;
 
 class MailboxesRoutesTest {
     private static final Username USERNAME = Username.of("benwa@apache.org");
     private static final MailboxPath INBOX = MailboxPath.inbox(USERNAME);
+    private static final ImmutableSet<TaskFromRequestRegistry.TaskRegistration> NO_ADDITIONAL_REGISTRATION = ImmutableSet.of();
 
     private WebAdminServer webAdminServer;
     private ListeningMessageSearchIndex searchIndex;
@@ -99,7 +102,8 @@ class MailboxesRoutesTest {
                     new PreviousReIndexingService(taskManager),
                     mailboxIdFactory,
                     reIndexer,
-                    jsonTransformer))
+                    jsonTransformer,
+                    NO_ADDITIONAL_REGISTRATION))
             .start();
 
         RestAssured.requestSpecification = WebAdminUtils.buildRequestSpecification(webAdminServer).build();


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


[james-project] 01/08: JAMES-2993 Document webadmin endpoint for message fast view recomputation

Posted by bt...@apache.org.
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 a557d0a48a3bd975488c9771e9d3f30c32b018b0
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Dec 16 14:54:12 2019 +0700

    JAMES-2993 Document webadmin endpoint for message fast view recomputation
---
 src/site/markdown/server/manage-webadmin.md | 37 +++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/src/site/markdown/server/manage-webadmin.md b/src/site/markdown/server/manage-webadmin.md
index c3b3786..74a22f7 100644
--- a/src/site/markdown/server/manage-webadmin.md
+++ b/src/site/markdown/server/manage-webadmin.md
@@ -358,6 +358,43 @@ Response codes:
 
 The kind of task scheduled depends on the action parameter. See below for details.
 
+### Recomputing JMAP fast message view projection
+
+This action is only available for backends supporting JMAP protocol.
+
+Message fast view projection stores message properties expected to be fast to fetch but are actually expensive to compute,
+in order for GetMessages operation to be fast to execute for these properties.
+
+These projection items are asynchronously computed via a dedicated listener.
+
+You can force the full projection recomputation by calling the following endpoint:
+
+```
+curl -XPOST /mailboxes?task=recomputeFastViewProjectionItems
+```
+
+Will schedule a task for recomputing the fast message view projection for all mailboxes.
+
+[More details about endpoints returning a task](#Endpoints_returning_a_task).
+
+
+The scheduled task will have the following type `RecomputeAllPreviewsTask` and the following `additionalInformation`:
+
+```
+{
+  "type":"RecomputeAllPreviewsTask",
+  "processedUserCount": 3,
+  "processedMessageCount": 3,
+  "failedUserCount": 2,
+  "failedMessageCount": 1
+}
+```
+
+Response codes:
+
+ - 201: Success. Corresponding task id is returned.
+ - 400: Error in the request. Details can be found in the reported error.
+
 #### ReIndexing action
 
 Be also aware of the limits of this API:


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


[james-project] 08/08: JAMES-2993 Create maven module for webadmin-data-jmap

Posted by bt...@apache.org.
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 40670841ab193d9b5b9613db8ae142754e1f04bf
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Dec 18 18:03:20 2019 +0700

    JAMES-2993 Create maven module for webadmin-data-jmap
---
 server/protocols/webadmin/pom.xml                                     | 2 +-
 .../protocols/webadmin/{webadmin-data-jmap => webadmin-jmap}/pom.xml  | 4 ++--
 .../src/main/java/org/apache/james/webadmin/data/jmap/Constants.java  | 0
 .../james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java  | 0
 .../data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java   | 0
 .../webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTask.java   | 0
 .../data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java   | 0
 .../jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java    | 0
 ...uteAllFastViewProjectionItemsTaskAdditionalInformationDTOTest.java | 0
 .../RecomputeAllFastViewProjectionItemsTaskSerializationTest.java     | 0
 .../src/test/resources/json/recomputeAll.additionalInformation.json   | 0
 .../src/test/resources/json/recomputeAll.task.json                    | 0
 12 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/server/protocols/webadmin/pom.xml b/server/protocols/webadmin/pom.xml
index 82edadd..38dca23 100644
--- a/server/protocols/webadmin/pom.xml
+++ b/server/protocols/webadmin/pom.xml
@@ -37,7 +37,7 @@
         <module>webadmin-cassandra-data</module>
         <module>webadmin-core</module>
         <module>webadmin-data</module>
-        <module>webadmin-data-jmap</module>
+        <module>webadmin-jmap</module>
         <module>webadmin-mailbox</module>
         <module>webadmin-mailbox-deleted-message-vault</module>
         <module>webadmin-mailqueue</module>
diff --git a/server/protocols/webadmin/webadmin-data-jmap/pom.xml b/server/protocols/webadmin/webadmin-jmap/pom.xml
similarity index 97%
rename from server/protocols/webadmin/webadmin-data-jmap/pom.xml
rename to server/protocols/webadmin/webadmin-jmap/pom.xml
index 7c9330f..5913715 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/pom.xml
+++ b/server/protocols/webadmin/webadmin-jmap/pom.xml
@@ -26,9 +26,9 @@
         <relativePath>../../../pom.xml</relativePath>
     </parent>
 
-    <artifactId>james-server-webadmin-data-jmap</artifactId>
+    <artifactId>james-server-webadmin-jmap</artifactId>
 
-    <name>Apache James :: Server :: Web Admin :: data :: JMAP</name>
+    <name>Apache James :: Server :: Web Admin :: JMAP</name>
 
     <dependencies>
         <dependency>
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java
similarity index 100%
rename from server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java
rename to server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java
similarity index 100%
rename from server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java
rename to server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java
similarity index 100%
rename from server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java
rename to server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTask.java
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTask.java b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTask.java
similarity index 100%
rename from server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTask.java
rename to server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTask.java
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java
similarity index 100%
rename from server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java
rename to server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java
similarity index 100%
rename from server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java
rename to server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsRequestToTaskTest.java
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskAdditionalInformationDTOTest.java b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskAdditionalInformationDTOTest.java
similarity index 100%
rename from server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskAdditionalInformationDTOTest.java
rename to server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskAdditionalInformationDTOTest.java
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskSerializationTest.java b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskSerializationTest.java
similarity index 100%
rename from server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskSerializationTest.java
rename to server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllFastViewProjectionItemsTaskSerializationTest.java
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.additionalInformation.json b/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/recomputeAll.additionalInformation.json
similarity index 100%
rename from server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.additionalInformation.json
rename to server/protocols/webadmin/webadmin-jmap/src/test/resources/json/recomputeAll.additionalInformation.json
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.task.json b/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/recomputeAll.task.json
similarity index 100%
rename from server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.task.json
rename to server/protocols/webadmin/webadmin-jmap/src/test/resources/json/recomputeAll.task.json


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


[james-project] 06/08: JAMES-2993 Simple webadmin integration test for JMAP routes

Posted by bt...@apache.org.
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 cae55820788490cf7d42144bdb1b86b2e9d7b597
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Wed Nov 27 17:01:34 2019 +0700

    JAMES-2993 Simple webadmin integration test for JMAP routes
---
 .../integration/WebAdminServerIntegrationTest.java      | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java
index ee1e92c..7ab11e8 100644
--- a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationTest.java
@@ -44,6 +44,7 @@ import org.apache.james.webadmin.routes.GroupsRoutes;
 import org.apache.james.webadmin.routes.HealthCheckRoutes;
 import org.apache.james.webadmin.routes.MailQueueRoutes;
 import org.apache.james.webadmin.routes.MailRepositoriesRoutes;
+import org.apache.james.webadmin.routes.TasksRoutes;
 import org.apache.james.webadmin.routes.UserMailboxesRoutes;
 import org.apache.james.webadmin.routes.UserRoutes;
 import org.apache.james.webadmin.swagger.routes.SwaggerRoutes;
@@ -307,4 +308,20 @@ public abstract class WebAdminServerIntegrationTest {
         .then()
             .statusCode(HttpStatus.OK_200);
     }
+
+    @Test
+    public void jmapTasksShouldBeExposed() {
+        String taskId = with()
+            .queryParam("task", "recomputeFastViewProjectionItems")
+            .post("/mailboxes")
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"));
+    }
 }
\ 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


[james-project] 03/08: JAMES-2993 Implement recompute all JMAP message preview task

Posted by bt...@apache.org.
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 6f92767ee5277b597a3bfd75fbc25e6e39fe44fa
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Dec 16 14:38:31 2019 +0700

    JAMES-2993 Implement recompute all JMAP message preview task
---
 .../MemoryMessageFastViewProjection.java           |   7 +-
 .../protocols/webadmin/webadmin-data-jmap/pom.xml  |  71 +++++++++
 .../jmap/MessageFastViewProjectionCorrector.java   | 142 +++++++++++++++++
 .../data/jmap/RecomputeAllPreviewsTask.java        | 168 +++++++++++++++++++++
 ...uteAllPreviewsTaskAdditionalInformationDTO.java |  98 ++++++++++++
 ...llPreviewsTaskAdditionalInformationDTOTest.java |  47 ++++++
 .../RecomputeAllPreviewsTaskSerializationTest.java |  53 +++++++
 .../json/recomputeAll.additionalInformation.json   |   8 +
 .../src/test/resources/json/recomputeAll.task.json |   1 +
 9 files changed, 591 insertions(+), 4 deletions(-)

diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/projections/MemoryMessageFastViewProjection.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/projections/MemoryMessageFastViewProjection.java
index 9492ab3..e39fcbe 100644
--- a/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/projections/MemoryMessageFastViewProjection.java
+++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/memory/projections/MemoryMessageFastViewProjection.java
@@ -28,7 +28,6 @@ import org.apache.james.jmap.api.projections.MessageFastViewProjection;
 import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.metrics.api.Metric;
 import org.apache.james.metrics.api.MetricFactory;
-import org.reactivestreams.Publisher;
 
 import com.google.common.base.Preconditions;
 
@@ -48,7 +47,7 @@ public class MemoryMessageFastViewProjection implements MessageFastViewProjectio
     }
 
     @Override
-    public Publisher<Void> store(MessageId messageId, MessageFastViewPrecomputedProperties precomputedProperties) {
+    public Mono<Void> store(MessageId messageId, MessageFastViewPrecomputedProperties precomputedProperties) {
         Preconditions.checkNotNull(messageId);
         Preconditions.checkNotNull(precomputedProperties);
 
@@ -56,7 +55,7 @@ public class MemoryMessageFastViewProjection implements MessageFastViewProjectio
     }
 
     @Override
-    public Publisher<MessageFastViewPrecomputedProperties> retrieve(MessageId messageId) {
+    public Mono<MessageFastViewPrecomputedProperties> retrieve(MessageId messageId) {
         Preconditions.checkNotNull(messageId);
 
         return Mono.fromSupplier(() -> previews.get(messageId))
@@ -65,7 +64,7 @@ public class MemoryMessageFastViewProjection implements MessageFastViewProjectio
     }
 
     @Override
-    public Publisher<Void> delete(MessageId messageId) {
+    public Mono<Void> delete(MessageId messageId) {
         Preconditions.checkNotNull(messageId);
 
         return Mono.fromRunnable(() -> previews.remove(messageId));
diff --git a/server/protocols/webadmin/webadmin-data-jmap/pom.xml b/server/protocols/webadmin/webadmin-data-jmap/pom.xml
index 9ac8a75..e42b17e 100644
--- a/server/protocols/webadmin/webadmin-data-jmap/pom.xml
+++ b/server/protocols/webadmin/webadmin-data-jmap/pom.xml
@@ -33,15 +33,86 @@
     <dependencies>
         <dependency>
             <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-api</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>apache-james-mailbox-memory</artifactId>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-json</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
             <artifactId>james-server-data-jmap</artifactId>
         </dependency>
         <dependency>
             <groupId>${project.groupId}</groupId>
+            <artifactId>james-server-data-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-data-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
             <artifactId>james-server-jmap-draft</artifactId>
         </dependency>
         <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>james-server-task-json</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-task-memory</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>james-server-webadmin-core</artifactId>
+        </dependency>
+        <dependency>
             <groupId>${james.groupId}</groupId>
             <artifactId>james-server-webadmin-core</artifactId>
+            <scope>test</scope>
+            <type>test-jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
+            <artifactId>testing-base</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.rest-assured</groupId>
+            <artifactId>rest-assured</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.javacrumbs.json-unit</groupId>
+            <artifactId>json-unit-assertj</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
         </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java
new file mode 100644
index 0000000..6fc4c86
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/MessageFastViewProjectionCorrector.java
@@ -0,0 +1,142 @@
+/****************************************************************
+ * 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.webadmin.data.jmap;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.inject.Inject;
+
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.james.core.Username;
+import org.apache.james.jmap.api.projections.MessageFastViewPrecomputedProperties;
+import org.apache.james.jmap.api.projections.MessageFastViewProjection;
+import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.exception.MailboxException;
+import org.apache.james.mailbox.model.FetchGroup;
+import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.search.MailboxQuery;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.api.UsersRepositoryException;
+import org.apache.james.util.streams.Iterators;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.github.fge.lambdas.Throwing;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class MessageFastViewProjectionCorrector {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(MessageFastViewProjectionCorrector.class);
+
+    static class Progress {
+        private final AtomicLong processedUserCount;
+        private final AtomicLong processedMessageCount;
+        private final AtomicLong failedUserCount;
+        private final AtomicLong failedMessageCount;
+
+        Progress() {
+            failedUserCount = new AtomicLong();
+            processedMessageCount = new AtomicLong();
+            processedUserCount = new AtomicLong();
+            failedMessageCount = new AtomicLong();
+        }
+
+        long getProcessedUserCount() {
+            return processedUserCount.get();
+        }
+
+        long getProcessedMessageCount() {
+            return processedMessageCount.get();
+        }
+
+        long getFailedUserCount() {
+            return failedUserCount.get();
+        }
+
+        long getFailedMessageCount() {
+            return failedMessageCount.get();
+        }
+
+        boolean failed() {
+            return failedMessageCount.get() > 0 || failedUserCount.get() > 0;
+        }
+    }
+
+    private final UsersRepository usersRepository;
+    private final MailboxManager mailboxManager;
+    private final MessageFastViewProjection messageFastViewProjection;
+    private final MessageFastViewPrecomputedProperties.Factory projectionItemFactory;
+
+    @Inject
+    MessageFastViewProjectionCorrector(UsersRepository usersRepository, MailboxManager mailboxManager,
+                                       MessageFastViewProjection messageFastViewProjection,
+                                       MessageFastViewPrecomputedProperties.Factory projectionItemFactory) {
+        this.usersRepository = usersRepository;
+        this.mailboxManager = mailboxManager;
+        this.messageFastViewProjection = messageFastViewProjection;
+        this.projectionItemFactory = projectionItemFactory;
+    }
+
+    Mono<Void> correctAllProjectionItems(Progress progress) {
+        try {
+            return Iterators.toFlux(usersRepository.list())
+                .concatMap(username -> correctUsersProjectionItems(progress, username))
+                .then();
+        } catch (UsersRepositoryException e) {
+            return Mono.error(e);
+        }
+    }
+
+    private Mono<Void> correctUsersProjectionItems(Progress progress, Username username) {
+        try {
+            MailboxSession session = mailboxManager.createSystemSession(username);
+            return Flux.fromIterable(mailboxManager.search(MailboxQuery.privateMailboxesBuilder(session).build(), session))
+                .concatMap(mailboxMetadata -> Mono.fromCallable(() -> mailboxManager.getMailbox(mailboxMetadata.getId(), session)))
+                .concatMap(Throwing.function(messageManager -> correctMailboxProjectionItems(progress, messageManager, session)))
+                .doOnComplete(progress.processedUserCount::incrementAndGet)
+                .onErrorContinue((error, o) -> {
+                    LOGGER.error("JMAP preview re-computation aborted for {}", username, error);
+                    progress.failedUserCount.incrementAndGet();
+                })
+                .then();
+        } catch (MailboxException e) {
+            LOGGER.error("JMAP preview re-computation aborted for {} as we failed listing user mailboxes", username, e);
+            progress.failedUserCount.incrementAndGet();
+            return Mono.empty();
+        }
+    }
+
+    private Mono<Void> correctMailboxProjectionItems(Progress progress, MessageManager messageManager, MailboxSession session) throws MailboxException {
+        return Iterators.toFlux(messageManager.getMessages(MessageRange.all(), FetchGroup.MINIMAL, session))
+            .concatMap(Throwing.function(messageResult -> Iterators.toFlux(messageManager.getMessages(MessageRange.all(), FetchGroup.BODY_CONTENT, session))))
+            .map(Throwing.function(messageResult -> Pair.of(messageResult.getMessageId(), projectionItemFactory.from(messageResult))))
+            .concatMap(pair -> Mono.from(messageFastViewProjection.store(pair.getKey(), pair.getValue()))
+                .doOnSuccess(any -> progress.processedMessageCount.incrementAndGet()))
+            .onErrorContinue((error, triggeringValue) -> {
+                LOGGER.error("JMAP preview re-computation aborted for {} - {}", session.getUser(), triggeringValue, error);
+                progress.failedMessageCount.incrementAndGet();
+            })
+            .then();
+    }
+}
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTask.java b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTask.java
new file mode 100644
index 0000000..bacf8ad
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTask.java
@@ -0,0 +1,168 @@
+/****************************************************************
+ * 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.webadmin.data.jmap;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.util.Objects;
+import java.util.Optional;
+
+import org.apache.james.json.DTOModule;
+import org.apache.james.server.task.json.dto.TaskDTO;
+import org.apache.james.server.task.json.dto.TaskDTOModule;
+import org.apache.james.task.Task;
+import org.apache.james.task.TaskExecutionDetails;
+import org.apache.james.task.TaskType;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import reactor.core.scheduler.Schedulers;
+
+public class RecomputeAllPreviewsTask implements Task {
+    static final TaskType TASK_TYPE = TaskType.of("RecomputeAllPreviewsTask");
+
+    public static class AdditionalInformation implements TaskExecutionDetails.AdditionalInformation {
+        private static AdditionalInformation from(MessageFastViewProjectionCorrector.Progress progress) {
+            return new AdditionalInformation(
+                progress.getProcessedUserCount(),
+                progress.getProcessedMessageCount(),
+                progress.getFailedUserCount(),
+                progress.getFailedMessageCount(),
+                Clock.systemUTC().instant());
+        }
+
+        private final long processedUserCount;
+        private final long processedMessageCount;
+        private final long failedUserCount;
+        private final long failedMessageCount;
+        private final Instant timestamp;
+
+        public AdditionalInformation(long processedUserCount, long processedMessageCount, long failedUserCount, long failedMessageCount, Instant timestamp) {
+            this.processedUserCount = processedUserCount;
+            this.processedMessageCount = processedMessageCount;
+            this.failedUserCount = failedUserCount;
+            this.failedMessageCount = failedMessageCount;
+            this.timestamp = timestamp;
+        }
+
+        public long getProcessedUserCount() {
+            return processedUserCount;
+        }
+
+        public long getProcessedMessageCount() {
+            return processedMessageCount;
+        }
+
+        public long getFailedUserCount() {
+            return failedUserCount;
+        }
+
+        public long getFailedMessageCount() {
+            return failedMessageCount;
+        }
+
+        @Override
+        public Instant timestamp() {
+            return timestamp;
+        }
+
+        @Override
+        public final boolean equals(Object o) {
+            if (o instanceof AdditionalInformation) {
+                AdditionalInformation that = (AdditionalInformation) o;
+
+                return Objects.equals(this.processedUserCount, that.processedUserCount)
+                    && Objects.equals(this.processedMessageCount, that.processedMessageCount)
+                    && Objects.equals(this.failedUserCount, that.failedUserCount)
+                    && Objects.equals(this.failedMessageCount, that.failedMessageCount)
+                    && Objects.equals(this.timestamp, that.timestamp);
+            }
+            return false;
+        }
+
+        @Override
+        public final int hashCode() {
+            return Objects.hash(processedUserCount, processedMessageCount, failedUserCount, failedMessageCount, timestamp);
+        }
+    }
+
+    public static class RecomputeAllPreviousTaskDTO implements TaskDTO {
+        private final String type;
+
+        public RecomputeAllPreviousTaskDTO(@JsonProperty("type") String type) {
+            this.type = type;
+        }
+
+        @Override
+        public String getType() {
+            return type;
+        }
+    }
+
+    public static TaskDTOModule<RecomputeAllPreviewsTask, RecomputeAllPreviousTaskDTO> module(MessageFastViewProjectionCorrector corrector) {
+        return DTOModule
+            .forDomainObject(RecomputeAllPreviewsTask.class)
+            .convertToDTO(RecomputeAllPreviousTaskDTO.class)
+            .toDomainObjectConverter(dto -> new RecomputeAllPreviewsTask(corrector))
+            .toDTOConverter((task, type) -> new RecomputeAllPreviousTaskDTO(type))
+            .typeName(TASK_TYPE.asString())
+            .withFactory(TaskDTOModule::new);
+    }
+
+    private final MessageFastViewProjectionCorrector corrector;
+    private final MessageFastViewProjectionCorrector.Progress progress;
+
+    RecomputeAllPreviewsTask(MessageFastViewProjectionCorrector corrector) {
+        this.corrector = corrector;
+        this.progress = new MessageFastViewProjectionCorrector.Progress();
+    }
+
+    @Override
+    public Result run() {
+        corrector.correctAllProjectionItems(progress)
+            .subscribeOn(Schedulers.boundedElastic())
+            .block();
+
+        if (progress.failed()) {
+            return Result.PARTIAL;
+        }
+        return Result.COMPLETED;
+    }
+
+    @Override
+    public TaskType type() {
+        return TASK_TYPE;
+    }
+
+    @Override
+    public Optional<TaskExecutionDetails.AdditionalInformation> details() {
+        return Optional.of(AdditionalInformation.from(progress));
+    }
+
+    @Override
+    public final boolean equals(Object o) {
+        return o instanceof RecomputeAllPreviewsTask;
+    }
+
+    @Override
+    public final int hashCode() {
+        return Objects.hash(RecomputeAllPreviewsTask.class);
+    }
+}
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java
new file mode 100644
index 0000000..aab6aa7
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTO.java
@@ -0,0 +1,98 @@
+/****************************************************************
+ * 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.webadmin.data.jmap;
+
+import java.time.Instant;
+
+import org.apache.james.json.DTOModule;
+import org.apache.james.server.task.json.dto.AdditionalInformationDTO;
+import org.apache.james.server.task.json.dto.AdditionalInformationDTOModule;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.annotations.VisibleForTesting;
+
+public class RecomputeAllPreviewsTaskAdditionalInformationDTO implements AdditionalInformationDTO {
+    public static final AdditionalInformationDTOModule<RecomputeAllPreviewsTask.AdditionalInformation, RecomputeAllPreviewsTaskAdditionalInformationDTO> SERIALIZATION_MODULE =
+        DTOModule.forDomainObject(RecomputeAllPreviewsTask.AdditionalInformation.class)
+            .convertToDTO(RecomputeAllPreviewsTaskAdditionalInformationDTO.class)
+            .toDomainObjectConverter(dto -> new RecomputeAllPreviewsTask.AdditionalInformation(
+                dto.getProcessedUserCount(),
+                dto.getProcessedMessageCount(),
+                dto.getFailedUserCount(),
+                dto.getFailedMessageCount(),
+                dto.timestamp))
+            .toDTOConverter((details, type) -> new RecomputeAllPreviewsTaskAdditionalInformationDTO(
+                type,
+                details.timestamp(),
+                details.getProcessedUserCount(),
+                details.getProcessedMessageCount(),
+                details.getFailedUserCount(),
+                details.getFailedMessageCount()))
+            .typeName(RecomputeAllPreviewsTask.TASK_TYPE.asString())
+            .withFactory(AdditionalInformationDTOModule::new);
+
+    private final String type;
+    private final Instant timestamp;
+    private final long processedUserCount;
+    private final long processedMessageCount;
+    private final long failedUserCount;
+    private final long failedMessageCount;
+
+    @VisibleForTesting
+    RecomputeAllPreviewsTaskAdditionalInformationDTO(@JsonProperty("type") String type,
+                                                     @JsonProperty("timestamp") Instant timestamp,
+                                                     @JsonProperty("processedUserCount") long processedUserCount,
+                                                     @JsonProperty("processedMessageCount") long processedMessageCount,
+                                                     @JsonProperty("failedUserCount") long failedUserCount,
+                                                     @JsonProperty("failedMessageCount") long failedMessageCount) {
+        this.type = type;
+        this.timestamp = timestamp;
+        this.processedUserCount = processedUserCount;
+        this.processedMessageCount = processedMessageCount;
+        this.failedUserCount = failedUserCount;
+        this.failedMessageCount = failedMessageCount;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    @Override
+    public Instant getTimestamp() {
+        return timestamp;
+    }
+
+    public long getProcessedUserCount() {
+        return processedUserCount;
+    }
+
+    public long getProcessedMessageCount() {
+        return processedMessageCount;
+    }
+
+    public long getFailedUserCount() {
+        return failedUserCount;
+    }
+
+    public long getFailedMessageCount() {
+        return failedMessageCount;
+    }
+}
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTOTest.java b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTOTest.java
new file mode 100644
index 0000000..125464f
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskAdditionalInformationDTOTest.java
@@ -0,0 +1,47 @@
+/****************************************************************
+ * 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.webadmin.data.jmap;
+
+import java.time.Instant;
+
+import org.apache.james.JsonSerializationVerifier;
+import org.apache.james.util.ClassLoaderUtils;
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class RecomputeAllPreviewsTaskAdditionalInformationDTOTest {
+    private static final Instant INSTANT = Instant.parse("2007-12-03T10:15:30.00Z");
+    private static final RecomputeAllPreviewsTask.AdditionalInformation DOMAIN_OBJECT = new RecomputeAllPreviewsTask.AdditionalInformation(1, 2, 3, 4, INSTANT);
+
+    @Test
+    void shouldMatchJsonSerializationContract() throws Exception {
+        JsonSerializationVerifier.dtoModule(RecomputeAllPreviewsTaskAdditionalInformationDTO.SERIALIZATION_MODULE)
+            .bean(DOMAIN_OBJECT)
+            .json(ClassLoaderUtils.getSystemResourceAsString("json/recomputeAll.additionalInformation.json"))
+            .verify();
+    }
+
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(RecomputeAllPreviewsTask.AdditionalInformation.class)
+            .verify();
+    }
+}
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
new file mode 100644
index 0000000..e56432a
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RecomputeAllPreviewsTaskSerializationTest.java
@@ -0,0 +1,53 @@
+/****************************************************************
+ * 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.webadmin.data.jmap;
+
+import static org.mockito.Mockito.mock;
+
+import org.apache.james.JsonSerializationVerifier;
+import org.apache.james.util.ClassLoaderUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class RecomputeAllPreviewsTaskSerializationTest {
+    MessageFastViewProjectionCorrector corrector;
+
+    @BeforeEach
+    void setUp() {
+        corrector = mock(MessageFastViewProjectionCorrector.class);
+    }
+
+    @Test
+    void shouldMatchJsonSerializationContract() throws Exception {
+        JsonSerializationVerifier.dtoModule(RecomputeAllPreviewsTask.module(corrector))
+            .bean(new RecomputeAllPreviewsTask(corrector))
+            .json(ClassLoaderUtils.getSystemResourceAsString("json/recomputeAll.task.json"))
+            .verify();
+    }
+
+    @Test
+    void shouldMatchBeanContract() {
+        EqualsVerifier.forClass(RecomputeAllPreviewsTask.class)
+            .withIgnoredFields("corrector", "progress")
+            .verify();
+    }
+}
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.additionalInformation.json b/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.additionalInformation.json
new file mode 100644
index 0000000..bc3d81f
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.additionalInformation.json
@@ -0,0 +1,8 @@
+{
+  "type":"RecomputeAllPreviewsTask",
+  "timestamp":"2007-12-03T10:15:30Z",
+  "processedUserCount":1,
+  "processedMessageCount":2,
+  "failedUserCount":3,
+  "failedMessageCount":4
+}
\ No newline at end of file
diff --git a/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.task.json b/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.task.json
new file mode 100644
index 0000000..a447293
--- /dev/null
+++ b/server/protocols/webadmin/webadmin-data-jmap/src/test/resources/json/recomputeAll.task.json
@@ -0,0 +1 @@
+{"type":"RecomputeAllPreviewsTask"}
\ 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