diff --git a/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/Mail.java b/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/Mail.java index 8366dd212272e..2b26e5613b815 100644 --- a/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/Mail.java +++ b/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/Mail.java @@ -17,7 +17,7 @@ public class Mail { private List bcc = new ArrayList<>(); private List cc = new ArrayList<>(); private String from; - private String replyTo; + private List replyTo = new ArrayList<>(); private String bounceAddress; private String subject; private String text; @@ -163,10 +163,27 @@ public Mail setFrom(String from) { } /** - * @return the reply-to address. + * @return the reply-to address. In the case of multiple addresses, the comma-separated list is returned, following + * the https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.2 recommendation. If no reply-to address has been + * set, it returns {@code null}. */ public String getReplyTo() { - return replyTo; + if (replyTo == null || replyTo.isEmpty()) { + return null; + } + return String.join(",", replyTo); + } + + /** + * Adds a reply-to address. + * + * @param replyTo the address to use as reply-to. Must be a valid email address. + * @return the current {@link Mail} + * @see #setReplyTo(String) + */ + public Mail addReplyTo(String replyTo) { + this.replyTo.add(replyTo); + return this; } /** @@ -174,9 +191,24 @@ public String getReplyTo() { * * @param replyTo the address to use as reply-to. Must be a valid email address. * @return the current {@link Mail} + * @see #setReplyTo(String[]) */ public Mail setReplyTo(String replyTo) { - this.replyTo = replyTo; + this.replyTo.clear(); + this.replyTo.add(replyTo); + return this; + } + + /** + * Sets the reply-to addresses. + * + * @param replyTo the addresses to use as reply-to. Must contain valid email addresses, must contain at least + * one address. + * @return the current {@link Mail} + */ + public Mail setReplyTo(String... replyTo) { + this.replyTo.clear(); + Collections.addAll(this.replyTo, replyTo); return this; } @@ -436,7 +468,8 @@ public Mail addAttachment(String name, byte[] data, String contentType, String d * @param disposition the disposition of the attachment * @return the current {@link Mail} */ - public Mail addAttachment(String name, Publisher data, String contentType, String description, String disposition) { + public Mail addAttachment(String name, Publisher data, String contentType, String description, + String disposition) { this.attachments.add(new Attachment(name, data, contentType, description, disposition)); return this; } diff --git a/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/MailTemplate.java b/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/MailTemplate.java index 8df9f5142cd86..d2694c2c8bd06 100644 --- a/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/MailTemplate.java +++ b/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/MailTemplate.java @@ -48,6 +48,8 @@ interface MailTemplateInstance { MailTemplateInstance replyTo(String replyTo); + MailTemplateInstance replyTo(String... replyTo); + MailTemplateInstance bounceAddress(String bounceAddress); MailTemplateInstance addInlineAttachment(String name, File file, String contentType, String contentId); diff --git a/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MailTemplateInstanceImpl.java b/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MailTemplateInstanceImpl.java index 30bbaf17ba0d4..6c6e1d76f7051 100644 --- a/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MailTemplateInstanceImpl.java +++ b/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MailTemplateInstanceImpl.java @@ -73,6 +73,12 @@ public MailTemplateInstance replyTo(String replyTo) { return this; } + @Override + public MailTemplateInstance replyTo(String... replyTo) { + this.mail.setReplyTo(replyTo); + return this; + } + @Override public MailTemplateInstance bounceAddress(String bounceAddress) { this.mail.setBounceAddress(bounceAddress); diff --git a/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MutinyMailerImpl.java b/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MutinyMailerImpl.java index e618a29f1854e..28e7d2ad2ba06 100644 --- a/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MutinyMailerImpl.java +++ b/extensions/mailer/runtime/src/main/java/io/quarkus/mailer/runtime/MutinyMailerImpl.java @@ -105,6 +105,7 @@ private Uni toMailMessage(Mail mail) { message.setHtml(mail.getHtml()); message.setHeaders(toMultimap(mail.getHeaders())); if (mail.getReplyTo() != null) { + // getReplyTo produces the comma-separated list. message.addHeader("Reply-To", mail.getReplyTo()); } diff --git a/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/MailTest.java b/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/MailTest.java index 6a8226dc37074..cd7b1b9fe97ee 100644 --- a/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/MailTest.java +++ b/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/MailTest.java @@ -171,4 +171,32 @@ void testHeaders() { assertThat(mail.getHeaders()).isEmpty(); } + @Test + void testMultipleReplyTo() { + Mail mail = new Mail().addTo(TO_ADDRESS).setSubject("test").setText(BEGINNING) + .setFrom("from@quarkus.io") + .setReplyTo("reply-to@quarkus.io", "another@quarkus.io") + .setBounceAddress("bounce@quarkus.io"); + assertThat(mail.getTo()).containsExactly(TO_ADDRESS); + assertThat(mail.getSubject()).isEqualTo("test"); + assertThat(mail.getText()).isEqualTo(BEGINNING); + assertThat(mail.getHtml()).isNull(); + assertThat(mail.getFrom()).isEqualTo("from@quarkus.io"); + assertThat(mail.getReplyTo()).isEqualTo("reply-to@quarkus.io,another@quarkus.io"); + assertThat(mail.getBounceAddress()).isEqualTo("bounce@quarkus.io"); + + mail = new Mail().addTo(TO_ADDRESS).setSubject("test").setText(BEGINNING) + .setFrom("from@quarkus.io") + .addReplyTo("another@quarkus.io") + .addReplyTo("reply-to@quarkus.io") + .setBounceAddress("bounce@quarkus.io"); + assertThat(mail.getTo()).containsExactly(TO_ADDRESS); + assertThat(mail.getSubject()).isEqualTo("test"); + assertThat(mail.getText()).isEqualTo(BEGINNING); + assertThat(mail.getHtml()).isNull(); + assertThat(mail.getFrom()).isEqualTo("from@quarkus.io"); + assertThat(mail.getReplyTo()).isEqualTo("another@quarkus.io,reply-to@quarkus.io"); + assertThat(mail.getBounceAddress()).isEqualTo("bounce@quarkus.io"); + } + } diff --git a/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MailerImplTest.java b/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MailerImplTest.java index 36515f65fc4f1..86a6b3eb1c211 100644 --- a/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MailerImplTest.java +++ b/extensions/mailer/runtime/src/test/java/io/quarkus/mailer/runtime/MailerImplTest.java @@ -165,7 +165,7 @@ public Byte next() { @Test void testInlineAttachment() throws MessagingException, IOException { - String cid = UUID.randomUUID().toString() + "@acme"; + String cid = UUID.randomUUID() + "@acme"; mailer.send(Mail.withHtml(TO, "Test", "testInlineAttachment") .addInlineAttachment("inline.txt", "my inlined text".getBytes(StandardCharsets.UTF_8), TEXT_CONTENT_TYPE, cid)) .await().indefinitely(); @@ -210,6 +210,19 @@ void testReplyToHeaderIsSet() throws MessagingException { assertThat(msg.getReplyTo()).containsExactly(InternetAddress.parse("reply-to@quarkus.io")); } + @Test + void testMultipleReplyToHeaderIsSet() throws MessagingException { + mailer.send(Mail.withText(TO, "Test", "testHeaders") + .setReplyTo("reply-to@quarkus.io", "another@quarkus.io")) + .await().indefinitely(); + assertThat(wiser.getMessages()).hasSize(1); + WiserMessage actual = wiser.getMessages().get(0); + MimeMessage msg = actual.getMimeMessage(); + assertThat(msg.getHeader("Reply-To")).containsExactly("reply-to@quarkus.io,another@quarkus.io"); + assertThat(msg.getReplyTo()).hasSize(2).contains(InternetAddress.parse("reply-to@quarkus.io")) + .contains(InternetAddress.parse("another@quarkus.io")); + } + private String getContent(WiserMessage msg) { try { Object content = msg.getMimeMessage().getContent(); @@ -279,24 +292,4 @@ private String getTextFromMimeMultipart( return result.toString(); } - private List getContentTypesFromMimeMultipart( - MimeMultipart mimeMultipart) throws MessagingException, IOException { - List types = new ArrayList<>(); - int count = mimeMultipart.getCount(); - for (int i = 0; i < count; i++) { - BodyPart bodyPart = mimeMultipart.getBodyPart(i); - if (bodyPart.getContent() instanceof MimeMultipart) { - types.addAll(getContentTypesFromMimeMultipart((MimeMultipart) bodyPart.getContent())); - } else { - types.add(bodyPart.getContentType()); - } - } - return types; - } - - private List getContentTypesFromMimeMultipart( - String content) throws MessagingException, IOException { - return Collections.singletonList(content); - } - }