This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch 3.9.x in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 58013510fa8d6573b46deb2a8d740c94d8f9cbe1 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]
