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 d79968462cf17fd096b4eb9302a0d5c042f49e0d
Author: Benoit Tellier <btell...@linagora.com>
AuthorDate: Fri Apr 14 18:32:10 2023 +0700

    JAMES-3777 Create a webadmin exposed task to populate Cassandra filtering 
projection
---
 .../docs/modules/ROOT/pages/operate/webadmin.adoc  |  31 +++
 .../james/modules/data/CassandraJmapModule.java    |   1 +
 .../james/modules/data/MemoryDataJmapModule.java   |   1 +
 .../server/JmapTaskSerializationModule.java        |  22 ++
 .../james/modules/server/JmapTasksModule.java      |   4 +
 .../filtering/CassandraFilteringProjection.java    |   3 +-
 .../CassandraFilteringProjectionModule.java        |   1 -
 .../apache/james/jmap/api/filtering/Version.java   |   7 +
 .../impl/EventSourcingFilteringManagement.java     |   7 +-
 .../WebAdminServerIntegrationImmutableTest.java    |  17 ++
 .../apache/james/webadmin/data/jmap/Constants.java |   1 +
 ... PopulateFilteringProjectionRequestToTask.java} |  19 +-
 .../data/jmap/PopulateFilteringProjectionTask.java | 164 ++++++++++++++
 ...ringProjectionTaskAdditionalInformationDTO.java |  89 ++++++++
 ...tionItemsTaskAdditionalInformationDTOTest.java} |  25 ++-
 ...pulateFilteringProjectionRequestToTaskTest.java | 250 +++++++++++++++++++++
 ...teFilteringProjectionTaskSerializationTest.java |  51 +++++
 .../populateFilters.additionalInformation.json     |   6 +
 .../test/resources/json/populateFilters.task.json  |   3 +
 src/site/markdown/server/manage-webadmin.md        |  30 +++
 20 files changed, 717 insertions(+), 15 deletions(-)

diff --git 
a/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc 
b/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc
index 449216344b..3a62da769d 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/operate/webadmin.adoc
@@ -1044,6 +1044,37 @@ Response codes:
 * 201: Success. Corresponding task id is returned.
 * 400: Error in the request. Details can be found in the reported error.
 
+==== Recomputing Cassandra filtering projection
+
+You can force the reset of the Cassandra filtering projection by calling the 
following
+endpoint:
+
+....
+curl -XPOST /mailboxes?task=populateFilteringProjection
+....
+
+Will schedule a task.
+
+link:#_endpoints_returning_a_task[More details about endpoints returning
+a task].
+
+The scheduled task will have the following type
+`PopulateFilteringProjectionTask` and the following
+`additionalInformation`:
+
+....
+{
+  "type":"RecomputeAllPreviewsTask",
+  "processedUserCount": 3,
+  "failedUserCount": 2
+}
+....
+
+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:
diff --git 
a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
 
