You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by hq...@apache.org on 2026/10/16 08:03:32 UTC
(james-project) 03/07: JAMES-4236 JMAP: reject delayed sends the server cannot honour
This is an automated email from the ASF dual-hosted git repository.
quantranhong1999 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 45e99ab17883d949e0febcb0081071097faf5c54
Author: Min Nguyen <mi...@gmail.com>
AuthorDate: Tue Oct 13 15:51:58 2026 +0700
JAMES-4236 JMAP: reject delayed sends the server cannot honour
---
docs/modules/servers/partials/configure/jmap.adoc | 4 +-
.../sample-configuration/jmap.properties | 2 +-
.../sample-configuration/jmap.properties | 2 +-
.../java/org/apache/james/jmap/JMAPModule.java | 44 +++++++++++-
.../java/org/apache/james/jmap/JMAPModuleTest.java | 38 ++++++++++-
...istributedDelaySendsOnRabbitMQStartUpTest.java} | 59 ++++++----------
.../EmailSubmissionSetMethodContract.scala | 79 ++++++++++++++++++++++
...> PostgresDelaySendsOnRabbitMQStartUpTest.java} | 58 ++++++----------
.../jmap/method/EmailSubmissionSetMethod.scala | 11 +--
9 files changed, 210 insertions(+), 87 deletions(-)
diff --git a/docs/modules/servers/partials/configure/jmap.adoc b/docs/modules/servers/partials/configure/jmap.adoc
index 2eadeaa05e..9bf073725e 100644
--- a/docs/modules/servers/partials/configure/jmap.adoc
+++ b/docs/modules/servers/partials/configure/jmap.adoc
@@ -119,7 +119,9 @@ for JMAP filters. This projection optimizes reads, but needs to be correctly pop
systems with filters already defined would result in those filters to be not read.
| delay.sends.enabled
-| Optional boolean. Defaults to false. Whether to support or not the delay send with JMAP protocol.
+| Optional boolean. Defaults to false. Whether to support or not the delay send with JMAP protocol. When disabled,
+`EmailSubmission/set` rejects `holdFor` / `holdUntil`. Requires a mail queue supporting delays: the RabbitMQ mail
+queue does not, and the server refuses to start when this option is enabled on top of it.
| send.validate.rcpt
| Optional boolean. Defaults to false. Whether `EmailSubmission/set` rejects recipients James knows it cannot
diff --git a/server/apps/distributed-app/sample-configuration/jmap.properties b/server/apps/distributed-app/sample-configuration/jmap.properties
index fc37f1499c..0b27f829ed 100644
--- a/server/apps/distributed-app/sample-configuration/jmap.properties
+++ b/server/apps/distributed-app/sample-configuration/jmap.properties
@@ -6,7 +6,7 @@ enabled=true
tls.keystoreURL=file://conf/keystore
tls.secret=james72laBalle
-# only not work for RabbitMQ mail queue
+# Not supported by the RabbitMQ mail queue: the server refuses to start when enabled on top of it
#delay.sends.enabled=true
# Reject recipients James knows it cannot deliver to upon EmailSubmission/set, rather than bouncing
diff --git a/server/apps/postgres-app/sample-configuration/jmap.properties b/server/apps/postgres-app/sample-configuration/jmap.properties
index fc37f1499c..0b27f829ed 100644
--- a/server/apps/postgres-app/sample-configuration/jmap.properties
+++ b/server/apps/postgres-app/sample-configuration/jmap.properties
@@ -6,7 +6,7 @@ enabled=true
tls.keystoreURL=file://conf/keystore
tls.secret=james72laBalle
-# only not work for RabbitMQ mail queue
+# Not supported by the RabbitMQ mail queue: the server refuses to start when enabled on top of it
#delay.sends.enabled=true
# Reject recipients James knows it cannot deliver to upon EmailSubmission/set, rather than bouncing
diff --git a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
index b35544b0e8..bd71becc4f 100644
--- a/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
+++ b/server/container/guice/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPModule.java
@@ -62,6 +62,8 @@ import org.apache.james.mailbox.MailboxManager;
import org.apache.james.mailbox.MailboxManager.SearchCapabilities;
import org.apache.james.modules.server.MailetContainerModule;
import org.apache.james.modules.server.MailetContainerModule.ProcessorsCheck;
+import org.apache.james.queue.api.MailQueue;
+import org.apache.james.queue.api.MailQueueFactory;
import org.apache.james.queue.api.MailQueueItemDecoratorFactory;
import org.apache.james.server.core.configuration.FileConfigurationProvider;
import org.apache.james.transport.mailets.VacationMailet;
@@ -148,7 +150,9 @@ public class JMAPModule extends AbstractModule {
bind(JsoupHtmlTextExtractor.class).in(Scopes.SINGLETON);
bind(HtmlTextExtractor.class).to(JsoupHtmlTextExtractor.class);
- Multibinder.newSetBinder(binder(), StartUpCheck.class).addBinding().to(RequiredCapabilitiesStartUpCheck.class);
+ Multibinder<StartUpCheck> startUpChecks = Multibinder.newSetBinder(binder(), StartUpCheck.class);
+ startUpChecks.addBinding().to(RequiredCapabilitiesStartUpCheck.class);
+ startUpChecks.addBinding().to(DelaySendsStartUpCheck.class);
bind(MailQueueItemDecoratorFactory.class).to(PostDequeueDecoratorFactory.class).in(Scopes.SINGLETON);
@@ -319,4 +323,42 @@ public class JMAPModule extends AbstractModule {
return CHECK_NAME;
}
}
+
+ /**
+ * A mail queue ignoring delays would send delayed mails right away: refuse to start rather than
+ * advertising delayed sends we cannot honour.
+ */
+ @Singleton
+ public static class DelaySendsStartUpCheck implements StartUpCheck {
+ public static final String CHECK_NAME = "DelaySendsForJMAP";
+
+ private final boolean supportsDelaySends;
+ private final MailQueueFactory<? extends MailQueue> mailQueueFactory;
+
+ @Inject
+ public DelaySendsStartUpCheck(@Named("supportsDelaySends") boolean supportsDelaySends, MailQueueFactory<? extends MailQueue> mailQueueFactory) {
+ this.supportsDelaySends = supportsDelaySends;
+ this.mailQueueFactory = mailQueueFactory;
+ }
+
+ @Override
+ public CheckResult check() {
+ if (supportsDelaySends && !mailQueueFactory.supportsDelays()) {
+ return CheckResult.builder()
+ .checkName(checkName())
+ .resultType(ResultType.BAD)
+ .description("'delay.sends.enabled' is set in jmap.properties but the mail queue does not support delays")
+ .build();
+ }
+ return CheckResult.builder()
+ .checkName(checkName())
+ .resultType(ResultType.GOOD)
+ .build();
+ }
+
+ @Override
+ public String checkName() {
+ return CHECK_NAME;
+ }
+ }
}
diff --git a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPModuleTest.java b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPModuleTest.java
index 8192451e0b..49b414a803 100644
--- a/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPModuleTest.java
+++ b/server/container/guice/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPModuleTest.java
@@ -26,8 +26,10 @@ import static org.mockito.Mockito.when;
import java.util.EnumSet;
+import org.apache.james.jmap.JMAPModule.DelaySendsStartUpCheck;
import org.apache.james.jmap.JMAPModule.RequiredCapabilitiesStartUpCheck;
import org.apache.james.mailbox.MailboxManager;
+import org.apache.james.queue.api.MailQueueFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@@ -102,4 +104,38 @@ class JMAPModuleTest {
.isTrue();
}
}
-}
\ No newline at end of file
+
+ @Nested
+ class DelaySendsStartUpCheckTest {
+ private MailQueueFactory<?> mailQueueFactory;
+
+ @BeforeEach
+ void beforeEach() {
+ mailQueueFactory = mock(MailQueueFactory.class);
+ }
+
+ @Test
+ void checkShouldReturnBadWhenDelaySendsAreEnabledOnAQueueNotSupportingDelays() {
+ when(mailQueueFactory.supportsDelays()).thenReturn(false);
+
+ assertThat(new DelaySendsStartUpCheck(true, mailQueueFactory).check().isBad())
+ .isTrue();
+ }
+
+ @Test
+ void checkShouldReturnGoodWhenDelaySendsAreEnabledOnAQueueSupportingDelays() {
+ when(mailQueueFactory.supportsDelays()).thenReturn(true);
+
+ assertThat(new DelaySendsStartUpCheck(true, mailQueueFactory).check().isGood())
+ .isTrue();
+ }
+
+ @Test
+ void checkShouldReturnGoodWhenDelaySendsAreDisabled() {
+ when(mailQueueFactory.supportsDelays()).thenReturn(false);
+
+ assertThat(new DelaySendsStartUpCheck(false, mailQueueFactory).check().isGood())
+ .isTrue();
+ }
+ }
+}
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSubmissionSetMethodFutureReleaseTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedDelaySendsOnRabbitMQStartUpTest.java
similarity index 59%
rename from server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSubmissionSetMethodFutureReleaseTest.java
rename to server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedDelaySendsOnRabbitMQStartUpTest.java
index 3cd86a7e29..5e5bb48aad 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEmailSubmissionSetMethodFutureReleaseTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedDelaySendsOnRabbitMQStartUpTest.java
@@ -19,34 +19,32 @@
package org.apache.james.jmap.rfc8621.distributed;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
import org.apache.james.CassandraExtension;
import org.apache.james.CassandraRabbitMQJamesConfiguration;
import org.apache.james.CassandraRabbitMQJamesServerMain;
-import org.apache.james.ClockExtension;
import org.apache.james.DockerOpenSearchExtension;
import org.apache.james.GuiceJamesServer;
import org.apache.james.JamesServerBuilder;
import org.apache.james.JamesServerExtension;
import org.apache.james.MailQueueChoice;
import org.apache.james.SearchConfiguration;
-import org.apache.james.jmap.rfc8621.contract.EmailSubmissionSetMethodFutureReleaseContract;
-import org.apache.james.jmap.rfc8621.contract.probe.DelegationProbeModule;
-import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
-import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.StartUpChecksPerformer;
+import org.apache.james.jmap.JMAPModule.DelaySendsStartUpCheck;
+import org.apache.james.jmap.rfc8621.contract.tags.CategoryTags;
import org.apache.james.modules.AwsS3BlobStoreExtension;
import org.apache.james.modules.RabbitMQExtension;
import org.apache.james.modules.TestJMAPServerModule;
import org.apache.james.modules.blobstore.BlobStoreConfiguration;
-import org.apache.james.utils.UpdatableTickingClock;
-import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
-import com.datastax.oss.driver.api.core.uuid.Uuids;
-import com.google.inject.name.Names;
-
-public class DistributedEmailSubmissionSetMethodFutureReleaseTest implements EmailSubmissionSetMethodFutureReleaseContract {
- public static final CassandraMessageId.Factory MESSAGE_ID_FACTORY = new CassandraMessageId.Factory();
+import com.google.common.collect.ImmutableMap;
+class DistributedDelaySendsOnRabbitMQStartUpTest {
@RegisterExtension
static JamesServerExtension testExtension = new JamesServerBuilder<CassandraRabbitMQJamesConfiguration>(tmpDir ->
CassandraRabbitMQJamesConfiguration.builder()
@@ -65,35 +63,18 @@ public class DistributedEmailSubmissionSetMethodFutureReleaseTest implements Ema
.extension(new CassandraExtension())
.extension(new RabbitMQExtension())
.extension(new AwsS3BlobStoreExtension())
- .extension(new ClockExtension())
.server(configuration -> CassandraRabbitMQJamesServerMain.createServer(configuration)
- .overrideWith(new TestJMAPServerModule(), new DelegationProbeModule()))
- .overrideServerModule(binder -> binder.bind(Boolean.class).annotatedWith(Names.named("supportsDelaySends")).toInstance(true))
- .lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
+ .overrideWith(new TestJMAPServerModule(ImmutableMap.of("delay.sends.enabled", true))))
+ .disableAutoStart()
.build();
- @Override
- public MessageId randomMessageId() {
- return MESSAGE_ID_FACTORY.of(Uuids.timeBased());
- }
-
- @Disabled("Not work for distributed test")
- @Override
- public void emailSubmissionSetCreateShouldDeliverEmailWhenHoldForExpired(GuiceJamesServer server, UpdatableTickingClock updatableTickingClock){
- }
-
- @Disabled("Not work for distributed test")
- @Override
- public void emailSubmissionSetCreateShouldDeliverEmailWhenHoldUntilExpired(GuiceJamesServer server, UpdatableTickingClock updatableTickingClock){
- }
-
- @Disabled("Not work for distributed test")
- @Override
- public void emailSubmissionSetCreateShouldDelayEmailWithHoldFor(GuiceJamesServer server, UpdatableTickingClock updatableTickingClock){
- }
-
- @Disabled("Not work for distributed test")
- @Override
- public void emailSubmissionSetCreateShouldDelayEmailWithHoldUntil(GuiceJamesServer server, UpdatableTickingClock updatableTickingClock){
+ @Test
+ @Tag(CategoryTags.BASIC_FEATURE)
+ void serverShouldNotStartWhenDelaySendsAreEnabledOnTheRabbitMQMailQueue(GuiceJamesServer server) {
+ assertThatThrownBy(server::start)
+ .isInstanceOfSatisfying(StartUpChecksPerformer.StartUpChecksException.class,
+ e -> assertThat(e.getBadChecks())
+ .extracting(check -> check.getName())
+ .contains(DelaySendsStartUpCheck.CHECK_NAME));
}
}
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSubmissionSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSubmissionSetMethodContract.scala
index 879e41ffb4..f49151eff0 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSubmissionSetMethodContract.scala
+++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/EmailSubmissionSetMethodContract.scala
@@ -2483,4 +2483,83 @@ trait EmailSubmissionSetMethodContract {
.isObject
.containsKey("k1490")
}
+
+ private def appendDraft(server: GuiceJamesServer): MessageId = {
+ val message: Message = Message.Builder
+ .of
+ .setSubject("test")
+ .setSender(bobUsername.asString)
+ .setFrom(bobUsername.asString)
+ .setTo(andreUsername.asString)
+ .setBody("testmail", StandardCharsets.UTF_8)
+ .build
+
+ val bobDraftsPath = MailboxPath.forUser(bobUsername, DefaultMailboxes.DRAFTS)
+ server.getProbe(classOf[MailboxProbeImpl]).createMailbox(bobDraftsPath)
+ server.getProbe(classOf[MailboxProbeImpl]).appendMessage(bobUsername.asString(), bobDraftsPath, AppendCommand.builder()
+ .build(message))
+ .getMessageId
+ }
+
+ private def post(request: String): String =
+ `given`
+ .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER)
+ .body(request)
+ .when
+ .post
+ .`then`
+ .statusCode(SC_OK)
+ .contentType(JSON)
+ .extract
+ .body
+ .asString
+
+ private def creationWithParameters(messageId: MessageId, parameters: String): String =
+ s"""{
+ | "using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail", "urn:ietf:params:jmap:submission"],
+ | "methodCalls": [
+ | ["EmailSubmission/set", {
+ | "accountId": "$bobAccountId",
+ | "create": {
+ | "k1490": {
+ | "emailId": "${messageId.serialize}",
+ | "envelope": {
+ | "mailFrom": {"email": "${bobUsername.asString}", "parameters": $parameters},
+ | "rcptTo": [{"email": "${andreUsername.asString}"}]
+ | }
+ | }
+ | }
+ | }, "c1"]]
+ |}""".stripMargin
+
+ @Test
+ def holdForShouldBeRejectedWhenDelayedSendsAreNotSupported(server: GuiceJamesServer): Unit = {
+ val response = post(creationWithParameters(appendDraft(server), """{"holdFor": "20"}"""))
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].notCreated.k1490")
+ .isEqualTo("""{
+ | "type": "invalidArguments",
+ | "description": "Delayed sends are not supported"
+ |}""".stripMargin)
+ }
+
+ @Test
+ def holdUntilShouldBeRejectedWhenDelayedSendsAreNotSupported(server: GuiceJamesServer): Unit = {
+ val response = post(creationWithParameters(appendDraft(server), """{"holdUntil": "2100-01-01T00:00:00Z"}"""))
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].notCreated.k1490.type")
+ .isEqualTo("invalidArguments")
+ }
+
+ @Test
+ def zeroHoldForShouldBeAcceptedWhenDelayedSendsAreNotSupported(server: GuiceJamesServer): Unit = {
+ val response = post(creationWithParameters(appendDraft(server), """{"holdFor": "0"}"""))
+
+ assertThatJson(response)
+ .inPath("methodResponses[0][1].created")
+ .isObject
+ .containsKey("k1490")
+ }
}
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/postgres/PostgresEmailSubmissionSetMethodFutureReleaseTest.java b/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/postgres/PostgresDelaySendsOnRabbitMQStartUpTest.java
similarity index 58%
rename from server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/postgres/PostgresEmailSubmissionSetMethodFutureReleaseTest.java
rename to server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/postgres/PostgresDelaySendsOnRabbitMQStartUpTest.java
index 128ade1373..18877c7cab 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/postgres/PostgresEmailSubmissionSetMethodFutureReleaseTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/postgres-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/postgres/PostgresDelaySendsOnRabbitMQStartUpTest.java
@@ -20,31 +20,29 @@
package org.apache.james.jmap.rfc8621.postgres;
import static org.apache.james.data.UsersRepositoryModuleChooser.Implementation.DEFAULT;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import org.apache.james.ClockExtension;
import org.apache.james.GuiceJamesServer;
import org.apache.james.JamesServerBuilder;
import org.apache.james.JamesServerExtension;
import org.apache.james.PostgresJamesConfiguration;
import org.apache.james.PostgresJamesServerMain;
import org.apache.james.SearchConfiguration;
+import org.apache.james.StartUpChecksPerformer;
import org.apache.james.backends.postgres.PostgresExtension;
-import org.apache.james.jmap.rfc8621.contract.EmailSubmissionSetMethodFutureReleaseContract;
-import org.apache.james.jmap.rfc8621.contract.probe.DelegationProbeModule;
-import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.postgres.PostgresMessageId;
+import org.apache.james.jmap.JMAPModule.DelaySendsStartUpCheck;
+import org.apache.james.jmap.rfc8621.contract.tags.CategoryTags;
import org.apache.james.modules.RabbitMQExtension;
import org.apache.james.modules.TestJMAPServerModule;
import org.apache.james.modules.blobstore.BlobStoreConfiguration;
-import org.apache.james.utils.UpdatableTickingClock;
-import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
-import com.google.inject.name.Names;
-
-public class PostgresEmailSubmissionSetMethodFutureReleaseTest implements EmailSubmissionSetMethodFutureReleaseContract {
- public static final PostgresMessageId.Factory MESSAGE_ID_FACTORY = new PostgresMessageId.Factory();
+import com.google.common.collect.ImmutableMap;
+class PostgresDelaySendsOnRabbitMQStartUpTest {
@RegisterExtension
static JamesServerExtension testExtension = new JamesServerBuilder<PostgresJamesConfiguration>(tmpDir ->
PostgresJamesConfiguration.builder()
@@ -61,36 +59,18 @@ public class PostgresEmailSubmissionSetMethodFutureReleaseTest implements EmailS
.build())
.extension(PostgresExtension.empty())
.extension(new RabbitMQExtension())
- .extension(new ClockExtension())
.server(configuration -> PostgresJamesServerMain.createServer(configuration)
- .overrideWith(new TestJMAPServerModule())
- .overrideWith(new DelegationProbeModule()))
- .overrideServerModule(binder -> binder.bind(Boolean.class).annotatedWith(Names.named("supportsDelaySends")).toInstance(true))
- .lifeCycle(JamesServerExtension.Lifecycle.PER_CLASS)
+ .overrideWith(new TestJMAPServerModule(ImmutableMap.of("delay.sends.enabled", true))))
+ .disableAutoStart()
.build();
- @Override
- public MessageId randomMessageId() {
- return MESSAGE_ID_FACTORY.generate();
- }
-
- @Disabled("Not work for postgres test")
- @Override
- public void emailSubmissionSetCreateShouldDeliverEmailWhenHoldForExpired(GuiceJamesServer server, UpdatableTickingClock updatableTickingClock){
- }
-
- @Disabled("Not work for postgres test")
- @Override
- public void emailSubmissionSetCreateShouldDeliverEmailWhenHoldUntilExpired(GuiceJamesServer server, UpdatableTickingClock updatableTickingClock){
- }
-
- @Disabled("Not work for postgres test")
- @Override
- public void emailSubmissionSetCreateShouldDelayEmailWithHoldFor(GuiceJamesServer server, UpdatableTickingClock updatableTickingClock){
- }
-
- @Disabled("Not work for postgres test")
- @Override
- public void emailSubmissionSetCreateShouldDelayEmailWithHoldUntil(GuiceJamesServer server, UpdatableTickingClock updatableTickingClock){
+ @Test
+ @Tag(CategoryTags.BASIC_FEATURE)
+ void serverShouldNotStartWhenDelaySendsAreEnabledOnTheRabbitMQMailQueue(GuiceJamesServer server) {
+ assertThatThrownBy(server::start)
+ .isInstanceOfSatisfying(StartUpChecksPerformer.StartUpChecksException.class,
+ e -> assertThat(e.getBadChecks())
+ .extracting(check -> check.getName())
+ .contains(DelaySendsStartUpCheck.CHECK_NAME));
}
}
diff --git a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
index 716e90a334..9cc0cca669 100644
--- a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
+++ b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/method/EmailSubmissionSetMethod.scala
@@ -28,7 +28,7 @@ import com.google.common.collect.ImmutableMap
import eu.timepit.refined.auto._
import eu.timepit.refined.refineV
import jakarta.annotation.PreDestroy
-import jakarta.inject.Inject
+import jakarta.inject.{Inject, Named}
import jakarta.mail.Address
import jakarta.mail.Message.RecipientType
import jakarta.mail.internet.{InternetAddress, MimeMessage}
@@ -182,6 +182,7 @@ class EmailSubmissionSetMethod @Inject()(serializer: EmailSubmissionSetSerialize
emailSetMethod: EmailSetMethod,
clock: Clock,
javaValidations: java.util.Set[EmailSubmissionSetValidation],
+ @Named("supportsDelaySends") supportsDelaySends: Boolean,
val metricFactory: MetricFactory,
val sessionSupplier: SessionSupplier,
val sessionTranslator: SessionTranslator) extends MethodRequiringAccountId[EmailSubmissionSetRequest] with Startable {
@@ -375,10 +376,12 @@ class EmailSubmissionSetMethod @Inject()(serializer: EmailSubmissionSetSerialize
def validateDelay(delay: Duration): Try[Duration] =
- if (delay.getSeconds >= 0 && delay.getSeconds <= SubmissionCapabilityFactory.maximumDelays.getSeconds) {
- Success(delay)
- } else {
+ if (delay.getSeconds < 0 || delay.getSeconds > SubmissionCapabilityFactory.maximumDelays.getSeconds) {
Failure(new IllegalArgumentException("Invalid delayed time!"))
+ } else if (!delay.isZero && !supportsDelaySends) {
+ Failure(new IllegalArgumentException("Delayed sends are not supported"))
+ } else {
+ Success(delay)
}
def validateMimeMessages(mimeMessage: MimeMessage) : SMono[MimeMessage] = validateMailAddressHeaderMimeMessage(mimeMessage)
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org