Skip to content

Commit

Permalink
Merge pull request #45171 from cescoffier/mailer-fire-sent-mail
Browse files Browse the repository at this point in the history
Send SentMail Instances When Sending Emails
  • Loading branch information
mkouba authored Jan 2, 2025
2 parents e4cab19 + b80eb2e commit 0a91594
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package io.quarkus.mailer;

import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.assertEquals;

import java.util.concurrent.CompletionStage;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

Expand All @@ -28,7 +30,7 @@ public class InjectionTest {
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(BeanUsingBareMailClient.class, BeanUsingBlockingMailer.class,
BeanUsingReactiveMailer.class, MailTemplates.class)
BeanUsingReactiveMailer.class, MailTemplates.class, MailListener.class)
.addAsResource("mock-config.properties", "application.properties")
.addAsResource(new StringAsset(""
+ "<html>{name}</html>"), "templates/test1.html")
Expand Down Expand Up @@ -56,15 +58,33 @@ public class InjectionTest {
@Inject
MailTemplates templates;

@Inject
MailListener listener;

@Test
public void testInjection() {
beanUsingMutiny.verify();
beanUsingBare.verify();
beanUsingBlockingMailer.verify();

await().until(() -> listener.getLast() != null);
listener.reset();

beanUsingReactiveMailer.verify().toCompletableFuture().join();
templates.send1();
templates.send2().await();
templates.sendNative().await();
await().until(() -> listener.getLast() != null);
listener.reset();

templates.send1().await().indefinitely();
await().until(() -> listener.getLast() != null);
listener.reset();

templates.send2().await().indefinitely();
await().until(() -> listener.getLast() != null);
listener.reset();

templates.sendNative().await().indefinitely();
await().until(() -> listener.getLast() != null);
listener.reset();
assertEquals("<html>Me</html>", MailTemplates.Templates.testNative("Me").templateInstance().render());
}

Expand Down Expand Up @@ -139,4 +159,22 @@ Uni<Void> sendNative() {
return Templates.testNative("John").to("[email protected]").subject("Test").send();
}
}

@ApplicationScoped
public static class MailListener {

volatile SentMail last;

public void onMailSent(@Observes SentMail mail) {
last = mail;
}

public SentMail getLast() {
return last;
}

public void reset() {
last = null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.quarkus.mailer;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Flow;

/**
* Represents a sent mail.
* Instances of this class are sent using CDI events.
*
* @param from the sender address
* @param to the list of recipients
* @param cc the list of CC recipients
* @param bcc the list of BCC recipients
* @param replyTo the list of reply-to addresses
* @param bounceAddress the bounce address
* @param subject the subject
* @param textBody the text body
* @param htmlBody the HTML body
* @param headers the headers
* @param attachments the attachments
*/
public record SentMail(String from,
List<String> to, List<String> cc, List<String> bcc,
String replyTo, String bounceAddress,
String subject, String textBody, String htmlBody,
Map<String, List<String>> headers, List<SentAttachment> attachments) {

/**
* An immutable representation of an attachment that has been sent.
*
* @param name the name
* @param file the file
* @param description the description
* @param disposition the disposition
* @param data the data
* @param contentType the content type
* @param contentId the content ID
*/
public record SentAttachment(String name, File file, String description, String disposition,
Flow.Publisher<Byte> data, String contentType, String contentId) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
import java.util.stream.Collectors;

import jakarta.annotation.PreDestroy;
import jakarta.enterprise.event.Event;
import jakarta.inject.Singleton;

import org.jboss.logging.Logger;

import io.quarkus.mailer.Mailer;
import io.quarkus.mailer.MockMailbox;
import io.quarkus.mailer.SentMail;
import io.quarkus.mailer.reactive.ReactiveMailer;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ConfigurationException;
Expand Down Expand Up @@ -54,7 +56,8 @@ public class Mailers {
private final Map<String, MutinyMailerImpl> mutinyMailers;

public Mailers(Vertx vertx, io.vertx.mutiny.core.Vertx mutinyVertx, MailersRuntimeConfig mailersRuntimeConfig,
LaunchMode launchMode, MailerSupport mailerSupport, TlsConfigurationRegistry tlsRegistry) {
LaunchMode launchMode, MailerSupport mailerSupport, TlsConfigurationRegistry tlsRegistry,
Event<SentMail> sentMailEvent) {
Map<String, MailClient> localClients = new HashMap<>();
Map<String, io.vertx.mutiny.ext.mail.MailClient> localMutinyClients = new HashMap<>();
Map<String, MockMailboxImpl> localMockMailboxes = new HashMap<>();
Expand All @@ -76,7 +79,7 @@ public Mailers(Vertx vertx, io.vertx.mutiny.core.Vertx mutinyVertx, MailersRunti
mailersRuntimeConfig.defaultMailer.approvedRecipients.orElse(List.of()).stream()
.filter(Objects::nonNull).collect(Collectors.toList()),
mailersRuntimeConfig.defaultMailer.logRejectedRecipients,
mailersRuntimeConfig.defaultMailer.logInvalidRecipients));
mailersRuntimeConfig.defaultMailer.logInvalidRecipients, sentMailEvent));
}

for (String name : mailerSupport.namedMailers) {
Expand All @@ -99,7 +102,8 @@ public Mailers(Vertx vertx, io.vertx.mutiny.core.Vertx mutinyVertx, MailersRunti
namedMailerRuntimeConfig.approvedRecipients.orElse(List.of()).stream()
.filter(p -> p != null).collect(Collectors.toList()),
namedMailerRuntimeConfig.logRejectedRecipients,
namedMailerRuntimeConfig.logInvalidRecipients));
namedMailerRuntimeConfig.logInvalidRecipients,
sentMailEvent));
}

