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
The following commit(s) were added to refs/heads/master by this push:
new ddfef1e054 JAMES-4148 Apply maximum number of actions taken per
mailbox (#2836)
ddfef1e054 is described below
commit ddfef1e05403a46a9136023721807bc791f20431
Author: Trần Hồng Quân <[email protected]>
AuthorDate: Tue Oct 14 14:19:10 2025 +0700
JAMES-4148 Apply maximum number of actions taken per mailbox (#2836)
Avoid moving too many messages at once which could cause tombstone issue on
Cassandra.
---
.../modules/servers/partials/operate/webadmin.adoc | 5 +
.../data/jmap/RunRulesOnMailboxService.java | 38 ++++-
.../webadmin/data/jmap/RunRulesOnMailboxTask.java | 60 ++++++--
...RulesOnMailboxTaskAdditionalInformationDTO.java | 24 +++-
.../data/jmap/RunRulesOnMailboxRoutesTest.java | 158 ++++++++++++++++++++-
...sOnMailboxTaskAdditionalInformationDTOTest.java | 2 +-
.../runRulesOnMailbox.additionalInformation.json | 2 +
7 files changed, 273 insertions(+), 16 deletions(-)
diff --git a/docs/modules/servers/partials/operate/webadmin.adoc
b/docs/modules/servers/partials/operate/webadmin.adoc
index 36e5d7d985..ccf1fdf780 100644
--- a/docs/modules/servers/partials/operate/webadmin.adoc
+++ b/docs/modules/servers/partials/operate/webadmin.adoc
@@ -1864,14 +1864,19 @@ the following `additionalInformation`:
....
{
"mailboxName": "mbx1",
+ "processedMessagesCount": 15,
"rulesOnMessagesApplySuccessfully": 9,
"rulesOnMessagesApplyFailed": 3,
+ "maximumAppliedActionExceeded": false,
"timestamp": "2024-12-03T10:15:30Z",
"type": "RunRulesOnMailboxTask",
"username": "[email protected]"
}
....
+Note: you can configure the maximum number of actions applied per mailbox by
setting the JVM property `james.rules.triage.max.actions.per.mailbox`. The
default value is 25000.
+This limit may help avoid the tombstone threshold issue on Cassandra backends
when too many messages are moved or deleted at once.
+
=== Subscribing a user to all of its mailboxes
....
diff --git
a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxService.java
b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxService.java
index d80664a1ef..881dc3fefb 100644
---
a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxService.java
+++
b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxService.java
@@ -22,6 +22,8 @@ package org.apache.james.webadmin.data.jmap;
import static org.apache.james.util.ReactorUtils.DEFAULT_CONCURRENCY;
import java.util.List;
+import java.util.Optional;
+import java.util.function.BiConsumer;
import jakarta.inject.Inject;
@@ -49,23 +51,39 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.fge.lambdas.Throwing;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
+import reactor.core.publisher.SynchronousSink;
public class RunRulesOnMailboxService {
+ public static class TooManyAppliedActionsException extends
RuntimeException {
+ }
+
private static final Logger LOGGER =
LoggerFactory.getLogger(RunRulesOnMailboxService.class);
+ private static final int MAX_ACTIONS_PER_MAILBOX =
Optional.ofNullable(System.getProperty("james.rules.triage.max.actions.per.mailbox"))
+ .map(Integer::valueOf)
+ .orElse(25000);
private final MailboxManager mailboxManager;
private final MailboxId.Factory mailboxIdFactory;
private final MessageIdManager messageIdManager;
+ private final int maxActionsPerMailbox;
@Inject
public RunRulesOnMailboxService(MailboxManager mailboxManager,
MailboxId.Factory mailboxIdFactory, MessageIdManager messageIdManager) {
+ this(mailboxManager, mailboxIdFactory, messageIdManager,
MAX_ACTIONS_PER_MAILBOX);
+ }
+
+ @VisibleForTesting
+ public RunRulesOnMailboxService(MailboxManager mailboxManager,
MailboxId.Factory mailboxIdFactory, MessageIdManager messageIdManager,
+ int maxActionsPerMailbox) {
this.mailboxManager = mailboxManager;
this.mailboxIdFactory = mailboxIdFactory;
this.messageIdManager = messageIdManager;
+ this.maxActionsPerMailbox = maxActionsPerMailbox;
}
public Mono<Task.Result> runRulesOnMailbox(Username username, MailboxName
mailboxName, Rules rules, RunRulesOnMailboxTask.Context context) {
@@ -75,6 +93,11 @@ public class RunRulesOnMailboxService {
return
Mono.from(mailboxManager.getMailboxReactive(MailboxPath.forUser(username,
mailboxName.asString()), mailboxSession))
.flatMapMany(messageManager ->
Flux.from(messageManager.getMessagesReactive(MessageRange.all(),
FetchGroup.HEADERS, mailboxSession))
.flatMap(Throwing.function(messageResult ->
runRulesOnMessage(ruleMatcher, messageResult, mailboxSession, context)),
DEFAULT_CONCURRENCY))
+ .onErrorResume(TooManyAppliedActionsException.class, e -> {
+ LOGGER.info("Maximum number of actions exceeded for mailbox {}
of user {}", mailboxName.asString(), username);
+ context.setMaximumAppliedActionExceeded();
+ return Mono.just(Task.Result.COMPLETED);
+ })
.onErrorResume(e -> {
LOGGER.error("Error when applying rules to mailbox. Mailbox {}
for user {}", mailboxName.asString(), username, e);
context.incrementFails();
@@ -86,11 +109,24 @@ public class RunRulesOnMailboxService {
}
private Flux<Task.Result> runRulesOnMessage(RuleMatcher ruleMatcher,
MessageResult messageResult, MailboxSession mailboxSession,
RunRulesOnMailboxTask.Context context) throws MailboxException {
+ context.increaseProcessedMessagesCount();
+
return Flux.fromStream(ruleMatcher.findApplicableRules(messageResult))
.map(Rule::getAction)
+ .handle(applyMaxActionsLimit(context))
.concatMap(action -> applyActionOnMessage(messageResult, action,
mailboxSession, context));
}
+ private BiConsumer<Rule.Action, SynchronousSink<Rule.Action>>
applyMaxActionsLimit(RunRulesOnMailboxTask.Context context) {
+ return (action, sink) -> {
+ if (context.snapshot().getRulesOnMessagesApplySuccessfully() >=
maxActionsPerMailbox) {
+ sink.error(new TooManyAppliedActionsException());
+ } else {
+ sink.next(action);
+ }
+ };
+ }
+
private Mono<Task.Result> applyActionOnMessage(MessageResult
messageResult, Rule.Action action, MailboxSession mailboxSession,
RunRulesOnMailboxTask.Context context) {
actionOnMessagePreconditions(action);
return appendInMailboxes(messageResult.getMessageId(), action,
mailboxSession, context)
@@ -131,7 +167,7 @@ public class RunRulesOnMailboxService {
}
return
Mono.from(messageIdManager.setInMailboxesReactive(messageId, mailboxIds,
mailboxSession))
- .doOnSuccess(next -> context.incrementSuccesses())
+ .then(Mono.fromRunnable(context::incrementSuccesses))
.then(Mono.just(Task.Result.COMPLETED));
});
}
diff --git
a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTask.java
b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTask.java
index 0b3f30be1d..e9c3f7f356 100644
---
a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTask.java
+++
b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTask.java
@@ -23,6 +23,7 @@ import java.time.Clock;
import java.time.Instant;
import java.util.Objects;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.james.core.Username;
@@ -39,10 +40,14 @@ public class RunRulesOnMailboxTask implements Task {
public static class Snapshot {
private final long rulesOnMessagesApplySuccessfully;
private final long rulesOnMessagesApplyFailed;
+ private final boolean maximumAppliedActionExceeded;
+ private final long processedMessagesCount;
- private Snapshot(long rulesOnMessagesApplySuccessfully, long
rulesOnMessagesApplyFailed) {
+ private Snapshot(long rulesOnMessagesApplySuccessfully, long
rulesOnMessagesApplyFailed, boolean maximumAppliedActionExceeded, long
processedMessagesCount) {
this.rulesOnMessagesApplySuccessfully =
rulesOnMessagesApplySuccessfully;
this.rulesOnMessagesApplyFailed = rulesOnMessagesApplyFailed;
+ this.maximumAppliedActionExceeded =
maximumAppliedActionExceeded;
+ this.processedMessagesCount = processedMessagesCount;
}
public long getRulesOnMessagesApplySuccessfully() {
@@ -59,14 +64,17 @@ public class RunRulesOnMailboxTask implements Task {
Context.Snapshot that = (Context.Snapshot) o;
return
Objects.equals(this.rulesOnMessagesApplySuccessfully,
that.rulesOnMessagesApplySuccessfully)
- && Objects.equals(this.rulesOnMessagesApplyFailed,
that.rulesOnMessagesApplyFailed);
+ && Objects.equals(this.rulesOnMessagesApplyFailed,
that.rulesOnMessagesApplyFailed)
+ && Objects.equals(this.maximumAppliedActionExceeded,
that.maximumAppliedActionExceeded)
+ && Objects.equals(this.processedMessagesCount,
that.processedMessagesCount);
}
return false;
}
@Override
public final int hashCode() {
- return Objects.hash(rulesOnMessagesApplySuccessfully,
rulesOnMessagesApplyFailed);
+ return Objects.hash(rulesOnMessagesApplySuccessfully,
rulesOnMessagesApplyFailed, maximumAppliedActionExceeded,
+ processedMessagesCount);
}
@Override
@@ -74,44 +82,62 @@ public class RunRulesOnMailboxTask implements Task {
return MoreObjects.toStringHelper(this)
.add("rulesOnMessagesApplySuccessfully",
rulesOnMessagesApplySuccessfully)
.add("rulesOnMessagesApplyFailed",
rulesOnMessagesApplyFailed)
+ .add("maximumAppliedActionExceeded",
maximumAppliedActionExceeded)
+ .add("processedMessagesCount", processedMessagesCount)
.toString();
}
}
private final AtomicLong rulesOnMessagesApplySuccessfully;
private final AtomicLong rulesOnMessagesApplyFailed;
+ private final AtomicBoolean maximumAppliedActionExceeded;
+ private final AtomicLong processedMessagesCount;
public Context() {
this.rulesOnMessagesApplySuccessfully = new AtomicLong();
this.rulesOnMessagesApplyFailed = new AtomicLong();
+ this.maximumAppliedActionExceeded = new AtomicBoolean();
+ this.processedMessagesCount = new AtomicLong();
}
- public Context(long rulesOnMessagesApplySuccessfully, long
rulesOnMessagesApplyFailed) {
+ public Context(long rulesOnMessagesApplySuccessfully, long
rulesOnMessagesApplyFailed, boolean maximumAppliedActionExceeded,
+ long processedMessagesCount) {
this.rulesOnMessagesApplySuccessfully = new
AtomicLong(rulesOnMessagesApplySuccessfully);
this.rulesOnMessagesApplyFailed = new
AtomicLong(rulesOnMessagesApplyFailed);
+ this.maximumAppliedActionExceeded = new
AtomicBoolean(maximumAppliedActionExceeded);
+ this.processedMessagesCount = new
AtomicLong(processedMessagesCount);
}
public void incrementSuccesses() {
rulesOnMessagesApplySuccessfully.incrementAndGet();
}
-
public void incrementFails() {
rulesOnMessagesApplyFailed.incrementAndGet();
}
+ public void setMaximumAppliedActionExceeded() {
+ maximumAppliedActionExceeded.set(true);
+ }
+
+ public void increaseProcessedMessagesCount() {
+ processedMessagesCount.incrementAndGet();
+ }
+
public Context.Snapshot snapshot() {
- return new
Context.Snapshot(rulesOnMessagesApplySuccessfully.get(),
rulesOnMessagesApplyFailed.get());
+ return new
Context.Snapshot(rulesOnMessagesApplySuccessfully.get(),
rulesOnMessagesApplyFailed.get(), maximumAppliedActionExceeded.get(),
+ processedMessagesCount.get());
}
}
public static class AdditionalInformation implements
TaskExecutionDetails.AdditionalInformation {
private static AdditionalInformation from(Username username,
-
MailboxName mailboxName,
-
RunRulesOnMailboxTask.Context context) {
+ MailboxName mailboxName,
+
RunRulesOnMailboxTask.Context context) {
Context.Snapshot snapshot = context.snapshot();
- return new AdditionalInformation(username, mailboxName,
Clock.systemUTC().instant(), snapshot.rulesOnMessagesApplySuccessfully,
snapshot.rulesOnMessagesApplyFailed);
+ return new AdditionalInformation(username, mailboxName,
Clock.systemUTC().instant(), snapshot.rulesOnMessagesApplySuccessfully,
+ snapshot.rulesOnMessagesApplyFailed,
snapshot.maximumAppliedActionExceeded, snapshot.processedMessagesCount);
}
private final Username username;
@@ -119,17 +145,23 @@ public class RunRulesOnMailboxTask implements Task {
private final Instant timestamp;
private final long rulesOnMessagesApplySuccessfully;
private final long rulesOnMessagesApplyFailed;
+ private final boolean maximumAppliedActionExceeded;
+ private final long processedMessagesCount;
public AdditionalInformation(Username username,
MailboxName mailboxName,
Instant timestamp,
long rulesOnMessagesApplySuccessfully,
- long rulesOnMessagesApplyFailed) {
+ long rulesOnMessagesApplyFailed,
+ boolean maximumAppliedActionExceeded,
+ long processedMessagesCount) {
this.username = username;
this.mailboxName = mailboxName;
this.timestamp = timestamp;
this.rulesOnMessagesApplySuccessfully =
rulesOnMessagesApplySuccessfully;
this.rulesOnMessagesApplyFailed = rulesOnMessagesApplyFailed;
+ this.maximumAppliedActionExceeded = maximumAppliedActionExceeded;
+ this.processedMessagesCount = processedMessagesCount;
}
public Username getUsername() {
@@ -152,6 +184,14 @@ public class RunRulesOnMailboxTask implements Task {
return rulesOnMessagesApplyFailed;
}
+ public boolean maximumAppliedActionExceeded() {
+ return maximumAppliedActionExceeded;
+ }
+
+ public long getProcessedMessagesCount() {
+ return processedMessagesCount;
+ }
+
@Override
public Instant timestamp() {
return timestamp;
diff --git
a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTaskAdditionalInformationDTO.java
b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTaskAdditionalInformationDTO.java
index a3b101f23e..4f3feff328 100644
---
a/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTaskAdditionalInformationDTO.java
+++
b/server/protocols/webadmin/webadmin-jmap/src/main/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTaskAdditionalInformationDTO.java
@@ -44,7 +44,9 @@ public class RunRulesOnMailboxTaskAdditionalInformationDTO
implements Additional
new MailboxName(dto.getMailboxName()),
dto.getTimestamp(),
dto.getRulesOnMessagesApplySuccessfully(),
- dto.getRulesOnMessagesApplyFailed());
+ dto.getRulesOnMessagesApplyFailed(),
+ dto.getMaximumAppliedActionExceeded(),
+ dto.getProcessedMessagesCount());
}
private static RunRulesOnMailboxTaskAdditionalInformationDTO
toDto(RunRulesOnMailboxTask.AdditionalInformation domain, String type) {
@@ -54,7 +56,9 @@ public class RunRulesOnMailboxTaskAdditionalInformationDTO
implements Additional
domain.getMailboxName().asString(),
domain.getTimestamp(),
domain.getRulesOnMessagesApplySuccessfully(),
- domain.getRulesOnMessagesApplyFailed());
+ domain.getRulesOnMessagesApplyFailed(),
+ domain.maximumAppliedActionExceeded(),
+ domain.getProcessedMessagesCount());
}
private final String type;
@@ -63,19 +67,25 @@ public class RunRulesOnMailboxTaskAdditionalInformationDTO
implements Additional
private final Instant timestamp;
private final long rulesOnMessagesApplySuccessfully;
private final long rulesOnMessagesApplyFailed;
+ private final boolean maximumAppliedActionExceeded;
+ private final long processedMessagesCount;
public RunRulesOnMailboxTaskAdditionalInformationDTO(@JsonProperty("type")
String type,
@JsonProperty("username") String username,
@JsonProperty("mailboxName") String mailboxName,
@JsonProperty("timestamp") Instant timestamp,
@JsonProperty("rulesOnMessagesApplySuccessfully") long
rulesOnMessagesApplySuccessfully,
-
@JsonProperty("rulesOnMessagesApplyFailed") long rulesOnMessagesApplyFailed) {
+
@JsonProperty("rulesOnMessagesApplyFailed") long rulesOnMessagesApplyFailed,
+
@JsonProperty("maximumAppliedActionExceeded") boolean
maximumAppliedActionExceeded,
+
@JsonProperty("processedMessagesCount") long processedMessagesCount) {
this.type = type;
this.username = username;
this.mailboxName = mailboxName;
this.timestamp = timestamp;
this.rulesOnMessagesApplySuccessfully =
rulesOnMessagesApplySuccessfully;
this.rulesOnMessagesApplyFailed = rulesOnMessagesApplyFailed;
+ this.maximumAppliedActionExceeded = maximumAppliedActionExceeded;
+ this.processedMessagesCount = processedMessagesCount;
}
@Override
@@ -103,4 +113,12 @@ public class RunRulesOnMailboxTaskAdditionalInformationDTO
implements Additional
public long getRulesOnMessagesApplyFailed() {
return rulesOnMessagesApplyFailed;
}
+
+ public boolean getMaximumAppliedActionExceeded() {
+ return maximumAppliedActionExceeded;
+ }
+
+ public long getProcessedMessagesCount() {
+ return processedMessagesCount;
+ }
}
diff --git
a/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxRoutesTest.java
b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxRoutesTest.java
index 3d36c605f8..1111ea9d97 100644
---
a/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxRoutesTest.java
+++
b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxRoutesTest.java
@@ -36,6 +36,7 @@ import java.time.Clock;
import java.time.Duration;
import java.util.Date;
import java.util.Map;
+import java.util.stream.IntStream;
import jakarta.mail.Flags;
@@ -1039,6 +1040,161 @@ public class RunRulesOnMailboxRoutesTest {
.body("additionalInformation.username",
Matchers.is(USERNAME.asString()))
.body("additionalInformation.mailboxName",
Matchers.is(MAILBOX_NAME))
.body("additionalInformation.rulesOnMessagesApplySuccessfully",
Matchers.is(2))
- .body("additionalInformation.rulesOnMessagesApplyFailed",
Matchers.is(0));
+ .body("additionalInformation.rulesOnMessagesApplyFailed",
Matchers.is(0))
+ .body("additionalInformation.maximumAppliedActionExceeded",
Matchers.is(false))
+ .body("additionalInformation.processedMessagesCount",
Matchers.is(3));
+ }
+
+ @Test
+ void taskShouldStopAndCompleteWhenAppliedActionsExceedMaximumLimit()
throws Exception {
+ overrideTriageRulesRouteWithActionsLimit(2);
+
+ MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, MAILBOX_NAME);
+ MailboxPath otherMailboxPath = MailboxPath.forUser(USERNAME,
OTHER_MAILBOX_NAME);
+ MailboxSession systemSession =
mailboxManager.createSystemSession(USERNAME);
+
+ mailboxManager.createMailbox(mailboxPath, systemSession);
+ mailboxManager.createMailbox(otherMailboxPath, systemSession);
+
+ MessageManager messageManager = mailboxManager.getMailbox(mailboxPath,
systemSession);
+
+ // Add 20 matching messages, which exceeds the max actions of 2
+ IntStream.range(0, 20)
+ .forEach(Throwing.intConsumer(i -> messageManager.appendMessage(
+ MessageManager.AppendCommand.builder()
+ .build(Message.Builder.of()
+ .setSubject("plop")
+ .setFrom("[email protected]")
+ .setBody("matched mail", StandardCharsets.UTF_8)),
+ systemSession)));
+
+ MailboxId otherMailboxId = mailboxManager.getMailbox(otherMailboxPath,
systemSession).getId();
+
+ String taskId = given()
+ .queryParam("action", "triage")
+ .body(RULE_PAYLOAD.formatted(otherMailboxId.serialize()))
+ .post(MAILBOX_NAME + "/messages")
+ .then()
+ .statusCode(CREATED_201)
+ .extract()
+ .jsonPath()
+ .get("taskId");
+
+ given()
+ .basePath(TasksRoutes.BASE)
+ .when()
+ .get(taskId + "/await")
+ .then()
+ .body("status", Matchers.is("completed"))
+ .body("additionalInformation.rulesOnMessagesApplySuccessfully",
Matchers.lessThan(20))
+ .body("additionalInformation.rulesOnMessagesApplyFailed",
Matchers.is(0))
+ .body("additionalInformation.maximumAppliedActionExceeded",
Matchers.is(true));
+ }
+
+ @Test
+ void taskShouldCompleteWhenAppliedActionsReachMaximumLimit() throws
Exception {
+ overrideTriageRulesRouteWithActionsLimit(2);
+
+ MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, MAILBOX_NAME);
+ MailboxPath otherMailboxPath = MailboxPath.forUser(USERNAME,
OTHER_MAILBOX_NAME);
+ MailboxSession systemSession =
mailboxManager.createSystemSession(USERNAME);
+
+ mailboxManager.createMailbox(mailboxPath, systemSession);
+ mailboxManager.createMailbox(otherMailboxPath, systemSession);
+
+ MessageManager messageManager = mailboxManager.getMailbox(mailboxPath,
systemSession);
+
+ // Add 2 matching messages, reach the limit of 2
+ IntStream.range(0, 2)
+ .forEach(Throwing.intConsumer(i -> messageManager.appendMessage(
+ MessageManager.AppendCommand.builder()
+ .build(Message.Builder.of()
+ .setSubject("plop")
+ .setFrom("[email protected]")
+ .setBody("matched mail", StandardCharsets.UTF_8)),
+ systemSession)));
+
+ MailboxId otherMailboxId = mailboxManager.getMailbox(otherMailboxPath,
systemSession).getId();
+
+ String taskId = given()
+ .queryParam("action", "triage")
+ .body(RULE_PAYLOAD.formatted(otherMailboxId.serialize()))
+ .post(MAILBOX_NAME + "/messages")
+ .then()
+ .statusCode(CREATED_201)
+ .extract()
+ .jsonPath()
+ .get("taskId");
+
+ given()
+ .basePath(TasksRoutes.BASE)
+ .when()
+ .get(taskId + "/await")
+ .then()
+ .body("status", Matchers.is("completed"))
+ .body("additionalInformation.rulesOnMessagesApplySuccessfully",
Matchers.is(2))
+ .body("additionalInformation.rulesOnMessagesApplyFailed",
Matchers.is(0))
+ .body("additionalInformation.maximumAppliedActionExceeded",
Matchers.is(false));
+ }
+
+ @Test
+ void taskShouldCompleteWhenAppliedActionsLessThanMaximumLimit() throws
Exception {
+ overrideTriageRulesRouteWithActionsLimit(10);
+
+ MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, MAILBOX_NAME);
+ MailboxPath otherMailboxPath = MailboxPath.forUser(USERNAME,
OTHER_MAILBOX_NAME);
+ MailboxSession systemSession =
mailboxManager.createSystemSession(USERNAME);
+
+ mailboxManager.createMailbox(mailboxPath, systemSession);
+ mailboxManager.createMailbox(otherMailboxPath, systemSession);
+
+ MessageManager messageManager = mailboxManager.getMailbox(mailboxPath,
systemSession);
+
+ // Add 2 matching messages, < the limit of 10
+ IntStream.range(0, 2)
+ .forEach(Throwing.intConsumer(i -> messageManager.appendMessage(
+ MessageManager.AppendCommand.builder()
+ .build(Message.Builder.of()
+ .setSubject("plop")
+ .setFrom("[email protected]")
+ .setBody("matched mail", StandardCharsets.UTF_8)),
+ systemSession)));
+
+ MailboxId otherMailboxId = mailboxManager.getMailbox(otherMailboxPath,
systemSession).getId();
+
+ String taskId = given()
+ .queryParam("action", "triage")
+ .body(RULE_PAYLOAD.formatted(otherMailboxId.serialize()))
+ .post(MAILBOX_NAME + "/messages")
+ .then()
+ .statusCode(CREATED_201)
+ .extract()
+ .jsonPath()
+ .get("taskId");
+
+ given()
+ .basePath(TasksRoutes.BASE)
+ .when()
+ .get(taskId + "/await")
+ .then()
+ .body("status", Matchers.is("completed"))
+ .body("additionalInformation.rulesOnMessagesApplySuccessfully",
Matchers.is(2))
+ .body("additionalInformation.rulesOnMessagesApplyFailed",
Matchers.is(0))
+ .body("additionalInformation.maximumAppliedActionExceeded",
Matchers.is(false));
+ }
+
+ private void overrideTriageRulesRouteWithActionsLimit(int maxActionsLimit)
{
+ webAdminServer.destroy();
+ webAdminServer = WebAdminUtils.createWebAdminServer(
+ new RunRulesOnMailboxRoutes(usersRepository, mailboxManager,
taskManager, new JsonTransformer(),
+ new RunRulesOnMailboxService(mailboxManager, new
InMemoryId.Factory(), messageIdManager, maxActionsLimit)),
+ new TasksRoutes(taskManager, new JsonTransformer(),
+
DTOConverter.of(RunRulesOnMailboxTaskAdditionalInformationDTO.SERIALIZATION_MODULE)))
+ .start();
+
+ RestAssured.requestSpecification =
WebAdminUtils.buildRequestSpecification(webAdminServer)
+ .setBasePath(USERS_BASE + SEPARATOR + USERNAME.asString() +
SEPARATOR + UserMailboxesRoutes.MAILBOXES)
+ .setUrlEncodingEnabled(false)
+ .build();
}
}
diff --git
a/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTaskAdditionalInformationDTOTest.java
b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTaskAdditionalInformationDTOTest.java
index 0ce8572d5a..b6cfcc7a35 100644
---
a/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTaskAdditionalInformationDTOTest.java
+++
b/server/protocols/webadmin/webadmin-jmap/src/test/java/org/apache/james/webadmin/data/jmap/RunRulesOnMailboxTaskAdditionalInformationDTOTest.java
@@ -31,7 +31,7 @@ public class
RunRulesOnMailboxTaskAdditionalInformationDTOTest {
private static final Instant INSTANT =
Instant.parse("2007-12-03T10:15:30.00Z");
private static final RunRulesOnMailboxTask.AdditionalInformation
DOMAIN_OBJECT = new RunRulesOnMailboxTask.AdditionalInformation(
- Username.of("[email protected]"), new MailboxName("mbx1"), INSTANT, 10,
9);
+ Username.of("[email protected]"), new MailboxName("mbx1"), INSTANT, 10,
9, false, 30);
@Test
void shouldMatchJsonSerializationContract() throws Exception {
diff --git
a/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/runRulesOnMailbox.additionalInformation.json
b/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/runRulesOnMailbox.additionalInformation.json
index a3f8bd738b..6be4f207e4 100644
---
a/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/runRulesOnMailbox.additionalInformation.json
+++
b/server/protocols/webadmin/webadmin-jmap/src/test/resources/json/runRulesOnMailbox.additionalInformation.json
@@ -2,6 +2,8 @@
"mailboxName": "mbx1",
"rulesOnMessagesApplyFailed": 9,
"rulesOnMessagesApplySuccessfully": 10,
+ "processedMessagesCount": 30,
+ "maximumAppliedActionExceeded": false,
"timestamp": "2007-12-03T10:15:30Z",
"type": "RunRulesOnMailboxTask",
"username": "[email protected]"
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]