b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
index 96b1fa5aaa..7a3e8a65f4 100644
--- 
a/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
+++ 
b/server/container/guice/cassandra/src/main/java/org/apache/james/modules/data/CassandraJmapModule.java
@@ -82,6 +82,7 @@ public class CassandraJmapModule extends AbstractModule {
         bind(CustomIdentityDAO.class).to(CassandraCustomIdentityDAO.class);
 
         bind(CassandraFilteringProjection.class).in(Scopes.SINGLETON);
+        
bind(EventSourcingFilteringManagement.ReadProjection.class).to(CassandraFilteringProjection.class);
 
         bind(CassandraPushSubscriptionRepository.class).in(Scopes.SINGLETON);
         
bind(PushSubscriptionRepository.class).to(CassandraPushSubscriptionRepository.class);
diff --git 
a/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataJmapModule.java
 
b/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataJmapModule.java
index 55ba982be9..c7974d853a 100644
--- 
a/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataJmapModule.java
+++ 
b/server/container/guice/memory/src/main/java/org/apache/james/modules/data/MemoryDataJmapModule.java
@@ -58,6 +58,7 @@ public class MemoryDataJmapModule extends AbstractModule {
 
         bind(EventSourcingFilteringManagement.class).in(Scopes.SINGLETON);
         
bind(FilteringManagement.class).to(EventSourcingFilteringManagement.class);
+        
bind(EventSourcingFilteringManagement.ReadProjection.class).to(EventSourcingFilteringManagement.NoReadProjection.class);
 
         bind(DefaultTextExtractor.class).in(Scopes.SINGLETON);
         bind(TextExtractor.class).to(JsoupTextExtractor.class);
diff --git 
a/server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTaskSerializationModule.java
 
b/server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTaskSerializationModule.java
index 4684b32943..13a1a189e4 100644
--- 
a/server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTaskSerializationModule.java
+++ 
b/server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTaskSerializationModule.java
@@ -18,16 +18,20 @@
  ****************************************************************/
 package org.apache.james.modules.server;
 
+import 
org.apache.james.jmap.api.filtering.impl.EventSourcingFilteringManagement;
 import org.apache.james.server.task.json.dto.AdditionalInformationDTO;
 import org.apache.james.server.task.json.dto.AdditionalInformationDTOModule;
 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.user.api.UsersRepository;
 import org.apache.james.webadmin.data.jmap.EmailQueryViewPopulator;
 import org.apache.james.webadmin.data.jmap.MessageFastViewProjectionCorrector;
 import org.apache.james.webadmin.data.jmap.PopulateEmailQueryViewTask;
 import 
org.apache.james.webadmin.data.jmap.PopulateEmailQueryViewTaskAdditionalInformationDTO;
+import org.apache.james.webadmin.data.jmap.PopulateFilteringProjectionTask;
+import 
org.apache.james.webadmin.data.jmap.PopulateFilteringProjectionTaskAdditionalInformationDTO;
 import 
org.apache.james.webadmin.data.jmap.RecomputeAllFastViewProjectionItemsTask;
 import 
org.apache.james.webadmin.data.jmap.RecomputeAllFastViewTaskAdditionalInformationDTO;
 import 
org.apache.james.webadmin.data.jmap.RecomputeUserFastViewProjectionItemsTask;
@@ -49,6 +53,13 @@ public class JmapTaskSerializationModule extends 
AbstractModule {
         return PopulateEmailQueryViewTask.module(populator);
     }
 
+    @ProvidesIntoSet
+    public TaskDTOModule<? extends Task, ? extends TaskDTO> 
populateFilteringProjectionTask(EventSourcingFilteringManagement.NoReadProjection
 noReadProjection,
+                                                                               
             EventSourcingFilteringManagement.ReadProjection readProjection,
+                                                                               
             UsersRepository usersRepository) {
+        return PopulateFilteringProjectionTask.module(noReadProjection, 
readProjection, usersRepository);
+    }
+
     @ProvidesIntoSet
     public TaskDTOModule<? extends Task, ? extends TaskDTO> 
recomputeUserJmapPreviewsTask(MessageFastViewProjectionCorrector corrector) {
         return RecomputeUserFastViewProjectionItemsTask.module(corrector);
@@ -76,6 +87,17 @@ public class JmapTaskSerializationModule extends 
AbstractModule {
         return PopulateEmailQueryViewTaskAdditionalInformationDTO.module();
     }
 
+    @ProvidesIntoSet
+    public AdditionalInformationDTOModule<? extends 
TaskExecutionDetails.AdditionalInformation, ? extends  
AdditionalInformationDTO> populateFilteringProjectionAdditionalInformation() {
+        return 
PopulateFilteringProjectionTaskAdditionalInformationDTO.module();
+    }
+
+    @Named(DTOModuleInjections.WEBADMIN_DTO)
+    @ProvidesIntoSet
+    public AdditionalInformationDTOModule<? extends 
TaskExecutionDetails.AdditionalInformation, ? extends  
AdditionalInformationDTO> 
webAdminPopulateFilteringProjectionAdditionalInformation() {
+        return 
PopulateFilteringProjectionTaskAdditionalInformationDTO.module();
+    }
+
     @ProvidesIntoSet
     public AdditionalInformationDTOModule<? extends 
TaskExecutionDetails.AdditionalInformation, ? extends  
AdditionalInformationDTO> recomputeUserJmapPreviewsAdditionalInformation() {
         return RecomputeUserFastViewTaskAdditionalInformationDTO.module();
diff --git 
a/server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTasksModule.java
 
b/server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTasksModule.java
index aba434bb8b..c035925e66 100644
--- 
a/server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTasksModule.java
+++ 
b/server/container/guice/protocols/webadmin-jmap/src/main/java/org/apache/james/modules/server/JmapTasksModule.java
@@ -20,6 +20,7 @@
 package org.apache.james.modules.server;
 
 import org.apache.james.webadmin.data.jmap.PopulateEmailQueryViewRequestToTask;
+import 
org.apache.james.webadmin.data.jmap.PopulateFilteringProjectionRequestToTask;
 import 
org.apache.james.webadmin.data.jmap.RecomputeAllFastViewProjectionItemsRequestToTask;
 import 
org.apache.james.webadmin.data.jmap.RecomputeUserFastViewProjectionItemsRequestToTask;
 import org.apache.james.webadmin.routes.MailboxesRoutes;
@@ -41,6 +42,9 @@ public class JmapTasksModule extends AbstractModule {
         Multibinder.newSetBinder(binder(), 
TaskFromRequestRegistry.TaskRegistration.class, 
Names.named(MailboxesRoutes.ALL_MAILBOXES_TASKS))
             .addBinding().to(PopulateEmailQueryViewRequestToTask.class);
 
+        Multibinder.newSetBinder(binder(), 
TaskFromRequestRegistry.TaskRegistration.class, 
Names.named(MailboxesRoutes.ALL_MAILBOXES_TASKS))
+            .addBinding().to(PopulateFilteringProjectionRequestToTask.class);
+
         Multibinder.newSetBinder(binder(), 
TaskFromRequestRegistry.TaskRegistration.class, 
Names.named(UserMailboxesRoutes.USER_MAILBOXES_OPERATIONS_INJECTION_KEY))
             
.addBinding().to(RecomputeUserFastViewProjectionItemsRequestToTask.class);
 
diff --git 
a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/filtering/CassandraFilteringProjection.java
 
b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/filtering/CassandraFilteringProjection.java
index 0f0b74d98c..2841a99878 100644
--- 
a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/filtering/CassandraFilteringProjection.java
+++ 
b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/filtering/CassandraFilteringProjection.java
@@ -17,7 +17,6 @@ import 
org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
 import org.apache.james.core.Username;
 import org.apache.james.eventsourcing.Event;
 import org.apache.james.eventsourcing.ReactiveSubscriber;
-import org.apache.james.eventsourcing.Subscriber;
 import org.apache.james.jmap.api.filtering.Rules;
 import org.apache.james.jmap.api.filtering.Version;
 import 
org.apache.james.jmap.api.filtering.impl.EventSourcingFilteringManagement;
@@ -91,7 +90,7 @@ public class CassandraFilteringProjection implements 
EventSourcingFilteringManag
     }
 
     @Override
-    public Optional<Subscriber> subscriber() {
+    public Optional<ReactiveSubscriber> subscriber() {
         return Optional.of(this);
     }
 
diff --git 
a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/filtering/CassandraFilteringProjectionModule.java
 
b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/filtering/CassandraFilteringProjectionModule.java
index 5387e795a4..0c1b50d8eb 100644
--- 
a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/filtering/CassandraFilteringProjectionModule.java
+++ 
b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/filtering/CassandraFilteringProjectionModule.java
@@ -21,7 +21,6 @@ package org.apache.james.jmap.cassandra.filtering;
 
 import static com.datastax.oss.driver.api.core.type.DataTypes.INT;
 import static com.datastax.oss.driver.api.core.type.DataTypes.TEXT;
-import static com.datastax.oss.driver.api.core.type.DataTypes.frozenListOf;
 
 import org.apache.james.backends.cassandra.components.CassandraModule;
 
diff --git 
a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Version.java
 
b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Version.java
index 8e87cd2de6..5007ce7007 100644
--- 
a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Version.java
+++ 
b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Version.java
@@ -69,4 +69,11 @@ public class Version {
     public int asInteger() {
         return version;
     }
+
+    public Optional<EventId> asEventId() {
+        if (version == -1) {
+            return Optional.empty();
+        }
+        return Optional.of(EventId.apply(version));
+    }
 }
diff --git 
a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagement.java
 
b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagement.java
index 8061428e2c..402c532c6c 100644
--- 
a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagement.java
+++ 
b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/impl/EventSourcingFilteringManagement.java
@@ -28,6 +28,7 @@ import javax.inject.Inject;
 import org.apache.james.core.Username;
 import org.apache.james.eventsourcing.Event;
 import org.apache.james.eventsourcing.EventSourcingSystem;
+import org.apache.james.eventsourcing.ReactiveSubscriber;
 import org.apache.james.eventsourcing.Subscriber;
 import org.apache.james.eventsourcing.eventstore.EventStore;
 import org.apache.james.eventsourcing.eventstore.History;
@@ -49,7 +50,7 @@ public class EventSourcingFilteringManagement implements 
FilteringManagement {
 
         Publisher<Version> getLatestVersion(Username username);
 
-        Optional<Subscriber> subscriber();
+        Optional<ReactiveSubscriber> subscriber();
     }
 
     public static class NoReadProjection implements ReadProjection {
@@ -84,7 +85,7 @@ public class EventSourcingFilteringManagement implements 
FilteringManagement {
         }
 
         @Override
-        public Optional<Subscriber> subscriber() {
+        public Optional<ReactiveSubscriber> subscriber() {
             return Optional.empty();
         }
     }
@@ -103,7 +104,7 @@ public class EventSourcingFilteringManagement implements 
FilteringManagement {
         this.readProjection = new NoReadProjection(eventStore);
         this.eventSourcingSystem = EventSourcingSystem.fromJava(
             ImmutableSet.of(new DefineRulesCommandHandler(eventStore)),
-            
readProjection.subscriber().map(ImmutableSet::of).orElse(NO_SUBSCRIBER),
+            
readProjection.subscriber().map(Subscriber.class::cast).map(ImmutableSet::of).orElse(NO_SUBSCRIBER),
             eventStore);
     }
 
diff --git 
a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationImmutableTest.java
 
b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationImmutableTest.java
index 2c1e86c178..3eb1b128d6 100644
--- 
a/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationImmutableTest.java
+++ 
b/server/protocols/webadmin-integration-test/webadmin-integration-test-common/src/main/java/org/apache/james/webadmin/integration/WebAdminServerIntegrationImmutableTest.java
@@ -142,4 +142,21 @@ public abstract class 
WebAdminServerIntegrationImmutableTest {
             .body("status", is("completed"))
             .body("type", is("RecomputeAllFastViewProjectionItemsTask"));
     }
+
+    @Test
+    void jmapFilteringProjectionTasksShouldBeExposed() {
+        String taskId = with()
+            .queryParam("task", "populateFilteringProjection")
+            .post("/mailboxes")
+            .jsonPath()
+            .get("taskId");
+
+        given()
+            .basePath(TasksRoutes.BASE)
+        .when()
+            .get(taskId + "/await")
+        .then()
+            .body("status", is("completed"))
+            .body("type", is("PopulateFilteringProjectionTask"));
+    }
 }
\ No newline at end of file
diff --git 
a/server/protocols/webadmin/webadmin-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
index dfc0ad0f99..1a97bb9b68 100644
--- 
a/server/protocols/webadmin/webadmin-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
@@ -24,4 +24,5 @@ import org.apache.james.webadmin.tasks.TaskRegistrationKey;
 public interface Constants {
     TaskRegistrationKey TASK_REGISTRATION_KEY = 
TaskRegistrationKey.of("recomputeFastViewProjectionItems");
     TaskRegistrationKey POPULATE_EMAIL_QUERY_VIEW = 
TaskRegistrationKey.of("populateEmailQueryView");
+    TaskRegistrationKey POPULATE_FILTERING_PROJECTION = 
TaskRegistrationKey.of("populateFilteringProjection");
 }
diff --git 
a/server/protocols/webadmin/webadmin-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/PopulateFilteringProjectionRequestToTask.java
similarity index 57%
copy from 
server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java
copy to 
server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionRequestToTask.java
index dfc0ad0f99..7df285aaab 100644
--- 
a/server/protocols/webadmin/webadmin-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/PopulateFilteringProjectionRequestToTask.java
@@ -19,9 +19,20 @@
 
 package org.apache.james.webadmin.data.jmap;
 
-import org.apache.james.webadmin.tasks.TaskRegistrationKey;
+import static 
org.apache.james.webadmin.data.jmap.Constants.POPULATE_FILTERING_PROJECTION;
 
-public interface Constants {
-    TaskRegistrationKey TASK_REGISTRATION_KEY = 
TaskRegistrationKey.of("recomputeFastViewProjectionItems");
-    TaskRegistrationKey POPULATE_EMAIL_QUERY_VIEW = 
TaskRegistrationKey.of("populateEmailQueryView");
+import javax.inject.Inject;
+
+import 
org.apache.james.jmap.api.filtering.impl.EventSourcingFilteringManagement;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.webadmin.tasks.TaskFromRequestRegistry;
+
+public class PopulateFilteringProjectionRequestToTask extends 
TaskFromRequestRegistry.TaskRegistration {
+    @Inject
+    
PopulateFilteringProjectionRequestToTask(EventSourcingFilteringManagement.NoReadProjection
 noReadProjection,
+                                             
EventSourcingFilteringManagement.ReadProjection readProjection,
+                                             UsersRepository usersRepository) {
+        super(POPULATE_FILTERING_PROJECTION,
+            request -> new PopulateFilteringProjectionTask(noReadProjection, 
readProjection, usersRepository));
+    }
 }
diff --git 
a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionTask.java
 
b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionTask.java
new file mode 100644
index 0000000000..fb408500fe
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionTask.java
@@ -0,0 +1,164 @@
+/****************************************************************
+ * 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.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.james.core.Username;
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.jmap.api.filtering.Rules;
+import 
org.apache.james.jmap.api.filtering.impl.EventSourcingFilteringManagement;
+import org.apache.james.jmap.api.filtering.impl.FilteringAggregateId;
+import org.apache.james.jmap.api.filtering.impl.RuleSetDefined;
+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 org.apache.james.user.api.UsersRepository;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.common.collect.ImmutableList;
+
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+public class PopulateFilteringProjectionTask implements Task {
+    static final TaskType TASK_TYPE = 
TaskType.of("PopulateFilteringProjectionTask");
+
+    public static class AdditionalInformation implements 
TaskExecutionDetails.AdditionalInformation {
+        private static AdditionalInformation from(AtomicLong 
processedUserCount, AtomicLong failedUserCount) {
+            return new AdditionalInformation(processedUserCount.get(),
+                failedUserCount.get(),
+                Clock.systemUTC().instant());
+        }
+
+        private final long processedUserCount;
+        private final long failedUserCount;
+        private final Instant timestamp;
+
+        public AdditionalInformation(long processedUserCount, long 
failedUserCount, Instant timestamp) {
+            this.processedUserCount = processedUserCount;
+            this.failedUserCount = failedUserCount;
+            this.timestamp = timestamp;
+        }
+
+        public long getProcessedUserCount() {
+            return processedUserCount;
+        }
+
+        public long getFailedUserCount() {
+            return failedUserCount;
+        }
+
+        @Override
+        public Instant timestamp() {
+            return timestamp;
+        }
+    }
+
+    public static class PopulateFilteringProjectionTaskDTO implements TaskDTO {
+        private final String type;
+
+        public PopulateFilteringProjectionTaskDTO(@JsonProperty("type") String 
type) {
+            this.type = type;
+        }
+
+        @Override
+        public String getType() {
+            return type;
+        }
+    }
+
+    public static TaskDTOModule<PopulateFilteringProjectionTask, 
PopulateFilteringProjectionTaskDTO> 
module(EventSourcingFilteringManagement.NoReadProjection noReadProjection,
+                                                                               
                             EventSourcingFilteringManagement.ReadProjection 
readProjection,
+                                                                               
                             UsersRepository usersRepository) {
+        return DTOModule
+            .forDomainObject(PopulateFilteringProjectionTask.class)
+            .convertToDTO(PopulateFilteringProjectionTaskDTO.class)
+            .toDomainObjectConverter(dto -> asTask(noReadProjection, 
readProjection, usersRepository))
+            .toDTOConverter(PopulateFilteringProjectionTask::asDTO)
+            .typeName(TASK_TYPE.asString())
+            .withFactory(TaskDTOModule::new);
+    }
+
+    private static PopulateFilteringProjectionTaskDTO 
asDTO(PopulateFilteringProjectionTask task, String type) {
+        return new PopulateFilteringProjectionTaskDTO(type);
+    }
+
+    private static PopulateFilteringProjectionTask 
asTask(EventSourcingFilteringManagement.NoReadProjection noReadProjection,
+                                                          
EventSourcingFilteringManagement.ReadProjection readProjection,
+                                                          UsersRepository 
usersRepository) {
+        return new PopulateFilteringProjectionTask(noReadProjection, 
readProjection, usersRepository);
+    }
+
+    private final EventSourcingFilteringManagement.NoReadProjection 
noReadProjection;
+    private final EventSourcingFilteringManagement.ReadProjection 
readProjection;
+    private final UsersRepository usersRepository;
+    private final AtomicLong processedUserCount = new AtomicLong(0L);
+    private final AtomicLong failedUserCount = new AtomicLong(0L);
+
+    public 
PopulateFilteringProjectionTask(EventSourcingFilteringManagement.NoReadProjection
 noReadProjection,
+                                           
EventSourcingFilteringManagement.ReadProjection readProjection,
+                                           UsersRepository usersRepository) {
+        this.noReadProjection = noReadProjection;
+        this.readProjection = readProjection;
+        this.usersRepository = usersRepository;
+    }
+
+    @Override
+    public Result run() {
+        return Flux.from(usersRepository.listReactive())
+            .concatMap(user -> 
Mono.from(noReadProjection.listRulesForUser(user))
+                .flatMap(rules ->
+                    rules.getVersion().asEventId()
+                        .flatMap(eventId -> readProjection.subscriber()
+                            .map(s -> Mono.from(s.handleReactive(asEvent(user, 
rules, eventId)))))
+                        .orElse(Mono.empty()))
+                .thenReturn(Result.COMPLETED)
+                .doOnNext(next -> processedUserCount.incrementAndGet())
+                .onErrorResume(e -> {
+                    LOGGER.error("Failed populating Cassandra filter read 
projection for {}", user);
+                    failedUserCount.incrementAndGet();
+                    return Mono.just(Result.PARTIAL);
+                }))
+            .reduce(Task::combine)
+            .switchIfEmpty(Mono.just(Result.COMPLETED))
+            .block();
+    }
+
+    private RuleSetDefined asEvent(Username user, Rules rules, EventId 
eventId) {
+        return new RuleSetDefined(new FilteringAggregateId(user), eventId, 
ImmutableList.copyOf(rules.getRules()));
+    }
+
+    @Override
+    public TaskType type() {
+        return TASK_TYPE;
+    }
+
+    @Override
+    public Optional<TaskExecutionDetails.AdditionalInformation> details() {
+        return Optional.of(AdditionalInformation.from(processedUserCount, 
failedUserCount));
+    }
+}
\ No newline at end of file
diff --git 
a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionTaskAdditionalInformationDTO.java
 
b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionTaskAdditionalInformationDTO.java
new file mode 100644
index 0000000000..1b529c2914
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionTaskAdditionalInformationDTO.java
@@ -0,0 +1,89 @@
+/****************************************************************
+ * 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 PopulateFilteringProjectionTaskAdditionalInformationDTO 
implements AdditionalInformationDTO {
+    public static 
AdditionalInformationDTOModule<PopulateFilteringProjectionTask.AdditionalInformation,
 PopulateFilteringProjectionTaskAdditionalInformationDTO> module() {
+        return 
DTOModule.forDomainObject(PopulateFilteringProjectionTask.AdditionalInformation.class)
+            
.convertToDTO(PopulateFilteringProjectionTaskAdditionalInformationDTO.class)
+            
.toDomainObjectConverter(PopulateFilteringProjectionTaskAdditionalInformationDTO::toDomainObject)
+            
.toDTOConverter(PopulateFilteringProjectionTaskAdditionalInformationDTO::toDTO)
+            .typeName(PopulateFilteringProjectionTask.TASK_TYPE.asString())
+            .withFactory(AdditionalInformationDTOModule::new);
+    }
+
+    private static PopulateFilteringProjectionTask.AdditionalInformation 
toDomainObject(PopulateFilteringProjectionTaskAdditionalInformationDTO dto) {
+        return new PopulateFilteringProjectionTask.AdditionalInformation(
+            dto.getProcessedUserCount(),
+            dto.getFailedUserCount(),
+            dto.timestamp);
+    }
+
+    private static PopulateFilteringProjectionTaskAdditionalInformationDTO 
toDTO(PopulateFilteringProjectionTask.AdditionalInformation details, String 
type) {
+        return new PopulateFilteringProjectionTaskAdditionalInformationDTO(
+            type,
+            details.timestamp(),
+            details.getProcessedUserCount(),
+            details.getFailedUserCount());
+    }
+
+    private final String type;
+    private final Instant timestamp;
+    private final long processedUserCount;
+    private final long failedUserCount;
+
+    @VisibleForTesting
+    
PopulateFilteringProjectionTaskAdditionalInformationDTO(@JsonProperty("type") 
String type,
+                                                            
@JsonProperty("timestamp") Instant timestamp,
+                                                            
@JsonProperty("processedUserCount") long processedUserCount,
+                                                            
@JsonProperty("failedUserCount") long failedUserCount) {
+        this.type = type;
+        this.timestamp = timestamp;
+        this.processedUserCount = processedUserCount;
+        this.failedUserCount = failedUserCount;
+    }
+
+    @Override
+    public String getType() {
+        return type;
+    }
+
+    @Override
+    public Instant getTimestamp() {
+        return timestamp;
+    }
+
+    public long getProcessedUserCount() {
+        return processedUserCount;
+    }
+
+    public long getFailedUserCount() {
+        return failedUserCount;
+    }
+}
diff --git 
a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java
 
b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionItemsTaskAdditionalInformationDTOTest.java
similarity index 58%
copy from 
server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java
copy to 
server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionItemsTaskAdditionalInformationDTOTest.java
index dfc0ad0f99..5a5accfd1c 100644
--- 
a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/Constants.java
+++ 
b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionItemsTaskAdditionalInformationDTOTest.java
@@ -19,9 +19,24 @@
 
 package org.apache.james.webadmin.data.jmap;
 
-import org.apache.james.webadmin.tasks.TaskRegistrationKey;
+import java.time.Instant;
 
-public interface Constants {
-    TaskRegistrationKey TASK_REGISTRATION_KEY = 
TaskRegistrationKey.of("recomputeFastViewProjectionItems");
-    TaskRegistrationKey POPULATE_EMAIL_QUERY_VIEW = 
TaskRegistrationKey.of("populateEmailQueryView");
-}
+import org.apache.james.JsonSerializationVerifier;
+import org.apache.james.util.ClassLoaderUtils;
+import org.junit.jupiter.api.Test;
+
+class PopulateFilteringProjectionItemsTaskAdditionalInformationDTOTest {
+    private static final Instant INSTANT = 
Instant.parse("2007-12-03T10:15:30.00Z");
+    private static final PopulateFilteringProjectionTask.AdditionalInformation 
DOMAIN_OBJECT = new PopulateFilteringProjectionTask.AdditionalInformation(
+        1,
+        2,
+        INSTANT);
+
+    @Test
+    void shouldMatchJsonSerializationContract() throws Exception {
+        
JsonSerializationVerifier.dtoModule(PopulateFilteringProjectionTaskAdditionalInformationDTO.module())
+            .bean(DOMAIN_OBJECT)
+            
.json(ClassLoaderUtils.getSystemResourceAsString("json/populateFilters.additionalInformation.json"))
+            .verify();
+    }
+}
\ No newline at end of file
diff --git 
a/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionRequestToTaskTest.java
 
b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionRequestToTaskTest.java
new file mode 100644
index 0000000000..58b5fc547d
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionRequestToTaskTest.java
@@ -0,0 +1,250 @@
+/****************************************************************
+ * 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 javax.mail.Flags.Flag.DELETED;
+import static 
org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.CONTAINS;
+import static org.apache.james.jmap.api.filtering.Rule.Condition.Field.SUBJECT;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.Optional;
+
+import javax.mail.Flags;
+
+import org.apache.james.core.Username;
+import org.apache.james.domainlist.api.DomainList;
+import org.apache.james.eventsourcing.Event;
+import org.apache.james.eventsourcing.EventId;
+import org.apache.james.eventsourcing.ReactiveSubscriber;
+import org.apache.james.jmap.api.filtering.Rule;
+import org.apache.james.jmap.api.filtering.Rules;
+import org.apache.james.jmap.api.filtering.Version;
+import 
org.apache.james.jmap.api.filtering.impl.EventSourcingFilteringManagement;
+import org.apache.james.jmap.api.filtering.impl.FilteringAggregateId;
+import org.apache.james.jmap.api.filtering.impl.RuleSetDefined;
+import org.apache.james.jmap.memory.projections.MemoryEmailQueryView;
+import org.apache.james.json.DTOConverter;
+import org.apache.james.mailbox.MailboxSession;
+import org.apache.james.mailbox.MessageManager;
+import org.apache.james.mailbox.extension.PreDeletionHook;
+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.task.Hostname;
+import org.apache.james.task.MemoryTaskManager;
+import org.apache.james.task.TaskManager;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.user.memory.MemoryUsersRepository;
+import org.apache.james.util.streams.Limit;
+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 org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import com.google.common.collect.ImmutableList;
+
+import io.restassured.RestAssured;
+import reactor.core.publisher.Mono;
+import spark.Service;
+
+class PopulateFilteringProjectionRequestToTaskTest {
+    private static final String UNSCRAMBLED_SUBJECT = "this is the subject 
Frédéric MARTIN of the mail";
+
+    private EventSourcingFilteringManagement.NoReadProjection noReadProjection;
+    private EventSourcingFilteringManagement.ReadProjection readProjection;
+
+    private static final class JMAPRoutes implements Routes {
+        private final TaskManager taskManager;
+        private final EventSourcingFilteringManagement.NoReadProjection 
noReadProjection;
+        private final EventSourcingFilteringManagement.ReadProjection 
readProjection;
+        private final UsersRepository usersRepository;
+
+        private JMAPRoutes(EventSourcingFilteringManagement.NoReadProjection 
noReadProjection,
+                           EventSourcingFilteringManagement.ReadProjection 
readProjection,
+                           UsersRepository usersRepository,
+                           TaskManager taskManager) {
+            this.noReadProjection = noReadProjection;
+            this.readProjection = readProjection;
+            this.usersRepository = usersRepository;
+            this.taskManager = taskManager;
+        }
+
+        @Override
+        public String getBasePath() {
+            return BASE_PATH;
+        }
+
+        @Override
+        public void define(Service service) {
+            service.post(BASE_PATH,
+                TaskFromRequestRegistry.builder()
+                    .registrations(new 
PopulateFilteringProjectionRequestToTask(noReadProjection, readProjection, 
usersRepository))
+                    .buildAsRoute(taskManager),
+                new JsonTransformer());
+        }
+    }
+
+    static final String BASE_PATH = "/mailboxes";
+
+    static final DomainList NO_DOMAIN_LIST = null;
+    static final Username BOB = Username.of("bob");
+
+    private WebAdminServer webAdminServer;
+    private MemoryTaskManager taskManager;
+    private MailboxId bobInboxboxId;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        JsonTransformer jsonTransformer = new JsonTransformer();
+        taskManager = new MemoryTaskManager(new Hostname("foo"));
+
+        InMemoryMailboxManager mailboxManager = 
InMemoryIntegrationResources.defaultResources().getMailboxManager();
+        MemoryUsersRepository usersRepository = 
MemoryUsersRepository.withoutVirtualHosting(NO_DOMAIN_LIST);
+        usersRepository.addUser(BOB, "pass");
+        MailboxSession bobSession = mailboxManager.createSystemSession(BOB);
+        bobInboxboxId = mailboxManager.createMailbox(MailboxPath.inbox(BOB), 
bobSession)
+            .get();
+
+        noReadProjection = 
mock(EventSourcingFilteringManagement.NoReadProjection.class);
+        readProjection = 
mock(EventSourcingFilteringManagement.ReadProjection.class);
+        webAdminServer = WebAdminUtils.createWebAdminServer(
+            new TasksRoutes(taskManager, jsonTransformer,
+                
DTOConverter.of(PopulateFilteringProjectionTaskAdditionalInformationDTO.module())),
+            new JMAPRoutes(
+                noReadProjection,
+                readProjection,
+                usersRepository,
+                taskManager))
+            .start();
+
+        RestAssured.requestSpecification = 
WebAdminUtils.buildRequestSpecification(webAdminServer)
+            .setBasePath("/mailboxes")
+            .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 [populateFilteringProjection]"));
+    }
+
+    @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 [populateFilteringProjection]"));
+    }
+
+    @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 [populateFilteringProjection]"));
+    }
+
+    @Test
+    void postShouldCreateANewTask() {
+        given()
+            .queryParam("action", "populateFilteringProjection")
+            .post()
+        .then()
+            .statusCode(HttpStatus.CREATED_201)
+            .body("taskId", notNullValue());
+    }
+
+    @Test
+    void populateShouldUpdateProjection() {
+        Rule rule = Rule.builder()
+            .id(Rule.Id.of("2"))
+            .name("rule 2")
+            .condition(Rule.Condition.of(SUBJECT, CONTAINS, 
UNSCRAMBLED_SUBJECT))
+            
.action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(ImmutableList.of(bobInboxboxId.serialize()))))
+            .build();
+
+        Mockito.when(noReadProjection.listRulesForUser(any()))
+            .thenReturn(Mono.just(new Rules(ImmutableList.of(rule), new 
Version(4))));
+        ReactiveSubscriber subscriber = mock(ReactiveSubscriber.class);
+        
Mockito.when(readProjection.subscriber()).thenReturn(Optional.of(subscriber));
+        
Mockito.when(subscriber.handleReactive(any())).thenReturn(Mono.empty());
+
+        String taskId = with()
+            .queryParam("action", "populateFilteringProjection")
+            .post()
+            .jsonPath()
+            .get("taskId");
+
+        with()
+            .basePath(TasksRoutes.BASE)
+            .get(taskId + "/await");
+
+        ArgumentCaptor<Event> captor = ArgumentCaptor.forClass(Event.class);
+        verify(subscriber, times(1)).handleReactive(captor.capture());
+
+        
assertThat(captor.getValue().eventId()).isEqualTo(EventId.fromSerialized(4));
+        assertThat(captor.getValue().getAggregateId()).isEqualTo(new 
FilteringAggregateId(BOB));
+        assertThat(captor.getValue()).isInstanceOf(RuleSetDefined.class);
+        RuleSetDefined ruleSetDefined = (RuleSetDefined) captor.getValue();
+        assertThat(ruleSetDefined.getRules()).containsOnly(rule);
+    }
+}
\ No newline at end of file
diff --git 
a/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionTaskSerializationTest.java
 
b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionTaskSerializationTest.java
new file mode 100644
index 0000000000..03c4401d19
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/PopulateFilteringProjectionTaskSerializationTest.java
@@ -0,0 +1,51 @@
+/****************************************************************
+ * 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.eventsourcing.eventstore.EventStore;
+import 
org.apache.james.jmap.api.filtering.impl.EventSourcingFilteringManagement;
+import org.apache.james.user.api.UsersRepository;
+import org.apache.james.util.ClassLoaderUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class PopulateFilteringProjectionTaskSerializationTest {
+    private EventSourcingFilteringManagement.NoReadProjection noReadProjection;
+    private EventSourcingFilteringManagement.ReadProjection readProjection;
+    private UsersRepository usersRepository;
+
+    @BeforeEach
+    void setUp() {
+        noReadProjection = new 
EventSourcingFilteringManagement.NoReadProjection(mock(EventStore.class));
+        readProjection = 
mock(EventSourcingFilteringManagement.ReadProjection.class);
+        usersRepository = mock(UsersRepository.class);
+    }
+
+    @Test
+    void shouldMatchJsonSerializationContract() throws Exception {
+        
JsonSerializationVerifier.dtoModule(PopulateFilteringProjectionTask.module(noReadProjection,
 readProjection, usersRepository))
+            .bean(new PopulateFilteringProjectionTask(noReadProjection, 
readProjection, usersRepository))
+            
.json(ClassLoaderUtils.getSystemResourceAsString("json/populateFilters.task.json"))
+            .verify();
+    }
+}
\ No newline at end of file
diff --git 
a/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/populateFilters.additionalInformation.json
 
b/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/populateFilters.additionalInformation.json
new file mode 100644
index 0000000000..8e2fd25bd7
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/populateFilters.additionalInformation.json
@@ -0,0 +1,6 @@
+{
+  "type":"PopulateFilteringProjectionTask",
+  "timestamp":"2007-12-03T10:15:30Z",
+  "processedUserCount":1,
+  "failedUserCount":2
+}
\ No newline at end of file
diff --git 
a/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/populateFilters.task.json
 
b/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/populateFilters.task.json
new file mode 100644
index 0000000000..65b1b1f29d
--- /dev/null
+++ 
b/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/populateFilters.task.json
@@ -0,0 +1,3 @@
+{
+  "type":"PopulateFilteringProjectionTask"
+}
\ No newline at end of file
diff --git a/src/site/markdown/server/manage-webadmin.md 
b/src/site/markdown/server/manage-webadmin.md
index f918215dd1..ed18c2c9b2 100644
--- a/src/site/markdown/server/manage-webadmin.md
+++ b/src/site/markdown/server/manage-webadmin.md
@@ -1545,6 +1545,36 @@ Response codes:
  - 201: Success. Corresponding task id is returned.
  - 400: Error in the request. Details can be found in the reported error.
  - 404: User not found.
+ 
+ 
+### Recomputing Cassandra filtering projection
+
+You can force the reset of the Cassandra filtering projection by calling the 
following
+endpoint:
+
+```
+curl -XPOST /mailboxes?task=populateFilteringProjection
+```
+
+Will schedule a task.
+
+[More details about endpoints returning a task](#Endpoints_returning_a_task).
+
+The scheduled task will have the following type
+`PopulateFilteringProjectionTask` and the following
+`additionalInformation`:
+
+```{
+  "type":"RecomputeAllPreviewsTask",
+  "processedUserCount": 3,
+  "failedUserCount": 2
+}
+```
+
+Response codes:
+
+ - 201: Success. Corresponding task id is returned.
+ - 400: Error in the request. Details can be found in the reported error.
 
 ## Administrating quotas by users
 


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org
For additional commands, e-mail: notifications-h...@james.apache.org

Reply via email to