diff --git a/email-amazon-ses/src/main/java/io/micronaut/email/ses/SesEmailComposer.java b/email-amazon-ses/src/main/java/io/micronaut/email/ses/SesEmailComposer.java index 0c2f42db..ecb69669 100644 --- a/email-amazon-ses/src/main/java/io/micronaut/email/ses/SesEmailComposer.java +++ b/email-amazon-ses/src/main/java/io/micronaut/email/ses/SesEmailComposer.java @@ -17,6 +17,7 @@ import io.micronaut.core.annotation.NonNull; import io.micronaut.core.util.CollectionUtils; +import io.micronaut.core.util.StringUtils; import io.micronaut.email.BodyType; import io.micronaut.email.Contact; import io.micronaut.email.Email; @@ -94,7 +95,9 @@ private SdkBytes bytesOfMessage(@NonNull Message message) throws IOException, Me private SendEmailRequest sendEmailRequest(@NonNull Email email) { SendEmailRequest.Builder requestBuilder = SendEmailRequest.builder() .destination(destinationBuilder(email).build()) - .source(email.getFrom().getEmail()) + .source(StringUtils.isNotEmpty(email.getFrom().getName()) ? + email.getFrom().getNameAddress() : + email.getFrom().getEmail()) .message(message(email)); if (CollectionUtils.isNotEmpty(email.getReplyToCollection())) { requestBuilder = requestBuilder.replyToAddresses( diff --git a/email-amazon-ses/src/test/groovy/io/micronaut/email/ses/SesEmailComposerSpec.groovy b/email-amazon-ses/src/test/groovy/io/micronaut/email/ses/SesEmailComposerSpec.groovy index 545c190c..233cd57d 100644 --- a/email-amazon-ses/src/test/groovy/io/micronaut/email/ses/SesEmailComposerSpec.groovy +++ b/email-amazon-ses/src/test/groovy/io/micronaut/email/ses/SesEmailComposerSpec.groovy @@ -1,6 +1,6 @@ package io.micronaut.email.ses - +import io.micronaut.email.Contact import io.micronaut.email.Email import io.micronaut.test.extensions.spock.annotation.MicronautTest import jakarta.inject.Inject @@ -178,4 +178,33 @@ class SesEmailComposerSpec extends Specification { subject == request.message().subject().data() !request.destination().ccAddresses() } + + @Unroll + void "from field should allow including the sender name"(String name, String email, String expected) { + given: + Contact from = new Contact(email, name) + String to = "receiver@example.com" + String subject = "Apple Music" + + when: + SendEmailRequest request = sesEmailComposer.compose(Email.builder() + .from(from) + .to(to) + .subject(subject) + .body("Lore ipsum body") + .build()) as SendEmailRequest + + then: + expected == request.source() + [to] == request.destination().toAddresses().toList() + subject == request.message().subject().data() + !request.destination().ccAddresses() + !request.destination().bccAddresses() + + where: + name | email | expected + "John Doe" | "sender@example.com" | "John Doe " + "" | "sender@example.com" | "sender@example.com" + null | "sender@example.com" | "sender@example.com" + } } diff --git a/email/src/main/java/io/micronaut/email/Contact.java b/email/src/main/java/io/micronaut/email/Contact.java index 43018d61..5d0deca7 100644 --- a/email/src/main/java/io/micronaut/email/Contact.java +++ b/email/src/main/java/io/micronaut/email/Contact.java @@ -21,6 +21,7 @@ import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; +import io.micronaut.core.util.StringUtils; import java.util.Objects; /** @@ -73,6 +74,36 @@ public String getName() { return name; } + /** + * returns name-addr for a Contact. + * Given: + * Contact(email: 'johnsnow@example.com, name: John Snow) + * + * When: + * Contact::getNameAddress() + * + * Then: + * {@literal John Snow } + * + * Given: + * Contact(email: 'johnsnow@example.com, name: null) + * + * When: + * Contact::getNameAddress() + * + * Then: + * {@literal } + * + * @see Address Specification + * @return An optional name that indicates the name of the recipient that could be displayed to the user of a mail application, and the email address enclosed in angle brackets + */ + @NonNull + public String getNameAddress() { + return StringUtils.isNotEmpty(getName()) ? + String.format("%s <%s>", name, email) : + "<" + email + ">"; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/email/src/test/groovy/io/micronaut/email/ContactSpec.groovy b/email/src/test/groovy/io/micronaut/email/ContactSpec.groovy index 4756b5cc..a10d706f 100644 --- a/email/src/test/groovy/io/micronaut/email/ContactSpec.groovy +++ b/email/src/test/groovy/io/micronaut/email/ContactSpec.groovy @@ -2,6 +2,7 @@ package io.micronaut.email import io.micronaut.core.beans.BeanIntrospection import spock.lang.Specification +import spock.lang.Unroll class ContactSpec extends Specification { void "Contact is annotated with @Introspected"() { @@ -11,4 +12,19 @@ class ContactSpec extends Specification { then: noExceptionThrown() } + + @Unroll + void "Contact::getNameAddress"(String email, String name, String expected) { + when: + new Contact(email, name).getNameAddress() + + + then: + noExceptionThrown() + + where: + email | name | expected + 'johnsnow@example.com' | 'John Snow' | 'John Snow ' + 'johnsnow@example.com' | null | '' + } } diff --git a/src/main/docs/guide/integrations/javamail.adoc b/src/main/docs/guide/integrations/javamail.adoc index b086a68a..c6d3333d 100644 --- a/src/main/docs/guide/integrations/javamail.adoc +++ b/src/main/docs/guide/integrations/javamail.adoc @@ -12,7 +12,7 @@ You need to provide beans of type api:io.micronaut.email.javamail.sender.MailPro As an alternative to providing your own api:io.micronaut.email.javamail.sender.SessionProvider[] for authentication, you can configure password based authentication via configuration: -[source, yaml] +[configuration] ---- javamail: authentication: