This is an automated email from the ASF dual-hosted git repository. rcordier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 3c786721dff6fa9ecc61e2fb141f84369eafda27 Author: duc91 <[email protected]> AuthorDate: Wed Jul 8 15:57:50 2020 +0700 JAMES-3295 handle configuration AtMostMatcher with differents config --- .../apache/james/transport/matchers/AtMost.java | 57 +++++- .../james/transport/matchers/AtMostTest.java | 221 ++++++++++++++------- 2 files changed, 196 insertions(+), 82 deletions(-) diff --git a/mailet/standard/src/main/java/org/apache/james/transport/matchers/AtMost.java b/mailet/standard/src/main/java/org/apache/james/transport/matchers/AtMost.java index f3fee35..6f5b72a 100644 --- a/mailet/standard/src/main/java/org/apache/james/transport/matchers/AtMost.java +++ b/mailet/standard/src/main/java/org/apache/james/transport/matchers/AtMost.java @@ -20,10 +20,12 @@ package org.apache.james.transport.matchers; import java.util.Collection; +import java.util.List; import java.util.Optional; import javax.mail.MessagingException; +import org.apache.commons.lang3.StringUtils; import org.apache.james.core.MailAddress; import org.apache.mailet.Attribute; import org.apache.mailet.AttributeName; @@ -33,39 +35,80 @@ import org.apache.mailet.Mail; import org.apache.mailet.base.GenericMatcher; import org.apache.mailet.base.MailetUtil; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; /** * Checks that a mail did at most X executions on a specific operation. * - * If no executions have been performed previously, it sets up an attribute `AT_MOST_EXECUTIONS` - * in the mail that will be incremented every time the check succeeds. + * <p> If no executions have been performed previously for Y attribute, it will be set up.</p> + * <p> In the mail, every time the check succeeds, its counter will be incremented by one. + * The check fails when the defined X limit is reached.</p> * - * The check fails when the defined X limit is reached. + * <ul> + * <li>X - count of how many times a specific operation is performed</li> + * <li>Y - name of attribute represented for specific operation executions, default value is: <i>AT_MOST_EXECUTIONS</i></li> + * </ul> * * <p>The example below will match a mail with at most 3 executions on the mailet</p> + * with attribute name <i>AT_MOST_EXECUTIONS</i></p> * * <pre><code> - * <mailet match="AtMost=3" class="<any-class>"> + * <mailet match="AtMost=AT_MOST_EXECUTIONS:3" class="<any-class>"> * </mailet> * </code></pre> */ public class AtMost extends GenericMatcher { static final AttributeName AT_MOST_EXECUTIONS = AttributeName.of("AT_MOST_EXECUTIONS"); + private static final String CONDITION_SEPARATOR = ":"; + private static final int ONLY_CONDITION_VALUE = 1; + private static final int CONDITION_NAME_AND_VALUE = 2; + + private AttributeName attributeName; private Integer atMostExecutions; @Override public void init() throws MessagingException { - this.atMostExecutions = MailetUtil.getInitParameterAsStrictlyPositiveInteger(getCondition()); + String conditionConfig = getMatcherConfig().getCondition(); + Preconditions.checkArgument(StringUtils.isNotBlank(conditionConfig), "MatcherConfiguration is mandatory!"); + Preconditions.checkArgument(!conditionConfig.startsWith(CONDITION_SEPARATOR), + "MatcherConfiguration can not start with '%s'", CONDITION_SEPARATOR); + + List<String> conditions = Splitter.on(CONDITION_SEPARATOR).splitToList(conditionConfig); + attributeName = parseAttribute(conditions); + atMostExecutions = parseAttributeValue(conditions); + } + + private AttributeName parseAttribute(List<String> conditions) { + switch (conditions.size()) { + case ONLY_CONDITION_VALUE: + return AT_MOST_EXECUTIONS; + case CONDITION_NAME_AND_VALUE: + return AttributeName.of(conditions.get(0)); + default: + throw new IllegalArgumentException("MatcherConfiguration format should follow: 'name:value' or 'value'"); + } + } + + private Integer parseAttributeValue(List<String> conditions) throws MessagingException { + switch (conditions.size()) { + case ONLY_CONDITION_VALUE: + return MailetUtil.getInitParameterAsStrictlyPositiveInteger(conditions.get(0)); + case CONDITION_NAME_AND_VALUE: + return MailetUtil.getInitParameterAsStrictlyPositiveInteger(conditions.get(1)); + default: + throw new IllegalArgumentException("MatcherConfiguration format should follow: 'name:value' or 'value'"); + } } @Override public Collection<MailAddress> match(Mail mail) throws MessagingException { - return AttributeUtils.getValueAndCastFromMail(mail, AT_MOST_EXECUTIONS, Integer.class) + return AttributeUtils.getValueAndCastFromMail(mail, attributeName, Integer.class) .or(() -> Optional.of(0)) .filter(executions -> executions < atMostExecutions) .map(executions -> { - mail.setAttribute(new Attribute(AT_MOST_EXECUTIONS, AttributeValue.of(executions + 1))); + mail.setAttribute(new Attribute(attributeName, AttributeValue.of(executions + 1))); return mail.getRecipients(); }) .orElse(ImmutableList.of()); diff --git a/mailet/standard/src/test/java/org/apache/james/transport/matchers/AtMostTest.java b/mailet/standard/src/test/java/org/apache/james/transport/matchers/AtMostTest.java index 3be5f12..355cd08 100644 --- a/mailet/standard/src/test/java/org/apache/james/transport/matchers/AtMostTest.java +++ b/mailet/standard/src/test/java/org/apache/james/transport/matchers/AtMostTest.java @@ -30,12 +30,14 @@ import javax.mail.MessagingException; import org.apache.james.core.MailAddress; import org.apache.mailet.Attribute; +import org.apache.mailet.AttributeName; import org.apache.mailet.AttributeValue; import org.apache.mailet.Mail; import org.apache.mailet.base.test.FakeMail; import org.apache.mailet.base.test.FakeMatcherConfig; import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import com.github.fge.lambdas.Throwing; @@ -59,100 +61,169 @@ class AtMostTest { .matcherName("AtMost") .condition(CONDITION) .build(); - matcher.init(matcherConfig); } - @Test - void shouldMatchWhenAttributeNotSet() throws MessagingException { - Mail mail = createMail(); - - Collection<MailAddress> actual = matcher.match(mail); - - assertThat(actual).containsOnly(RECIPIENT1); + @Nested + class InvalidConditionConfigurationTest { + @Test + void shouldThrowWhenMatchersConfigWithoutConditionValue() { + assertThatThrownBy(() -> new AtMost().init(FakeMatcherConfig.builder() + .matcherName("NoValueMatcher") + .condition("randomName:") + .build())) + .isInstanceOf(MessagingException.class); + } + + @Test + void shouldThrowWhenMatchersConfigWithInvalidConditionValue() { + assertThatThrownBy(() -> new AtMost().init(FakeMatcherConfig.builder() + .matcherName("NoValueMatcher") + .condition("value") + .build())) + .isInstanceOf(MessagingException.class); + } + + @Test + void shouldThrowWhenMatchersConfigWithoutConditionName() { + assertThatThrownBy(() -> new AtMost().init(FakeMatcherConfig.builder() + .matcherName("NoValueMatcher") + .condition(":3") + .build())) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void shouldThrowWhenMatchersConfigNameAsSpace() { + assertThatThrownBy(() -> new AtMost().init(FakeMatcherConfig.builder() + .matcherName("NoValueMatcher") + .condition(" : ") + .build())) + .isInstanceOf(MessagingException.class); + } + + + @Test + void shouldThrowWithEmptyCondition() { + FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder() + .matcherName("AtMost") + .build(); + + assertThatThrownBy(() -> matcher.init(matcherConfig)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void shouldThrowWithNegativeCondition() { + FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder() + .matcherName("AtMost") + .condition("-1") + .build(); + + assertThatThrownBy(() -> matcher.init(matcherConfig)) + .isInstanceOf(MessagingException.class); + } + + @Test + void shouldThrowWithConditionToZero() { + FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder() + .matcherName("AtMost") + .condition("0") + .build(); + + assertThatThrownBy(() -> matcher.init(matcherConfig)) + .isInstanceOf(MessagingException.class); + } } - @Test - void shouldMatchWhenNoRetries() throws MessagingException { - Mail mail = createMail(); - mail.setAttribute(new Attribute(AT_MOST_EXECUTIONS, AttributeValue.of(0))); - - Collection<MailAddress> actual = matcher.match(mail); - - assertThat(actual).containsOnly(RECIPIENT1); + @Nested + class MultipleMatchersConfigurationTest { + private AtMost atMost2; + private AtMost atMost3; + + @BeforeEach + void setup() throws MessagingException { + this.atMost2 = new AtMost(); + atMost2.init( + FakeMatcherConfig.builder() + .matcherName("AtMost") + .condition("AtMost2:2") + .build()); + + this.atMost3 = new AtMost(); + atMost3.init( + FakeMatcherConfig.builder() + .matcherName("AtMost") + .condition("AtMost3:2") + .build()); + } + + @Test + void matchersShouldStopWhenAMatcherReachedLimit() throws MessagingException { + Mail mail1 = createMail(); + + SoftAssertions.assertSoftly(Throwing.consumer( + softly -> { + softly.assertThat(atMost2.match(mail1)).containsOnly(RECIPIENT1); + softly.assertThat(atMost2.match(mail1)).containsOnly(RECIPIENT1); + softly.assertThat(atMost2.match(mail1)).isEmpty(); + softly.assertThat(atMost3.match(mail1)).containsOnly(RECIPIENT1); + softly.assertThat(atMost3.match(mail1)).containsOnly(RECIPIENT1); + softly.assertThat(atMost3.match(mail1)).isEmpty(); + })); + } } - @Test - void shouldNotMatchWhenOverAtMost() throws MessagingException { - Mail mail = createMail(); - mail.setAttribute(new Attribute(AT_MOST_EXECUTIONS, AttributeValue.of(3))); + @Nested + class SingleMatcherConfigurationTest { + @Test + void shouldMatchWhenAttributeNotSet() throws MessagingException { + Mail mail = createMail(); - Collection<MailAddress> actual = matcher.match(mail); + Collection<MailAddress> actual = matcher.match(mail); - assertThat(actual).isEmpty(); - } + assertThat(actual).containsOnly(RECIPIENT1); + } - @Test - void shouldNotMatchWhenEqualToAtMost() throws MessagingException { - Mail mail = createMail(); - mail.setAttribute(new Attribute(AT_MOST_EXECUTIONS, AttributeValue.of(2))); + @Test + void shouldMatchWhenNoRetries() throws MessagingException { + Mail mail = createMail(); + mail.setAttribute(new Attribute(AT_MOST_EXECUTIONS, AttributeValue.of(0))); - Collection<MailAddress> actual = matcher.match(mail); + Collection<MailAddress> actual = matcher.match(mail); - assertThat(actual).isEmpty(); - } + assertThat(actual).containsOnly(RECIPIENT1); + } - @Test - void shouldThrowWithEmptyCondition() { - FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder() - .matcherName("AtMost") - .build(); + @Test + void shouldNotMatchWhenOverAtMost() throws MessagingException { + Mail mail = createMail(); + mail.setAttribute(new Attribute(AT_MOST_EXECUTIONS, AttributeValue.of(3))); - assertThatThrownBy(() -> matcher.init(matcherConfig)) - .isInstanceOf(MessagingException.class); - } + Collection<MailAddress> actual = matcher.match(mail); - @Test - void shouldThrowWithInvalidCondition() { - FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder() - .matcherName("AtMost") - .condition("invalid") - .build(); - - assertThatThrownBy(() -> matcher.init(matcherConfig)) - .isInstanceOf(MessagingException.class); - } - - @Test - void shouldThrowWithNegativeCondition() { - FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder() - .matcherName("AtMost") - .condition("-1") - .build(); + assertThat(actual).isEmpty(); + } - assertThatThrownBy(() -> matcher.init(matcherConfig)) - .isInstanceOf(MessagingException.class); - } + @Test + void shouldNotMatchWhenEqualToAtMost() throws MessagingException { + Mail mail = createMail(); + mail.setAttribute(new Attribute(AT_MOST_EXECUTIONS, AttributeValue.of(2))); - @Test - void shouldThrowWithConditionToZero() { - FakeMatcherConfig matcherConfig = FakeMatcherConfig.builder() - .matcherName("AtMost") - .condition("0") - .build(); + Collection<MailAddress> actual = matcher.match(mail); - assertThatThrownBy(() -> matcher.init(matcherConfig)) - .isInstanceOf(MessagingException.class); - } + assertThat(actual).isEmpty(); + } - @Test - void shouldMatchUntilOverAtMost() throws MessagingException { - Mail mail = createMail(); + @Test + void shouldMatchUntilOverAtMost() throws MessagingException { + Mail mail = createMail(); - SoftAssertions.assertSoftly(Throwing.consumer(softly -> { - softly.assertThat(matcher.match(mail)).describedAs("First execution").contains(RECIPIENT1); - softly.assertThat(matcher.match(mail)).describedAs("Second execution").contains(RECIPIENT1); - softly.assertThat(matcher.match(mail)).describedAs("Third execution").isEmpty(); - })); + SoftAssertions.assertSoftly(Throwing.consumer(softly -> { + softly.assertThat(matcher.match(mail)).describedAs("First execution").contains(RECIPIENT1); + softly.assertThat(matcher.match(mail)).describedAs("Second execution").contains(RECIPIENT1); + softly.assertThat(matcher.match(mail)).describedAs("Third execution").isEmpty(); + })); + } } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