this.clients = Collections.unmodifiableMap(localClients);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static java.util.Arrays.stream;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
Expand All @@ -13,10 +14,13 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import jakarta.enterprise.event.Event;

import org.jboss.logging.Logger;

import io.quarkus.mailer.Attachment;
import io.quarkus.mailer.Mail;
import io.quarkus.mailer.SentMail;
import io.quarkus.mailer.reactive.ReactiveMailer;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
Expand Down Expand Up @@ -51,10 +55,11 @@ public class MutinyMailerImpl implements ReactiveMailer {
private final boolean logRejectedRecipients;

private final boolean logInvalidRecipients;
private final Event<SentMail> sentEmailEvent;

MutinyMailerImpl(Vertx vertx, MailClient client, MockMailboxImpl mockMailbox,
String from, String bounceAddress, boolean mock, List<Pattern> approvedRecipients,
boolean logRejectedRecipients, boolean logInvalidRecipients) {
boolean logRejectedRecipients, boolean logInvalidRecipients, Event<SentMail> sentEmailEvent) {
this.vertx = vertx;
this.client = client;
this.mockMailbox = mockMailbox;
Expand All @@ -64,6 +69,7 @@ public class MutinyMailerImpl implements ReactiveMailer {
this.approvedRecipients = approvedRecipients;
this.logRejectedRecipients = logRejectedRecipients;
this.logInvalidRecipients = logInvalidRecipients;
this.sentEmailEvent = sentEmailEvent;
}

@Override
Expand Down Expand Up @@ -128,13 +134,41 @@ private Uni<Void> send(Mail mail, MailMessage message) {
message.getCc(), message.getBcc(),
message.getText() == null ? "<empty>" : message.getText(),
message.getHtml() == null ? "<empty>" : message.getHtml());
return mockMailbox.send(mail, message);
return mockMailbox.send(mail, message)
.invoke(() -> fire(mail, message));
} else {
return client.sendMail(message)
.invoke(() -> fire(mail, message))
.replaceWithVoid();
}
}

private Map<String, List<String>> copy(MultiMap headers) {
return headers.entries().stream()
.collect(
Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
}

private List<SentMail.SentAttachment> copy(List<Attachment> attachments) {
return attachments.stream()
.map(attachment -> new SentMail.SentAttachment(attachment.getName(), attachment.getFile(),
attachment.getDescription(), attachment.getDisposition(), attachment.getData(),
attachment.getContentType(), attachment.getContentId()))
.collect(Collectors.toList());
}

private void fire(Mail mail, MailMessage message) {
if (sentEmailEvent != null) {
SentMail sentMail = new SentMail(message.getFrom(),
Collections.unmodifiableList(message.getTo()), Collections.unmodifiableList(message.getCc()),
Collections.unmodifiableList(message.getBcc()),
mail.getReplyTo(), message.getBounceAddress(),
message.getSubject(), message.getText(), message.getHtml(),
copy(message.getHeaders()), copy(mail.getAttachments()));
sentEmailEvent.fire(sentMail);
}
}

private Uni<MailMessage> toMailMessage(Mail mail) {
MailMessage message = new MailMessage();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public boolean isTrustAll() {
public void register(String name, TlsConfiguration configuration) {
throw new UnsupportedOperationException();
}
});
}, null);
return mailers.reactiveMailerFromName(Mailers.DEFAULT_MAILER_NAME);
}

Expand Down Expand Up @@ -108,7 +108,7 @@ public Optional<TlsConfiguration> getDefault() {
public void register(String name, TlsConfiguration configuration) {
throw new UnsupportedOperationException();
}
});
}, null);
return mailers.reactiveMailerFromName(Mailers.DEFAULT_MAILER_NAME);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ void init() {
mailer = new MutinyMailerImpl(vertx,
MailClient.createShared(vertx,
new MailConfig().setPort(wiser.getServer().getPort())),
null, FROM, null, false, List.of(), false, false);
null, FROM, null, false, List.of(), false, false, null);

wiser.getMessages().clear();
}
Expand Down Expand Up @@ -271,7 +271,7 @@ private String getInlineAttachment(String cid, MimeMultipart multipart) throws I

private String read(BodyPart part) throws IOException, MessagingException {
try (InputStream is = part.getInputStream()) {
Scanner s = new Scanner(is, "UTF-8").useDelimiter("\\A");
Scanner s = new Scanner(is, StandardCharsets.UTF_8).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ static void stopWiser() {
void init() {
mailer = new MutinyMailerImpl(vertx, MailClient.createShared(vertx,
new MailConfig().setPort(wiser.getServer().getPort()).setMultiPartOnly(true)), null,
FROM, null, false, List.of(), false, false);
FROM, null, false, List.of(), false, false, null);

wiser.getMessages().clear();
}
Expand Down Expand Up @@ -261,7 +261,7 @@ private String getInlineAttachment(String cid, MimeMultipart multipart) throws I

private String read(BodyPart part) throws IOException, MessagingException {
try (InputStream is = part.getInputStream()) {
Scanner s = new Scanner(is, "UTF-8").useDelimiter("\\A");
Scanner s = new Scanner(is, StandardCharsets.UTF_8).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ static void stop() {
@BeforeEach
void init() {
mockMailbox = new MockMailboxImpl();
mailer = new MutinyMailerImpl(vertx, null, mockMailbox, FROM, null, true, List.of(), false, false);
mailer = new MutinyMailerImpl(vertx, null, mockMailbox, FROM, null, true, List.of(), false, false, null);
}

@Test
Expand Down

0 comments on commit 0a91594

Please sign in to comment.