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 1d7ab8ee5304daf6144e381934e9e06089290fcb Author: Trần Hồng Quân <[email protected]> AuthorDate: Fri Oct 3 14:09:03 2025 +0700 JAMES-4148 Implement ANY comparator for custom header (#2832) --- .../src/test/resources/json/event-v4.json | 5 ++ .../org/apache/james/jmap/api/filtering/Rule.java | 3 +- .../james/jmap/api/filtering/RuleFixture.java | 3 +- .../james/jmap/mailet/filter/ContentMatcher.java | 2 + .../jmap/mailet/filter/JMAPFilteringTest.java | 66 ++++++++++++++++++++++ .../data/jmap/RunRulesOnMailboxRoutesTest.java | 15 ++--- 6 files changed, 83 insertions(+), 11 deletions(-) diff --git a/server/data/data-jmap-cassandra/src/test/resources/json/event-v4.json b/server/data/data-jmap-cassandra/src/test/resources/json/event-v4.json index bb54fd57fa..63264aec34 100644 --- a/server/data/data-jmap-cassandra/src/test/resources/json/event-v4.json +++ b/server/data/data-jmap-cassandra/src/test/resources/json/event-v4.json @@ -18,6 +18,11 @@ "field": "header:custom", "comparator": "contains", "value": "another thing" + }, + { + "field": "header:anotherCustom", + "comparator": "any", + "value": "disregard me" } ] }, diff --git a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Rule.java b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Rule.java index bd64c8fb56..067f8b8a6a 100644 --- a/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Rule.java +++ b/server/data/data-jmap/src/main/java/org/apache/james/jmap/api/filtering/Rule.java @@ -189,7 +189,8 @@ public class Rule { NOT_EXACTLY_EQUALS("not-exactly-equals"), START_WITH("start-with"), IS_SET("isSet"), - IS_UNSET("isUnset"); + IS_UNSET("isUnset"), + ANY("any"); public static Optional<Comparator> find(String comparatorName) { return Arrays.stream(values()) diff --git a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/RuleFixture.java b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/RuleFixture.java index 3b946e3758..c3a93a5603 100644 --- a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/RuleFixture.java +++ b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/filtering/RuleFixture.java @@ -57,7 +57,8 @@ public interface RuleFixture { Rule RULE_3 = RULE_BUILDER.id(Rule.Id.of("3")).build(); Rule RULE_4 = Rule.builder() .conditionGroup(Rule.ConditionCombiner.AND, CONDITION, - Rule.Condition.of(new Rule.Condition.CustomHeaderField("custom"), Rule.Condition.Comparator.CONTAINS, "another thing")) + Rule.Condition.of(new Rule.Condition.CustomHeaderField("custom"), Rule.Condition.Comparator.CONTAINS, "another thing"), + Rule.Condition.of(new Rule.Condition.CustomHeaderField("anotherCustom"), Rule.Condition.Comparator.ANY, "disregard me")) .action(ACTION_2) .id(Rule.Id.of("1")) .name(NAME) diff --git a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java index 4ca7e5ca55..f0b55e3569 100644 --- a/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java +++ b/server/protocols/jmap-rfc-8621/src/main/java/org/apache/james/jmap/mailet/filter/ContentMatcher.java @@ -167,6 +167,7 @@ public interface ContentMatcher { ContentMatcher STRING_EXACTLY_EQUALS_MATCHER = (contents, valueToMatch) -> contents.anyMatch(content -> StringUtils.equals(content, valueToMatch)); ContentMatcher STRING_NOT_EXACTLY_EQUALS_MATCHER = negate(STRING_EXACTLY_EQUALS_MATCHER); ContentMatcher STRING_START_WITH_MATCHER = (contents, valueToMatch) -> contents.anyMatch(content -> content.startsWith(valueToMatch)); + ContentMatcher ANY_MATCHER = (contents, valueToMatch) -> contents.anyMatch(StringUtils::isNotBlank); ContentMatcher ADDRESS_CONTAINS_MATCHER = (contents, valueToMatch) -> contents .map(ContentMatcher::asAddressHeader) @@ -201,6 +202,7 @@ public interface ContentMatcher { .put(Rule.Condition.Comparator.EXACTLY_EQUALS, STRING_EXACTLY_EQUALS_MATCHER) .put(Rule.Condition.Comparator.NOT_EXACTLY_EQUALS, STRING_NOT_EXACTLY_EQUALS_MATCHER) .put(Rule.Condition.Comparator.START_WITH, STRING_START_WITH_MATCHER) + .put(Rule.Condition.Comparator.ANY, ANY_MATCHER) .build(); Map<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>> CONTENT_MATCHER_REGISTRY = ImmutableMap.<Rule.Condition.Field, Map<Rule.Condition.Comparator, ContentMatcher>>builder() diff --git a/server/protocols/jmap-rfc-8621/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java b/server/protocols/jmap-rfc-8621/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java index 7ea7ed1a5c..937c6fe14d 100644 --- a/server/protocols/jmap-rfc-8621/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java +++ b/server/protocols/jmap-rfc-8621/src/test/java/org/apache/james/jmap/mailet/filter/JMAPFilteringTest.java @@ -20,6 +20,7 @@ package org.apache.james.jmap.mailet.filter; import static org.apache.james.core.builder.MimeMessageBuilder.mimeMessageBuilder; +import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.ANY; import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.CONTAINS; import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.EXACTLY_EQUALS; import static org.apache.james.jmap.api.filtering.Rule.Condition.Comparator.IS_NEWER_THAN; @@ -1191,6 +1192,71 @@ class JMAPFilteringTest { .isEmpty(); } + @Test + void customHeaderShouldSupportAnyComparator(JMAPFilteringTestSystem testSystem) throws Exception { + Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME, + Optional.empty(), + Rule.builder() + .id(Rule.Id.of("1")) + .name("rule 1") + .conditionGroup(Rule.Condition.of(Rule.Condition.CustomHeaderField.find("header:X-Github").get(), ANY, "disregarded")) + .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(testSystem.getRecipient1MailboxId().serialize()))) + .build())).block(); + + FakeMail matchMail = testSystem.asMail(mimeMessageBuilder() + .addHeader(FROM.asString(), USER_2_ADDRESS) + .addHeader(SUBJECT.asString(), "subject") + .addHeader("X-Github", "I am Github header")); + + testSystem.getJmapFiltering().service(matchMail); + + assertThatAttribute(matchMail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME)) + .isEqualTo(RECIPIENT_1_MAILBOX_1_ATTRIBUTE); + } + + @Test + void nonExistingCustomHeaderShouldNotMatchAnyComparator(JMAPFilteringTestSystem testSystem) throws Exception { + Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME, + Optional.empty(), + Rule.builder() + .id(Rule.Id.of("1")) + .name("rule 1") + .conditionGroup(Rule.Condition.of(Rule.Condition.CustomHeaderField.find("header:X-Github").get(), ANY, "disregarded")) + .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(testSystem.getRecipient1MailboxId().serialize()))) + .build())).block(); + + FakeMail nonMatchMail = testSystem.asMail(mimeMessageBuilder() + .addHeader(FROM.asString(), USER_2_ADDRESS) + .addHeader(SUBJECT.asString(), "subject")); + + testSystem.getJmapFiltering().service(nonMatchMail); + + assertThat(nonMatchMail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME)) + .isEmpty(); + } + + @Test + void emptyCustomHeaderShouldNotMatchAnyComparator(JMAPFilteringTestSystem testSystem) throws Exception { + Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME, + Optional.empty(), + Rule.builder() + .id(Rule.Id.of("1")) + .name("rule 1") + .conditionGroup(Rule.Condition.of(Rule.Condition.CustomHeaderField.find("header:X-Github").get(), ANY, "disregarded")) + .action(Rule.Action.of(Rule.Action.AppendInMailboxes.withMailboxIds(testSystem.getRecipient1MailboxId().serialize()))) + .build())).block(); + + FakeMail nonMatchMail = testSystem.asMail(mimeMessageBuilder() + .addHeader(FROM.asString(), USER_2_ADDRESS) + .addHeader(SUBJECT.asString(), "subject") + .addHeader("X-Github", "")); + + testSystem.getJmapFiltering().service(nonMatchMail); + + assertThat(nonMatchMail.getAttribute(RECIPIENT_1_USERNAME_ATTRIBUTE_NAME)) + .isEmpty(); + } + @Test void orShouldNotMatchWhenNoConditionsAreMet(JMAPFilteringTestSystem testSystem) throws Exception { Mono.from(testSystem.getFilteringManagement().defineRulesForUser(RECIPIENT_1_USERNAME, 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 f65955ecc6..3d36c605f8 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 @@ -52,6 +52,7 @@ import org.apache.james.mailbox.inmemory.manager.InMemoryIntegrationResources; import org.apache.james.mailbox.model.MailboxId; import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mime4j.dom.Message; +import org.apache.james.mime4j.stream.RawField; import org.apache.james.task.Hostname; import org.apache.james.task.MemoryTaskManager; import org.apache.james.user.api.UsersRepository; @@ -329,7 +330,8 @@ public class RunRulesOnMailboxRoutesTest { .build(Message.Builder.of() .setSubject("plop") .setFrom("[email protected]") - .setBody("body", StandardCharsets.UTF_8)), + .setBody("body", StandardCharsets.UTF_8) + .addField(new RawField("X-Custom-Header", "value"))), systemSession); String taskId = given() @@ -354,14 +356,9 @@ public class RunRulesOnMailboxRoutesTest { "conditionCombiner": "OR", "conditions": [ { - "comparator": "contains", - "field": "subject", - "value": "plop" - }, - { - "comparator": "exactly-equals", - "field": "from", - "value": "[email protected]" + "comparator": "any", + "field": "header:X-Custom-Header", + "value": "disregarded" } ] } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
