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 99a8eac04ff211f27be5f299bf051062f3af1d0b Author: Quan Tran <[email protected]> AuthorDate: Tue Sep 30 17:07:06 2025 +0700 JAMES-4148 Webadmin rules triage task should support moveTo action --- .../data/jmap/RunRulesOnMailboxService.java | 33 ++- .../data/jmap/RunRulesOnMailboxRoutesTest.java | 303 +++++++++++++++++++++ 2 files changed, 327 insertions(+), 9 deletions(-) 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 0a5190dd03..1a64ccfeb3 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 @@ -33,6 +33,7 @@ import org.apache.james.jmap.mailet.filter.RuleMatcher; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MessageIdManager; +import org.apache.james.mailbox.MessageManager; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.model.FetchGroup; import org.apache.james.mailbox.model.MailboxId; @@ -46,6 +47,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.github.fge.lambdas.Throwing; +import com.google.common.collect.ImmutableList; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -103,24 +105,37 @@ public class RunRulesOnMailboxService { throw new NotImplementedException("Only action on moving messages is supported for now"); } - if (action.getAppendInMailboxes().getMailboxIds().isEmpty()) { + if (action.getAppendInMailboxes().getMailboxIds().isEmpty() && action.getMoveTo().isEmpty()) { throw new IllegalArgumentException("Move action should not be empty"); } } private Mono<Task.Result> appendInMailboxes(MessageId messageId, Rule.Action action, MailboxSession mailboxSession, RunRulesOnMailboxTask.Context context) { - List<MailboxId> mailboxIds = action.getAppendInMailboxes() + ImmutableList.Builder<MailboxId> mailboxIdsBuilder = ImmutableList.builder(); + List<MailboxId> appendInMailboxIds = action.getAppendInMailboxes() .getMailboxIds() .stream() .map(mailboxIdFactory::fromString) .toList(); + mailboxIdsBuilder.addAll(appendInMailboxIds); + + return Mono.justOrEmpty(action.getMoveTo()) + .flatMap(moveTo -> getMailboxId(mailboxSession, MailboxPath.forUser(mailboxSession.getUser(), moveTo.getMailboxName()))) + .map(moveToMailboxId -> mailboxIdsBuilder.add(moveToMailboxId).build()) + .switchIfEmpty(Mono.fromCallable(mailboxIdsBuilder::build)) + .flatMap(mailboxIds -> { + if (mailboxIds.isEmpty()) { + return Mono.just(Task.Result.COMPLETED); + } + + return Mono.from(messageIdManager.setInMailboxesReactive(messageId, mailboxIds, mailboxSession)) + .doOnSuccess(next -> context.incrementSuccesses()) + .then(Mono.just(Task.Result.COMPLETED)); + }); + } - if (mailboxIds.isEmpty()) { - return Mono.just(Task.Result.COMPLETED); - } - - return Mono.from(messageIdManager.setInMailboxesReactive(messageId, mailboxIds, mailboxSession)) - .doOnSuccess(next -> context.incrementSuccesses()) - .then(Mono.just(Task.Result.COMPLETED)); + private Mono<MailboxId> getMailboxId(MailboxSession mailboxSession, MailboxPath mailboxPath) { + return Mono.from(mailboxManager.getMailboxReactive(mailboxPath, mailboxSession)) + .map(MessageManager::getId); } } 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 3b6d0626c3..2950d2b5f8 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 @@ -72,6 +72,7 @@ public class RunRulesOnMailboxRoutesTest { private static final Username USERNAME = Username.of("username"); private static final String MAILBOX_NAME = "myMailboxName"; private static final String OTHER_MAILBOX_NAME = "myOtherMailboxName"; + private static final String MOVE_TO_MAILBOX_NAME = "moveToMailbox"; private static final String ERROR_TYPE_NOTFOUND = "notFound"; private static final String ERROR_TYPE_INVALIDARGUMENT = "InvalidArgument"; private static final String RULE_PAYLOAD = """ @@ -311,6 +312,308 @@ public class RunRulesOnMailboxRoutesTest { ); } + @Test + void runRulesOnMailboxShouldSupportMoveToMailboxNameWhenMatchingMessage() throws Exception { + 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); + + mailboxManager.getMailbox(mailboxPath, systemSession) + .appendMessage(MessageManager.AppendCommand.builder() + .build(Message.Builder.of() + .setSubject("plop") + .setFrom("[email protected]") + .setBody("body", StandardCharsets.UTF_8)), + systemSession); + + String taskId = given() + .queryParam("action", "triage") + .body(""" + { + "id": "1", + "name": "rule 1", + "action": { + "appendIn": { + "mailboxIds": [] + }, + "moveTo": { + "mailboxName": "%s" + }, + "important": false, + "keyworkds": [], + "reject": false, + "seen": false + }, + "conditionGroup": { + "conditionCombiner": "OR", + "conditions": [ + { + "comparator": "contains", + "field": "subject", + "value": "plop" + }, + { + "comparator": "exactly-equals", + "field": "from", + "value": "[email protected]" + } + ] + } + }""" + .formatted(OTHER_MAILBOX_NAME)) + .post(MAILBOX_NAME + "/messages") + .then() + .statusCode(CREATED_201) + .extract() + .jsonPath() + .get("taskId"); + + given() + .basePath(TasksRoutes.BASE) + .when() + .get(taskId + "/await"); + + SoftAssertions.assertSoftly( + softly -> { + softly.assertThat(Throwing.supplier(() -> mailboxManager.getMailbox(mailboxPath, systemSession).getMailboxCounters(systemSession).getCount()).get()) + .isEqualTo(0); + softly.assertThat(Throwing.supplier(() -> mailboxManager.getMailbox(otherMailboxPath, systemSession).getMailboxCounters(systemSession).getCount()).get()) + .isEqualTo(1); + } + ); + } + + @Test + void runRulesOnMailboxShouldNotMoveToMailboxNameWhenNonMatchingMessage() throws Exception { + 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); + + mailboxManager.getMailbox(mailboxPath, systemSession) + .appendMessage(MessageManager.AppendCommand.builder() + .build(Message.Builder.of() + .setSubject("not match rules") + .setFrom("[email protected]") + .setBody("body", StandardCharsets.UTF_8)), + systemSession); + + String taskId = given() + .queryParam("action", "triage") + .body(""" + { + "id": "1", + "name": "rule 1", + "action": { + "appendIn": { + "mailboxIds": [] + }, + "moveTo": { + "mailboxName": "%s" + }, + "important": false, + "keyworkds": [], + "reject": false, + "seen": false + }, + "conditionGroup": { + "conditionCombiner": "OR", + "conditions": [ + { + "comparator": "contains", + "field": "subject", + "value": "plop" + }, + { + "comparator": "exactly-equals", + "field": "from", + "value": "[email protected]" + } + ] + } + }""" + .formatted(OTHER_MAILBOX_NAME)) + .post(MAILBOX_NAME + "/messages") + .then() + .statusCode(CREATED_201) + .extract() + .jsonPath() + .get("taskId"); + + given() + .basePath(TasksRoutes.BASE) + .when() + .get(taskId + "/await"); + + SoftAssertions.assertSoftly( + softly -> { + softly.assertThat(Throwing.supplier(() -> mailboxManager.getMailbox(mailboxPath, systemSession).getMailboxCounters(systemSession).getCount()).get()) + .isEqualTo(1); + softly.assertThat(Throwing.supplier(() -> mailboxManager.getMailbox(otherMailboxPath, systemSession).getMailboxCounters(systemSession).getCount()).get()) + .isEqualTo(0); + } + ); + } + + @Test + void bothMoveToAndAppendInMailboxesShouldWork() throws Exception { + MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, MAILBOX_NAME); + MailboxPath appendIdMailboxPath = MailboxPath.forUser(USERNAME, OTHER_MAILBOX_NAME); + MailboxPath moveToMailboxPath = MailboxPath.forUser(USERNAME, MOVE_TO_MAILBOX_NAME); + MailboxSession systemSession = mailboxManager.createSystemSession(USERNAME); + + mailboxManager.createMailbox(mailboxPath, systemSession); + mailboxManager.createMailbox(appendIdMailboxPath, systemSession); + mailboxManager.createMailbox(moveToMailboxPath, systemSession); + MailboxId appendIdMailboxId = mailboxManager.getMailbox(appendIdMailboxPath, systemSession).getId(); + + mailboxManager.getMailbox(mailboxPath, systemSession) + .appendMessage(MessageManager.AppendCommand.builder() + .build(Message.Builder.of() + .setSubject("plop") + .setFrom("[email protected]") + .setBody("body", StandardCharsets.UTF_8)), + systemSession); + + String taskId = given() + .queryParam("action", "triage") + .body(""" + { + "id": "1", + "name": "rule 1", + "action": { + "appendIn": { + "mailboxIds": ["%s"] + }, + "moveTo": { + "mailboxName": "%s" + }, + "important": false, + "keyworkds": [], + "reject": false, + "seen": false + }, + "conditionGroup": { + "conditionCombiner": "OR", + "conditions": [ + { + "comparator": "contains", + "field": "subject", + "value": "plop" + }, + { + "comparator": "exactly-equals", + "field": "from", + "value": "[email protected]" + } + ] + } + }""" + .formatted(appendIdMailboxId.serialize(), MOVE_TO_MAILBOX_NAME)) + .post(MAILBOX_NAME + "/messages") + .then() + .statusCode(CREATED_201) + .extract() + .jsonPath() + .get("taskId"); + + given() + .basePath(TasksRoutes.BASE) + .when() + .get(taskId + "/await"); + + SoftAssertions.assertSoftly( + softly -> { + softly.assertThat(Throwing.supplier(() -> mailboxManager.getMailbox(mailboxPath, systemSession).getMailboxCounters(systemSession).getCount()).get()) + .isEqualTo(0); + softly.assertThat(Throwing.supplier(() -> mailboxManager.getMailbox(appendIdMailboxPath, systemSession).getMailboxCounters(systemSession).getCount()).get()) + .isEqualTo(1); + softly.assertThat(Throwing.supplier(() -> mailboxManager.getMailbox(moveToMailboxPath, systemSession).getMailboxCounters(systemSession).getCount()).get()) + .isEqualTo(1); + } + ); + } + + @Test + void bothMoveToAndAppendInMailboxesShouldNotDuplicateMessageWhenTheSameTargetMailbox() throws Exception { + MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, MAILBOX_NAME); + MailboxPath targetMailboxPath = MailboxPath.forUser(USERNAME, OTHER_MAILBOX_NAME); + MailboxSession systemSession = mailboxManager.createSystemSession(USERNAME); + + mailboxManager.createMailbox(mailboxPath, systemSession); + mailboxManager.createMailbox(targetMailboxPath, systemSession); + MailboxId targetMailboxId = mailboxManager.getMailbox(targetMailboxPath, systemSession).getId(); + + mailboxManager.getMailbox(mailboxPath, systemSession) + .appendMessage(MessageManager.AppendCommand.builder() + .build(Message.Builder.of() + .setSubject("plop") + .setFrom("[email protected]") + .setBody("body", StandardCharsets.UTF_8)), + systemSession); + + String taskId = given() + .queryParam("action", "triage") + .body(""" + { + "id": "1", + "name": "rule 1", + "action": { + "appendIn": { + "mailboxIds": ["%s"] + }, + "moveTo": { + "mailboxName": "%s" + }, + "important": false, + "keyworkds": [], + "reject": false, + "seen": false + }, + "conditionGroup": { + "conditionCombiner": "OR", + "conditions": [ + { + "comparator": "contains", + "field": "subject", + "value": "plop" + }, + { + "comparator": "exactly-equals", + "field": "from", + "value": "[email protected]" + } + ] + } + }""" + .formatted(targetMailboxId.serialize(), OTHER_MAILBOX_NAME)) + .post(MAILBOX_NAME + "/messages") + .then() + .statusCode(CREATED_201) + .extract() + .jsonPath() + .get("taskId"); + + given() + .basePath(TasksRoutes.BASE) + .when() + .get(taskId + "/await"); + + SoftAssertions.assertSoftly( + softly -> { + softly.assertThat(Throwing.supplier(() -> mailboxManager.getMailbox(mailboxPath, systemSession).getMailboxCounters(systemSession).getCount()).get()) + .isEqualTo(0); + softly.assertThat(Throwing.supplier(() -> mailboxManager.getMailbox(targetMailboxPath, systemSession).getMailboxCounters(systemSession).getCount()).get()) + .isEqualTo(1); + } + ); + } + @Test void runRulesShouldApplyDateCrieria() throws Exception { MailboxPath mailboxPath = MailboxPath.forUser(USERNAME, MAILBOX_NAME); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
