From 444278729e55bc0d3fed1fb84d50521bd2f11d67 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Tue, 6 Aug 2024 08:44:29 +0200 Subject: [PATCH 01/25] :sparkles: email-integration init mail lib --- .../refarch-email-integration/pom.xml | 27 ++ .../refarch-email/pom.xml | 20 ++ .../refarch-email/refarch-email-api/pom.xml | 46 +++ .../refarch/email/api/DigiwfEmailApi.java | 22 ++ .../email/impl/DigiwfEmailApiImpl.java | 133 ++++++++ .../refarch/email/model/FileAttachment.java | 17 + .../de/muenchen/refarch/email/model/Mail.java | 51 +++ .../refarch/email/DigiwfEmailApiImplTest.java | 322 ++++++++++++++++++ .../resources/templates/test-template.ftl | 9 + .../refarch-email-starter/pom.xml | 31 ++ .../DigiwfEmailAutoConfiguration.java | 63 ++++ .../properties/CustomMailProperties.java | 20 ++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + 13 files changed, 762 insertions(+) create mode 100644 refarch-integrations/refarch-email-integration/pom.xml create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/pom.xml create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/pom.xml create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/DigiwfEmailApi.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/DigiwfEmailApiImpl.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/DigiwfEmailApiImplTest.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/resources/templates/test-template.ftl create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/pom.xml create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/properties/CustomMailProperties.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/refarch-integrations/refarch-email-integration/pom.xml b/refarch-integrations/refarch-email-integration/pom.xml new file mode 100644 index 00000000..8228ab7c --- /dev/null +++ b/refarch-integrations/refarch-email-integration/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + + de.muenchen.refarch + refarch-integrations + 1.1.0-SNAPSHOT + + + refarch-email-integration + refarch-email-integration + 1.1.0-SNAPSHOT + pom + + + + 3.0.4 + 1.4.0 + + + + refarch-email + + diff --git a/refarch-integrations/refarch-email-integration/refarch-email/pom.xml b/refarch-integrations/refarch-email-integration/refarch-email/pom.xml new file mode 100644 index 00000000..653e19b9 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + de.muenchen.refarch + refarch-email-integration + 1.1.0-SNAPSHOT + + + refarch-email + pom + + + refarch-email-api + refarch-email-starter + + + diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/pom.xml b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/pom.xml new file mode 100644 index 00000000..5e2c9da9 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + de.muenchen.refarch + refarch-email + 1.1.0-SNAPSHOT + + + refarch-email-api + + + + org.springframework.boot + spring-boot-starter-mail + + + org.apache.commons + commons-lang3 + + + jakarta.validation + jakarta.validation-api + 3.1.0 + + + com.googlecode.owasp-java-html-sanitizer + owasp-java-html-sanitizer + 20240325.1 + + + + org.springframework.boot + spring-boot-starter-freemarker + + + org.springframework + spring-webmvc + + + + + + diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/DigiwfEmailApi.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/DigiwfEmailApi.java new file mode 100644 index 00000000..074a1e7c --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/DigiwfEmailApi.java @@ -0,0 +1,22 @@ +package de.muenchen.refarch.email.api; + +import de.muenchen.refarch.email.model.Mail; +import freemarker.template.TemplateException; +import jakarta.mail.MessagingException; +import jakarta.validation.Valid; +import java.io.IOException; +import java.util.Map; + +public interface DigiwfEmailApi { + + void sendMail(@Valid Mail mail) throws MessagingException; + + void sendMailWithDefaultLogo(@Valid Mail mail) throws MessagingException; + + void sendMail(@Valid Mail mail, String logoPath) throws MessagingException; + + String getBodyFromTemplate(String templateName, Map content) throws IOException, TemplateException; + + String getEmailBodyFromTemplate(String templatePath, Map content); + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/DigiwfEmailApiImpl.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/DigiwfEmailApiImpl.java new file mode 100644 index 00000000..2b712988 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/DigiwfEmailApiImpl.java @@ -0,0 +1,133 @@ +package de.muenchen.refarch.email.impl; + +import de.muenchen.refarch.email.api.DigiwfEmailApi; +import de.muenchen.refarch.email.model.Mail; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import jakarta.mail.Message; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; +import jakarta.validation.Valid; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import lombok.val; +import org.owasp.html.PolicyFactory; +import org.owasp.html.Sanitizers; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.ui.freemarker.FreeMarkerTemplateUtils; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; + +@Slf4j +@RequiredArgsConstructor +public class DigiwfEmailApiImpl implements DigiwfEmailApi { + + private final JavaMailSender mailSender; + private final ResourceLoader resourceLoader; + private final FreeMarkerConfigurer freeMarkerConfigurer; + private final String fromAddress; + private final String defaultReplyToAddress; + // use a prepackaged sanitizer to prevent XSS + private final PolicyFactory policy = Sanitizers.BLOCKS + .and(Sanitizers.FORMATTING) + .and(Sanitizers.LINKS) + .and(Sanitizers.TABLES); + + @Override + public void sendMail(@Valid Mail mail) throws MessagingException { + this.sendMail(mail, null); + } + + @Override + public void sendMailWithDefaultLogo(@Valid Mail mail) throws MessagingException { + this.sendMail(mail, "bausteine/mail/email-logo.png"); + } + + @Override + public void sendMail(@Valid Mail mail, String logoPath) throws MessagingException { + final MimeMessage mimeMessage = this.mailSender.createMimeMessage(); + + mimeMessage.setRecipients(Message.RecipientType.TO, InternetAddress.parse(mail.getReceivers())); + + if (mail.hasReceiversCc()) { + mimeMessage.setRecipients(Message.RecipientType.CC, InternetAddress.parse(mail.getReceiversCc())); + } + if (mail.hasReceiversBcc()) { + mimeMessage.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(mail.getReceiversBcc())); + } + + if (mail.hasReplyTo()) { + mimeMessage.setReplyTo(InternetAddress.parse(mail.getReplyTo())); + } else if (defaultReplyToAddress != null) { + mimeMessage.setReplyTo(InternetAddress.parse(defaultReplyToAddress)); + } + + final var helper = new MimeMessageHelper(mimeMessage, true, StandardCharsets.UTF_8.name()); + + helper.setSubject(mail.getSubject()); + helper.setText(mail.getBody(), mail.isHtmlBody()); + // use custom sender + helper.setFrom(mail.hasSender() ? mail.getSender() : this.fromAddress); + + // mail attachments + if (mail.hasAttachement()) { + for (val attachment : mail.getAttachments()) { + helper.addAttachment(attachment.getFileName(), attachment.getFile()); + } + } + + // logo + if (logoPath != null) { + final Resource logo = this.getRessourceFromClassPath(logoPath); + helper.addInline("logo", logo); + } + + this.mailSender.send(mimeMessage); + log.info("Mail {} sent to {}.", mail.getSubject(), mail.getReceivers()); + } + + @Override + public String getBodyFromTemplate(String templateName, Map content) throws IOException, TemplateException { + Template template = freeMarkerConfigurer.getConfiguration().getTemplate(templateName); + return FreeMarkerTemplateUtils.processTemplateIntoString(template, content); + } + + @Override + public String getEmailBodyFromTemplate(String templatePath, Map content) { + String mailTemplate = this.getTemplate(templatePath); + for (val entry : content.entrySet()) { + // make sure inputs are sanitized to prevent XSS + final String value = policy.sanitize(entry.getValue()); + // Make sure new lines are converted to
tags + mailTemplate = mailTemplate.replaceAll(entry.getKey(), value.replaceAll("(\r\n|\n\r|\r|\n)", "
")); + } + return mailTemplate; + } + + private String getTemplate(String templatePath) { + final Resource resource = this.getRessourceFromClassPath(templatePath); + if (!resource.exists()) { + log.error("Email Template not found: {}", templatePath); + throw new RuntimeException("Email Template not found: " + templatePath); + } + + try { + byte[] byteArray = FileCopyUtils.copyToByteArray(resource.getInputStream()); + return new String(byteArray, StandardCharsets.UTF_8); + } catch (Exception e) { + log.warn("Failed to load file: {}", templatePath); + throw new RuntimeException("Failed to load file: " + templatePath, e); + } + } + + private Resource getRessourceFromClassPath(String path) { + return resourceLoader.getResource("classpath:" + path); + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java new file mode 100644 index 00000000..cd356618 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java @@ -0,0 +1,17 @@ +package de.muenchen.refarch.email.model; + +import jakarta.mail.util.ByteArrayDataSource; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@Data +@RequiredArgsConstructor +@AllArgsConstructor +public class FileAttachment { + + private String fileName; + + private ByteArrayDataSource file; + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java new file mode 100644 index 00000000..0988f68a --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java @@ -0,0 +1,51 @@ +package de.muenchen.refarch.email.model; + +import jakarta.validation.constraints.NotBlank; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Mail { + + @NotBlank + private String receivers; + @NotBlank + private String subject; + @NotBlank + private String body; + @Builder.Default + private boolean htmlBody = false; + private String replyTo; + private String receiversCc; + private String receiversBcc; + private String sender; + private List attachments; + + public boolean hasAttachement() { + return attachments != null && !attachments.isEmpty(); + } + + public boolean hasReplyTo() { + return StringUtils.isNotBlank(this.replyTo); + } + + public boolean hasReceiversCc() { + return StringUtils.isNotBlank(this.receiversCc); + } + + public boolean hasReceiversBcc() { + return StringUtils.isNotBlank(this.receiversBcc); + } + + public boolean hasSender() { + return StringUtils.isNotBlank(this.sender); + } + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/DigiwfEmailApiImplTest.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/DigiwfEmailApiImplTest.java new file mode 100644 index 00000000..f174b4ea --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/DigiwfEmailApiImplTest.java @@ -0,0 +1,322 @@ +package de.muenchen.refarch.email; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.mockito.Mockito.*; + +import de.muenchen.refarch.email.api.DigiwfEmailApi; +import de.muenchen.refarch.email.impl.DigiwfEmailApiImpl; +import de.muenchen.refarch.email.model.FileAttachment; +import de.muenchen.refarch.email.model.Mail; +import freemarker.template.Configuration; +import freemarker.template.TemplateException; +import jakarta.mail.MessagingException; +import jakarta.mail.Session; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; +import jakarta.mail.util.ByteArrayDataSource; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; + +class DigiwfEmailApiImplTest { + + private final JavaMailSender javaMailSender = mock(JavaMailSender.class); + private final ResourceLoader resourceLoader = mock(ResourceLoader.class); + private final FreeMarkerConfigurer freeMarkerConfigurer = mock(FreeMarkerConfigurer.class); + // test data + private final String receiver = "mailReceiver1@muenchen.de,mailReceiver2@muenchen.de"; + private final String receiverCC = "receiverCC@muenchen.de"; + private final String receiverBCC = "receiverBCC@muenchen.de"; + private final String subject = "Test Mail"; + private final String body = "This is a test mail"; + private final String replyTo = "digiwf@muenchen.de"; + private final String defaultReplyTo = "noreply@muenchen.de"; + private final String sender = "some-custom-sender@muenchen.de"; + private DigiwfEmailApi digiwfEmailApi; + + @BeforeEach + void setUp() { + when(this.javaMailSender.createMimeMessage()).thenReturn(new MimeMessage((Session) null)); + this.digiwfEmailApi = new DigiwfEmailApiImpl(this.javaMailSender, this.resourceLoader, freeMarkerConfigurer, "digiwf@muenchen.de", defaultReplyTo); + } + + @Test + void testSendMail() throws MessagingException, IOException { + final Mail mail = Mail.builder() + .receivers(this.receiver) + .subject(this.subject) + .body(this.body) + .build(); + this.digiwfEmailApi.sendMail(mail); + + final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); + verify(this.javaMailSender).send(messageArgumentCaptor.capture()); + + assertThat(messageArgumentCaptor.getValue().getAllRecipients()).hasSize(2); + assertThat(messageArgumentCaptor.getValue().getSubject()).isEqualTo(this.subject); + assertThat(messageArgumentCaptor.getValue().getReplyTo()).hasSize(1); + assertThat(messageArgumentCaptor.getValue().getReplyTo()).contains(new InternetAddress(this.defaultReplyTo)); + final MimeMultipart content = (MimeMultipart) messageArgumentCaptor.getValue().getContent(); + assertThat(content.getContentType()).contains("multipart/mixed"); + } + + @Test + void testSendMailNoDefaultReplyTo() throws MessagingException, IOException { + var customAddress = new InternetAddress("custom.digiwf@muenchen.de"); + + final Mail mail = Mail.builder() + .receivers(this.receiver) + .subject(this.subject) + .body(this.body) + .build(); + new DigiwfEmailApiImpl(this.javaMailSender, this.resourceLoader, freeMarkerConfigurer, customAddress.getAddress(), null).sendMail(mail); + + final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); + verify(this.javaMailSender).send(messageArgumentCaptor.capture()); + + assertThat(messageArgumentCaptor.getValue().getAllRecipients()).hasSize(2); + assertThat(messageArgumentCaptor.getValue().getSubject()).isEqualTo(this.subject); + assertThat(messageArgumentCaptor.getValue().getReplyTo()).hasSize(1); + assertThat(messageArgumentCaptor.getValue().getReplyTo()).contains(new InternetAddress(customAddress.getAddress())); + final MimeMultipart content = (MimeMultipart) messageArgumentCaptor.getValue().getContent(); + assertThat(content.getContentType()).contains("multipart/mixed"); + } + + @Test + void testSendMailWithOptions() throws MessagingException, IOException { + final Mail mail = Mail.builder() + .receivers(this.receiver) + .subject(this.subject) + .body(this.body) + .replyTo(this.replyTo) + .receiversCc(this.receiverCC) + .receiversBcc(this.receiverBCC) + .sender(this.sender) + .build(); + this.digiwfEmailApi.sendMail(mail); + + final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); + verify(this.javaMailSender).send(messageArgumentCaptor.capture()); + + assertThat(messageArgumentCaptor.getValue().getAllRecipients()).hasSize(4); + assertThat(messageArgumentCaptor.getValue().getAllRecipients()).containsAll( + List.of(new InternetAddress("mailReceiver1@muenchen.de"), new InternetAddress("mailReceiver2@muenchen.de"), + new InternetAddress(this.receiverCC), new InternetAddress(this.receiverBCC))); + assertThat(messageArgumentCaptor.getValue().getSubject()).isEqualTo(this.subject); + assertThat(messageArgumentCaptor.getValue().getReplyTo()).hasSize(1); + assertThat(messageArgumentCaptor.getValue().getReplyTo()).contains(new InternetAddress(this.replyTo)); + assertThat(messageArgumentCaptor.getValue().getFrom()).contains(new InternetAddress(this.sender)); + final MimeMultipart content = (MimeMultipart) messageArgumentCaptor.getValue().getContent(); + assertThat(content.getContentType()).contains("multipart/mixed"); + } + + @Test + void sendMailWithAttachments() throws MessagingException, IOException { + final Mail mail = Mail.builder() + .receivers(this.receiver) + .subject(this.subject) + .body(this.body) + .attachments(List.of( + new FileAttachment("Testanhang", new ByteArrayDataSource("FooBar".getBytes(), "text/plain")))) + .build(); + this.digiwfEmailApi.sendMail(mail); + + final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); + verify(this.javaMailSender).send(messageArgumentCaptor.capture()); + + assertThat(messageArgumentCaptor.getValue().getAllRecipients()).hasSize(2); + assertThat(messageArgumentCaptor.getValue().getSubject()).isEqualTo(this.subject); + final MimeMultipart content = (MimeMultipart) messageArgumentCaptor.getValue().getContent(); + assertThat(content.getContentType()).contains("multipart/mixed"); + } + + @Test + void sendMailWithMultipleReplyToAddresses() throws MessagingException, IOException { + var reply1 = new InternetAddress("address1@muenchen.de"); + var reply2 = new InternetAddress("address2@muenchen.de"); + + final Mail mail = Mail.builder() + .receivers(this.receiver) + .subject(this.subject) + .body(this.body) + .replyTo(reply1.getAddress() + "," + reply2.getAddress()) + .build(); + this.digiwfEmailApi.sendMail(mail); + + final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); + verify(this.javaMailSender).send(messageArgumentCaptor.capture()); + + assertThat(messageArgumentCaptor.getValue().getAllRecipients()).hasSize(2); + assertThat(messageArgumentCaptor.getValue().getSubject()).isEqualTo(this.subject); + assertThat(messageArgumentCaptor.getValue().getReplyTo()).hasSize(2); + assertThat(messageArgumentCaptor.getValue().getReplyTo()).containsAll(List.of(reply1, reply2)); + final MimeMultipart content = (MimeMultipart) messageArgumentCaptor.getValue().getContent(); + assertThat(content.getContentType()).contains("multipart/mixed"); + } + + @Test + void sendMailWithDefaultLogo() throws MessagingException, IOException { + when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("Default Logo", true)); + + final Mail mail = Mail.builder() + .receivers(this.receiver) + .subject(this.subject) + .body(this.body) + .build(); + this.digiwfEmailApi.sendMailWithDefaultLogo(mail); + + final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); + verify(this.javaMailSender).send(messageArgumentCaptor.capture()); + verify(this.resourceLoader).getResource("classpath:bausteine/mail/email-logo.png"); + + assertThat(messageArgumentCaptor.getValue().getAllRecipients()).hasSize(2); + assertThat(messageArgumentCaptor.getValue().getSubject()).isEqualTo(this.subject); + final MimeMultipart content = (MimeMultipart) messageArgumentCaptor.getValue().getContent(); + assertThat(content.getContentType()).contains("multipart/mixed"); + } + + @Test + void sendMailWithCustomLogo() throws MessagingException, IOException { + when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("Custom Logo", true)); + + final Mail mail = Mail.builder() + .receivers(this.receiver) + .subject(this.subject) + .body(this.body) + .build(); + this.digiwfEmailApi.sendMail(mail, "some/random/path/on/classpath"); + + final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); + verify(this.javaMailSender).send(messageArgumentCaptor.capture()); + verify(this.resourceLoader).getResource("classpath:some/random/path/on/classpath"); + + assertThat(messageArgumentCaptor.getValue().getAllRecipients()).hasSize(2); + assertThat(messageArgumentCaptor.getValue().getSubject()).isEqualTo(this.subject); + final MimeMultipart content = (MimeMultipart) messageArgumentCaptor.getValue().getContent(); + assertThat(content.getContentType()).contains("multipart/mixed"); + } + + @Test + void testGetBodyFromFreemarkerTemplate() throws IOException, TemplateException { + final String templateName = "test-template.ftl"; + Map content = Map.of("data", "test"); + Configuration configuration = new Configuration(Configuration.VERSION_2_3_30); + configuration.setClassForTemplateLoading(this.getClass(), "/templates/"); + when(this.freeMarkerConfigurer.getConfiguration()).thenReturn(configuration); + + final String result = this.digiwfEmailApi.getBodyFromTemplate(templateName, content); + + assertThat(result.contains("test")).isTrue(); + } + + @Test + void testGetEmailBodyFromTemplate() { + when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("This is a test mail", true)); + + final String templatePath = "bausteine/mail/email-logo.png"; + final String result = this.digiwfEmailApi.getEmailBodyFromTemplate(templatePath, Map.of()); + + assertThat(result).isEqualTo("This is a test mail"); + } + + @Test + void testGetEmailBodyFromTemplateWithContent() { + when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("This is a test mail with content", true)); + + final String templatePath = "bausteine/mail/email-logo.png"; + final String result = this.digiwfEmailApi.getEmailBodyFromTemplate(templatePath, Map.of("content", "some content")); + + assertThat(result).isEqualTo("This is a test mail with some content"); + } + + @Test + void testGetEmailBodyFromTemplateWithContentAndNewLines() { + when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("This is a test mail with content", true)); + + final String templatePath = "bausteine/mail/email-logo.png"; + final String result = this.digiwfEmailApi.getEmailBodyFromTemplate(templatePath, Map.of("content", "some content \n with new line")); + + assertThat(result).isEqualTo("This is a test mail with some content
with new line"); + } + + @Test + void testGetEmailBodyFromTemplateWithContentFailsIfTemplateDoesNotExist() { + when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("foo bar", false)); + + final String templatePath = "some/temlate/that/does/not/exist"; + assertThatThrownBy(() -> { + this.digiwfEmailApi.getEmailBodyFromTemplate(templatePath, Map.of("content", "some content")); + }) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Email Template not found: " + templatePath); + } + + private Resource getResourceForText(final String text, final boolean resourceExists) { + return new Resource() { + @Override + public boolean exists() { + return resourceExists; + } + + @Override + public URL getURL() throws IOException { + return null; + } + + @Override + public URI getURI() throws IOException { + return null; + } + + @Override + public File getFile() throws IOException { + return null; + } + + @Override + public long contentLength() throws IOException { + return 0; + } + + @Override + public long lastModified() throws IOException { + return 0; + } + + @Override + public Resource createRelative(String relativePath) throws IOException { + return null; + } + + @Override + public String getFilename() { + return "test.txt"; + } + + @Override + public String getDescription() { + return null; + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(text.getBytes()); + } + }; + } + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/resources/templates/test-template.ftl b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/resources/templates/test-template.ftl new file mode 100644 index 00000000..1e47f8a2 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/resources/templates/test-template.ftl @@ -0,0 +1,9 @@ + + + + Titel + + +

Zweck dieser E-Mail ist ein ${data}

+ + diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/pom.xml b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/pom.xml new file mode 100644 index 00000000..9c2eb0bf --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + de.muenchen.refarch + refarch-email + 1.1.0-SNAPSHOT + + + refarch-email-starter + + + + de.muenchen.refarch + refarch-email-api + ${project.version} + + + org.springframework.boot + spring-boot + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java new file mode 100644 index 00000000..bf2897b5 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java @@ -0,0 +1,63 @@ +package de.muenchen.refarch.email.configuration; + +import de.muenchen.refarch.email.api.DigiwfEmailApi; +import de.muenchen.refarch.email.impl.DigiwfEmailApiImpl; +import de.muenchen.refarch.email.properties.CustomMailProperties; +import jakarta.mail.MessagingException; +import java.util.Properties; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.mail.MailProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.ResourceLoader; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; + +@RequiredArgsConstructor +@EnableConfigurationProperties({ MailProperties.class, CustomMailProperties.class }) +public class DigiwfEmailAutoConfiguration { + + private final MailProperties mailProperties; + private final CustomMailProperties customMailProperties; + + /** + * Configures the {@link JavaMailSender} + * + * @return configured JavaMailSender + */ + @Bean + @ConditionalOnMissingBean + public JavaMailSender getJavaMailSender() throws MessagingException { + final JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); + mailSender.setHost(this.mailProperties.getHost()); + mailSender.setPort(this.mailProperties.getPort()); + mailSender.setProtocol(this.mailProperties.getProtocol()); + mailSender.setUsername(this.mailProperties.getUsername()); + mailSender.setPassword(this.mailProperties.getPassword()); + + final Properties props = mailSender.getJavaMailProperties(); + props.putAll(this.mailProperties.getProperties()); + mailSender.setJavaMailProperties(props); + mailSender.testConnection(); + return mailSender; + } + + @ConditionalOnMissingBean + @Bean + public DigiwfEmailApi digiwfEmailApi(final ResourceLoader resourceLoader, final JavaMailSender javaMailSender, + final FreeMarkerConfigurer freeMarkerConfigurer) { + return new DigiwfEmailApiImpl(javaMailSender, resourceLoader, freeMarkerConfigurer, this.customMailProperties.getFromAddress(), + this.customMailProperties.getDefaultReplyToAddress()); + } + + @Bean + @ConditionalOnMissingBean + public FreeMarkerConfigurer freemarkerConfig() { + FreeMarkerConfigurer freeMarkerConfigurer = new FreeMarkerConfigurer(); + freeMarkerConfigurer.setTemplateLoaderPath("classpath:templates/"); + return freeMarkerConfigurer; + } + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/properties/CustomMailProperties.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/properties/CustomMailProperties.java new file mode 100644 index 00000000..b111c5d3 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/properties/CustomMailProperties.java @@ -0,0 +1,20 @@ +package de.muenchen.refarch.email.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@Data +@ConfigurationProperties(prefix = "io.muenchendigital.digiwf.mail") +public class CustomMailProperties { + + /** + * Sender mail address. + */ + private String fromAddress; + + /** + * Default Reply-to mail address, e.g. no-reply@domain + */ + private String defaultReplyToAddress; + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..e38c58a2 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +de.muenchen.refarch.email.configuration.DigiwfEmailAutoConfiguration From 027a6c49bea21992c691ff5778f109260ddc9109 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Tue, 6 Aug 2024 09:47:01 +0200 Subject: [PATCH 02/25] :sparkles: email-integration init core --- .../refarch-email-integration/pom.xml | 2 +- .../refarch-email-integration-core/pom.xml | 41 ++++++ .../adapter/out/mail/MailAdapter.java | 26 ++++ .../integration/adapter/out/s3/S3Adapter.java | 80 +++++++++++ .../port/in/SendMailPathsInPort.java | 13 ++ .../port/out/LoadMailAttachmentOutPort.java | 8 ++ .../application/port/out/MailOutPort.java | 15 ++ .../usecase/SendMailPathsUseCase.java | 89 ++++++++++++ .../domain/exception/LoadAttachmentError.java | 7 + .../domain/exception/TemplateError.java | 7 + .../integration/domain/model/BasicMail.java | 38 +++++ .../domain/model/paths/BasicMailPaths.java | 33 +++++ .../domain/model/paths/TemplateMailPaths.java | 36 +++++ .../domain/model/paths/TextMailPaths.java | 30 ++++ .../adapter/out/mail/MailAdapterTest.java | 43 ++++++ .../adapter/out/s3/S3AdapterTest.java | 84 +++++++++++ .../usecase/SendMailPathsUseCaseTest.java | 135 ++++++++++++++++++ .../src/test/resources/files/digiwf_logo.png | Bin 0 -> 100369 bytes .../src/test/resources/files/test-pdf.pdf | Bin 0 -> 7386 bytes .../src/test/resources/files/test-word.docx | Bin 0 -> 4266 bytes .../src/test/resources/logback-test.xml | 29 ++++ 21 files changed, 715 insertions(+), 1 deletion(-) create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/pom.xml create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapter.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailPathsInPort.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/LoadMailAttachmentOutPort.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/MailOutPort.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/exception/LoadAttachmentError.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/exception/TemplateError.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/digiwf_logo.png create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-pdf.pdf create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-word.docx create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/logback-test.xml diff --git a/refarch-integrations/refarch-email-integration/pom.xml b/refarch-integrations/refarch-email-integration/pom.xml index 8228ab7c..02957627 100644 --- a/refarch-integrations/refarch-email-integration/pom.xml +++ b/refarch-integrations/refarch-email-integration/pom.xml @@ -16,12 +16,12 @@ pom - 3.0.4 1.4.0 refarch-email + refarch-email-integration-core diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/pom.xml b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/pom.xml new file mode 100644 index 00000000..7b9ef378 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + refarch-email-integration + de.muenchen.refarch + 1.1.0-SNAPSHOT + + + refarch-email-integration-core + + + + de.muenchen.refarch + refarch-s3-integration-client-starter + ${project.version} + + + de.muenchen.refarch + refarch-email-starter + ${project.version} + + + org.springframework + spring-web + + + org.apache.pdfbox + jbig2-imageio + ${jbig2-imageio.version} + + + com.github.jai-imageio + jai-imageio-jpeg2000 + ${jai-imageio-jpeg2000.version} + + + + diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapter.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapter.java new file mode 100644 index 00000000..39756c2b --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapter.java @@ -0,0 +1,26 @@ +package de.muenchen.refarch.email.integration.adapter.out.mail; + +import de.muenchen.refarch.email.api.DigiwfEmailApi; +import de.muenchen.refarch.email.integration.application.port.out.MailOutPort; +import de.muenchen.refarch.email.model.Mail; +import freemarker.template.TemplateException; +import jakarta.mail.MessagingException; +import java.io.IOException; +import java.util.Map; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +public class MailAdapter implements MailOutPort { + + private final DigiwfEmailApi digiwfEmailApi; + + @Override + public void sendMail(Mail mail, String logoPath) throws MessagingException { + this.digiwfEmailApi.sendMail(mail, logoPath); + } + + @Override + public String getBodyFromTemplate(String templateName, Map content) throws TemplateException, IOException { + return this.digiwfEmailApi.getBodyFromTemplate(templateName, content); + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java new file mode 100644 index 00000000..b8d4c714 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java @@ -0,0 +1,80 @@ +package de.muenchen.refarch.email.integration.adapter.out.s3; + +import de.muenchen.refarch.email.integration.application.port.out.LoadMailAttachmentOutPort; +import de.muenchen.refarch.email.integration.domain.exception.LoadAttachmentError; +import de.muenchen.refarch.email.model.FileAttachment; +import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageClientErrorException; +import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageException; +import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageServerErrorException; +import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFileRepository; +import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFolderRepository; +import de.muenchen.refarch.s3.integration.client.repository.transfer.S3FileTransferRepository; +import de.muenchen.refarch.s3.integration.client.service.FileService; +import de.muenchen.refarch.s3.integration.client.service.S3StorageUrlProvider; +import jakarta.mail.util.ByteArrayDataSource; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; + +@Slf4j +@RequiredArgsConstructor +public class S3Adapter implements LoadMailAttachmentOutPort { + + private final S3FileTransferRepository s3FileTransferRepository; + private final DocumentStorageFileRepository documentStorageFileRepository; + private final DocumentStorageFolderRepository documentStorageFolderRepository; + private final FileService fileService; + private final S3StorageUrlProvider s3DomainService; + + @Override + public List loadAttachments(final String fileContext, final List filePaths) { + final List attachments = new ArrayList<>(); + filePaths.forEach(path -> { + final String fullPath = fileContext + "/" + path; + if (fullPath.endsWith("/")) { + attachments.addAll(getFilesFromFolder(fullPath)); + } else { + attachments.add(getFile(fullPath)); + } + }); + return attachments; + } + + private List getFilesFromFolder(final String folderPath) { + try { + final List contents = new ArrayList<>(); + final Set filepath; + filepath = documentStorageFolderRepository.getAllFilesInFolderRecursively(folderPath, s3DomainService.getDefaultDocumentStorageUrl()).block(); + if (Objects.isNull(filepath)) + throw new LoadAttachmentError("An folder could not be loaded from url: " + folderPath); + filepath.forEach(file -> contents.add(getFile(file))); + return contents; + } catch (final DocumentStorageException | DocumentStorageServerErrorException | + DocumentStorageClientErrorException e) { + throw new LoadAttachmentError("An folder could not be loaded from path: " + folderPath); + } + } + + private FileAttachment getFile(final String filePath) { + try { + final byte[] bytes; + bytes = this.documentStorageFileRepository.getFile(filePath, 3, s3DomainService.getDefaultDocumentStorageUrl()); + final String mimeType = fileService.detectFileType(bytes); + final String filename = FilenameUtils.getName(filePath); + final ByteArrayDataSource file = new ByteArrayDataSource(bytes, mimeType); + + // check if mimeType exists + if (!fileService.isSupported(mimeType)) + throw new LoadAttachmentError("The type of this file is not supported: " + filePath); + + return new FileAttachment(filename, file); + } catch (final DocumentStorageException | DocumentStorageServerErrorException | + DocumentStorageClientErrorException e) { + throw new LoadAttachmentError("An file could not be loaded from path: " + filePath); + } + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailPathsInPort.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailPathsInPort.java new file mode 100644 index 00000000..69ac37df --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailPathsInPort.java @@ -0,0 +1,13 @@ +package de.muenchen.refarch.email.integration.application.port.in; + +import de.muenchen.refarch.email.integration.domain.model.paths.TemplateMailPaths; +import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; +import jakarta.validation.Valid; + +public interface SendMailPathsInPort { + + void sendMailWithText(@Valid final TextMailPaths mail); + + void sendMailWithTemplate(@Valid final TemplateMailPaths mail); + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/LoadMailAttachmentOutPort.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/LoadMailAttachmentOutPort.java new file mode 100644 index 00000000..5c4b0f5a --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/LoadMailAttachmentOutPort.java @@ -0,0 +1,8 @@ +package de.muenchen.refarch.email.integration.application.port.out; + +import de.muenchen.refarch.email.model.FileAttachment; +import java.util.List; + +public interface LoadMailAttachmentOutPort { + List loadAttachments(final String fileContext, final List filePaths); +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/MailOutPort.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/MailOutPort.java new file mode 100644 index 00000000..20f8ecb3 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/MailOutPort.java @@ -0,0 +1,15 @@ +package de.muenchen.refarch.email.integration.application.port.out; + +import de.muenchen.refarch.email.model.Mail; +import freemarker.template.TemplateException; +import jakarta.mail.MessagingException; +import java.io.IOException; +import java.util.Map; + +public interface MailOutPort { + + void sendMail(Mail mail, String logoPath) throws MessagingException; + + String getBodyFromTemplate(String templateName, Map content) throws TemplateException, IOException; + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java new file mode 100644 index 00000000..60c7f57f --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java @@ -0,0 +1,89 @@ +package de.muenchen.refarch.email.integration.application.usecase; + +import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; +import de.muenchen.refarch.email.integration.application.port.out.LoadMailAttachmentOutPort; +import de.muenchen.refarch.email.integration.application.port.out.MailOutPort; +import de.muenchen.refarch.email.integration.domain.exception.TemplateError; +import de.muenchen.refarch.email.integration.domain.model.paths.BasicMailPaths; +import de.muenchen.refarch.email.integration.domain.model.paths.TemplateMailPaths; +import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; +import de.muenchen.refarch.email.model.FileAttachment; +import freemarker.template.TemplateException; +import jakarta.mail.MessagingException; +import jakarta.validation.Valid; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.mail.MailSendException; +import org.springframework.validation.annotation.Validated; + +@Slf4j +@RequiredArgsConstructor +@Validated +public class SendMailPathsUseCase implements SendMailPathsInPort { + + private final LoadMailAttachmentOutPort loadAttachmentOutPort; + private final MailOutPort mailOutPort; + + /** + * Send a mail. + * + * @param mail mail that is sent + */ + @Override + public void sendMailWithText(@Valid final TextMailPaths mail) { + de.muenchen.refarch.email.model.Mail mailModel = createMail(mail); + mailModel.setBody(mail.getBody()); + + this.sendMail(mailModel, null); + } + + @Override + public void sendMailWithTemplate(@Valid final TemplateMailPaths mail) throws TemplateError { + // get body from template + try { + Map content = new HashMap<>(mail.getContent()); + content.put("footer", "DigiWF 2.0
IT-Referat der Stadt München"); + String body = this.mailOutPort.getBodyFromTemplate(mail.getTemplate(), content); + + de.muenchen.refarch.email.model.Mail mailModel = createMail(mail); + mailModel.setBody(body); + mailModel.setHtmlBody(true); + + this.sendMail(mailModel, "templates/email-logo.png"); + + } catch (IOException ioException) { + throw new TemplateError("The template " + mail.getTemplate() + " could not be loaded"); + } catch (TemplateException templateException) { + throw new TemplateError(templateException.getMessage()); + } + } + + private de.muenchen.refarch.email.model.Mail createMail(BasicMailPaths mail) { + // load Attachments + List attachments = loadAttachmentOutPort.loadAttachments(mail.getFileContext(), mail.parseFilePaths()); + + // send mail + return de.muenchen.refarch.email.model.Mail.builder() + .receivers(mail.getReceivers()) + .subject(mail.getSubject()) + .replyTo(mail.getReplyTo()) + .receiversCc(mail.getReceiversCc()) + .receiversBcc(mail.getReceiversBcc()) + .attachments(attachments) + .build(); + } + + private void sendMail(de.muenchen.refarch.email.model.Mail mailModel, String logoPath) throws MailSendException { + try { + this.mailOutPort.sendMail(mailModel, logoPath); + } catch (final MessagingException ex) { + log.error("Sending mail failed with exception: {}", ex.getMessage()); + throw new MailSendException(ex.getMessage()); + } + } + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/exception/LoadAttachmentError.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/exception/LoadAttachmentError.java new file mode 100644 index 00000000..ba854d6b --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/exception/LoadAttachmentError.java @@ -0,0 +1,7 @@ +package de.muenchen.refarch.email.integration.domain.exception; + +public class LoadAttachmentError extends Error { + public LoadAttachmentError(final String message) { + super(message); + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/exception/TemplateError.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/exception/TemplateError.java new file mode 100644 index 00000000..7189207f --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/exception/TemplateError.java @@ -0,0 +1,7 @@ +package de.muenchen.refarch.email.integration.domain.exception; + +public class TemplateError extends Error { + public TemplateError(final String message) { + super(message); + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java new file mode 100644 index 00000000..ac2526bf --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java @@ -0,0 +1,38 @@ +package de.muenchen.refarch.email.integration.domain.model; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +@Data +@RequiredArgsConstructor +@AllArgsConstructor +public class BasicMail { + /** + * Receiver addresses of the mail, comma separated. + */ + @NotBlank(message = "No receivers given") + private String receivers; + + /** + * CC-Receiver addresses of the mail, comma separated. + */ + private String receiversCc; + + /** + * BCC-Receiver addresses of the mail, comma separated. + */ + private String receiversBcc; + + /** + * Subject of the mail. + */ + @NotBlank(message = "No subject given") + private String subject; + + /** + * Reply to address + */ + private String replyTo; +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java new file mode 100644 index 00000000..eddef682 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java @@ -0,0 +1,33 @@ +package de.muenchen.refarch.email.integration.domain.model.paths; + +import de.muenchen.refarch.email.integration.domain.model.BasicMail; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@Data +@RequiredArgsConstructor +@AllArgsConstructor +public class BasicMailPaths extends BasicMail { + @NotBlank + private String fileContext; + private String filePaths; + + public BasicMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, String fileContext, String filePaths) { + super(receivers, receiversCc, receiversBcc, subject, replyTo); + this.fileContext = fileContext; + this.filePaths = filePaths; + } + + public List parseFilePaths() { + if (StringUtils.isBlank(filePaths)) + return List.of(); + return List.of(filePaths.split("[,;]")); + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java new file mode 100644 index 00000000..39117ec2 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java @@ -0,0 +1,36 @@ +package de.muenchen.refarch.email.integration.domain.model.paths; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; + +import java.util.Map; + +@EqualsAndHashCode(callSuper = true) +@Data +@RequiredArgsConstructor +@AllArgsConstructor +public class TemplateMailPaths extends BasicMailPaths { + + /** + * Template of the mail. + */ + @NotBlank(message = "No template given") + private String template; + + /** + * Bottom body of the mail. + */ + @NotEmpty(message = "No content given") + private Map content; + + public TemplateMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, String fileContext, String filePaths, + String template, Map content) { + super(receivers, receiversCc, receiversBcc, subject, replyTo, fileContext, filePaths); + this.template = template; + this.content = content; + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java new file mode 100644 index 00000000..e65cdea9 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java @@ -0,0 +1,30 @@ +package de.muenchen.refarch.email.integration.domain.model.paths; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; + +/** + * Object contains all the information needed to send a mail. + */ +@EqualsAndHashCode(callSuper = true) +@Data +@RequiredArgsConstructor +@AllArgsConstructor +public class TextMailPaths extends BasicMailPaths { + + /** + * Body of the mail. + */ + @NotBlank(message = "No body given") + private String body; + + public TextMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String body, String replyTo, String fileContext, + String filePath) { + super(receivers, receiversCc, receiversBcc, subject, replyTo, fileContext, filePath); + this.body = body; + } + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java new file mode 100644 index 00000000..66e44746 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java @@ -0,0 +1,43 @@ +package de.muenchen.refarch.email.integration.adapter.out.mail; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.*; + +import de.muenchen.refarch.email.api.DigiwfEmailApi; +import de.muenchen.refarch.email.model.Mail; +import freemarker.template.TemplateException; +import jakarta.mail.MessagingException; +import java.io.IOException; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class MailAdapterTest { + + private final DigiwfEmailApi digiwfEmailApi = mock(DigiwfEmailApi.class); + + @Test + void sendMail() throws MessagingException { + final MailAdapter mailAdapter = new MailAdapter(digiwfEmailApi); + final Mail mail = Mail.builder() + .receivers("receivers") + .subject("subject") + .body("body") + .replyTo("replyTo") + .receiversCc("receiversCc") + .receiversBcc("receiversBcc") + .build(); + mailAdapter.sendMail(mail, "logoPath"); + verify(digiwfEmailApi).sendMail(mail, "logoPath"); + } + + @Test + void getBodyFromTemplate() throws TemplateException, IOException { + final MailAdapter mailAdapter = new MailAdapter(digiwfEmailApi); + when(digiwfEmailApi.getBodyFromTemplate(anyString(), anyMap())).thenReturn("generated body"); + String body = mailAdapter.getBodyFromTemplate("template", Map.of("key", "value")); + + assertThat(body).isEqualTo("generated body"); + verify(digiwfEmailApi).getBodyFromTemplate("template", Map.of("key", "value")); + } + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java new file mode 100644 index 00000000..385ebd53 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java @@ -0,0 +1,84 @@ +package de.muenchen.refarch.email.integration.adapter.out.s3; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import de.muenchen.refarch.email.integration.domain.exception.LoadAttachmentError; +import de.muenchen.refarch.email.model.FileAttachment; +import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageClientErrorException; +import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageException; +import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageServerErrorException; +import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFileRepository; +import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFolderRepository; +import de.muenchen.refarch.s3.integration.client.repository.transfer.S3FileTransferRepository; +import de.muenchen.refarch.s3.integration.client.service.FileService; +import de.muenchen.refarch.s3.integration.client.service.S3StorageUrlProvider; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.unit.DataSize; + +@Slf4j +class S3AdapterTest { + + private final S3FileTransferRepository s3FileTransferRepository = mock(S3FileTransferRepository.class); + private final DocumentStorageFileRepository documentStorageFileRepository = mock(DocumentStorageFileRepository.class); + private final DocumentStorageFolderRepository documentStorageFolderRepository = mock(DocumentStorageFolderRepository.class); + private final S3StorageUrlProvider s3DomainService = mock(S3StorageUrlProvider.class); + private final FileService fileService = new FileService(null, DataSize.ofMegabytes(50), DataSize.ofMegabytes(110)); + + private S3Adapter s3Adapter; + + @BeforeEach + void setup() { + s3Adapter = new S3Adapter(s3FileTransferRepository, documentStorageFileRepository, documentStorageFolderRepository, fileService, s3DomainService); + } + + @Test + void testLoadAttachment_DocumentStorageException() + throws DocumentStorageException, DocumentStorageClientErrorException, DocumentStorageServerErrorException { + final String path = "path/to/some-file.txt"; + final String context = "fileContext"; + final String fullPath = context + "/" + path; + + // DocumentStorageException + when(documentStorageFileRepository.getFile(eq(fullPath), anyInt(), isNull())) + .thenThrow(new DocumentStorageException("Some error", new RuntimeException("Some error"))); + assertThatThrownBy(() -> s3Adapter.loadAttachments(context, List.of(path))) + .isInstanceOf(LoadAttachmentError.class); + } + + @Test + void testLoadAttachment_Success() throws DocumentStorageException, DocumentStorageClientErrorException, DocumentStorageServerErrorException { + final Map files = Map.of( + "digiwf_logo.png", "image/png", + "test-pdf.pdf", "application/pdf", + "test-word.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + ); + + for (final Map.Entry file : files.entrySet()) { + try { + final String path = "files/" + file.getKey(); + final byte[] testFile = new ClassPathResource(path).getInputStream().readAllBytes(); + when(documentStorageFileRepository.getFile(anyString(), anyInt(), isNull())).thenReturn(testFile); + + final List fileAttachment = this.s3Adapter.loadAttachments("fileContext", List.of(path)); + + assertThat(Arrays.equals(testFile, fileAttachment.getFirst().getFile().getInputStream().readAllBytes())).isTrue(); + assertThat(file.getKey()).isEqualTo(fileAttachment.getFirst().getFileName()); + assertThat(file.getValue()).isEqualTo(fileAttachment.getFirst().getFile().getContentType()); + } catch (final IOException e) { + log.warn("Could not read file: {}", file); + fail(e.getMessage()); + } + } + } + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java new file mode 100644 index 00000000..ce504e18 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java @@ -0,0 +1,135 @@ +package de.muenchen.refarch.email.integration.application.usecase; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; +import de.muenchen.refarch.email.integration.application.port.out.LoadMailAttachmentOutPort; +import de.muenchen.refarch.email.integration.application.port.out.MailOutPort; +import de.muenchen.refarch.email.integration.domain.exception.TemplateError; +import de.muenchen.refarch.email.integration.domain.model.paths.TemplateMailPaths; +import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; +import de.muenchen.refarch.email.model.FileAttachment; +import freemarker.template.TemplateException; +import jakarta.mail.MessagingException; +import jakarta.mail.util.ByteArrayDataSource; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.mail.MailSendException; + +class SendMailPathsUseCaseTest { + + private final LoadMailAttachmentOutPort loadMailAttachmentOutPort = mock(LoadMailAttachmentOutPort.class); + private final MailOutPort mailOutPort = mock(MailOutPort.class); + private final TextMailPaths mail = new TextMailPaths( + "mailReceiver1@muenchen.de,mailReceiver2@muenchen.de", + "receiverCC@muenchen.de", + "receiverBCC@muenchen.de", + "Test Mail", + "This is a test mail", + "digiwf@muenchen.de", + "fileContext", + "folder/file.txt" + ); + private final TemplateMailPaths templateMail = new TemplateMailPaths( + "mailReceiver1@muenchen.de,mailReceiver2@muenchen.de", + "receiverCC@muenchen.de", + "receiverBCC@muenchen.de", + "Test Mail", + "digiwf@muenchen.de", + "fileContext", + "folder/file.txt", + "template", + Map.of("mail", Map.of()) + ); + private SendMailPathsInPort sendMailPathsInPort; + + @BeforeEach + void setUp() { + this.sendMailPathsInPort = new SendMailPathsUseCase(loadMailAttachmentOutPort, mailOutPort); + } + + @Test + void sendMail() throws MessagingException { + sendMailPathsInPort.sendMailWithText(mail); + final de.muenchen.refarch.email.model.Mail mailOutModel = de.muenchen.refarch.email.model.Mail.builder() + .receivers(mail.getReceivers()) + .subject(mail.getSubject()) + .body(mail.getBody()) + .replyTo(mail.getReplyTo()) + .receiversCc(mail.getReceiversCc()) + .receiversBcc(mail.getReceiversBcc()) + .attachments(List.of()) + .build(); + verify(mailOutPort).sendMail(mailOutModel, null); + } + + @Test + void sendMailWithAttachments() throws MessagingException { + final FileAttachment fileAttachment = new FileAttachment("test.txt", new ByteArrayDataSource("Anhang Inhalt".getBytes(), "text/plain")); + when(loadMailAttachmentOutPort.loadAttachments("fileContext", List.of("folder/file.txt"))).thenReturn(List.of(fileAttachment)); + + sendMailPathsInPort.sendMailWithText(mail); + final de.muenchen.refarch.email.model.Mail mailOutModel = de.muenchen.refarch.email.model.Mail.builder() + .receivers(mail.getReceivers()) + .subject(mail.getSubject()) + .body(mail.getBody()) + .replyTo(mail.getReplyTo()) + .receiversCc(mail.getReceiversCc()) + .receiversBcc(mail.getReceiversBcc()) + .attachments(List.of(fileAttachment)) + .build(); + verify(mailOutPort).sendMail(mailOutModel, null); + } + + @Test + void sendMailThrowsMailSendException() throws MessagingException { + doThrow(new MessagingException("Test Exception")).when(mailOutPort).sendMail(any(), any()); + assertThatThrownBy(() -> sendMailPathsInPort.sendMailWithText(mail)).isInstanceOf(MailSendException.class); + } + + @Test + void sendMailWithTemplate() throws MessagingException, TemplateException, IOException { + when(mailOutPort.getBodyFromTemplate(anyString(), anyMap())).thenReturn("generated body"); + sendMailPathsInPort.sendMailWithTemplate(templateMail); + final de.muenchen.refarch.email.model.Mail mailOutModel = de.muenchen.refarch.email.model.Mail.builder() + .receivers(mail.getReceivers()) + .subject(mail.getSubject()) + .htmlBody(true) + .body("generated body") + .replyTo(mail.getReplyTo()) + .receiversCc(mail.getReceiversCc()) + .receiversBcc(mail.getReceiversBcc()) + .attachments(List.of()) + .build(); + verify(mailOutPort).sendMail(mailOutModel, "templates/email-logo.png"); + } + + @Test + void sendMailWithTemplateThrowsIOException() throws TemplateException, IOException { + doThrow(new IOException("IO Exception")).when(mailOutPort).getBodyFromTemplate(anyString(), anyMap()); + TemplateError error = catchThrowableOfType(() -> sendMailPathsInPort.sendMailWithTemplate(templateMail), TemplateError.class); + + String expectedMessage = "The template " + templateMail.getTemplate() + " could not be loaded"; + String actualMessage = error.getMessage(); + + assertThat(actualMessage).isEqualTo(expectedMessage); + } + + @Test + void sendMailWithTemplateThrowsTemplateException() throws TemplateException, IOException { + TemplateException templateException = mock(TemplateException.class); + when(templateException.getMessage()).thenReturn("Template Exception Message"); + doThrow(templateException).when(mailOutPort).getBodyFromTemplate(anyString(), anyMap()); + TemplateError error = catchThrowableOfType(() -> sendMailPathsInPort.sendMailWithTemplate(templateMail), TemplateError.class); + + String expectedMessage = "Template Exception Message"; + String actualMessage = error.getMessage(); + + assertThat(actualMessage).isEqualTo(expectedMessage); + } + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/digiwf_logo.png b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/digiwf_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7aa3566f4c3d59762fe86a1a638d70b5df9dfc27 GIT binary patch literal 100369 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 z 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 Gfd2#Mpf^JR literal 0 HcmV?d00001 diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-pdf.pdf b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-pdf.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9ee1f262ec21c89a45509810611753538e0165d8 GIT binary patch literal 7386 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 literal 0 HcmV?d00001 diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-word.docx b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-word.docx new file mode 100644 index 0000000000000000000000000000000000000000..43f359865fc96aa8bdbb98d34ab8daa3ee43ea19 GIT binary patch literal 4266 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 + + + + + + + + + %date{yyyy.MM.dd HH:mm:ss.SSS} | %highlight(%level) | [%thread] | %cyan(%logger{0}) | [%file : %line] - + %msg%n + + + + + + + + + + + + + + + From 874abe3b4ca7f77de93ebc595243eccc7a60b73d Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Tue, 6 Aug 2024 09:59:40 +0200 Subject: [PATCH 03/25] :art: email-integration-core format --- .../refarch/email/integration/adapter/out/s3/S3Adapter.java | 6 ++---- .../email/integration/adapter/out/s3/S3AdapterTest.java | 3 +-- .../application/usecase/SendMailPathsUseCaseTest.java | 6 ++---- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java index b8d4c714..0fad29a3 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java @@ -53,8 +53,7 @@ private List getFilesFromFolder(final String folderPath) { throw new LoadAttachmentError("An folder could not be loaded from url: " + folderPath); filepath.forEach(file -> contents.add(getFile(file))); return contents; - } catch (final DocumentStorageException | DocumentStorageServerErrorException | - DocumentStorageClientErrorException e) { + } catch (final DocumentStorageException | DocumentStorageServerErrorException | DocumentStorageClientErrorException e) { throw new LoadAttachmentError("An folder could not be loaded from path: " + folderPath); } } @@ -72,8 +71,7 @@ private FileAttachment getFile(final String filePath) { throw new LoadAttachmentError("The type of this file is not supported: " + filePath); return new FileAttachment(filename, file); - } catch (final DocumentStorageException | DocumentStorageServerErrorException | - DocumentStorageClientErrorException e) { + } catch (final DocumentStorageException | DocumentStorageServerErrorException | DocumentStorageClientErrorException e) { throw new LoadAttachmentError("An file could not be loaded from path: " + filePath); } } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java index 385ebd53..6104837e 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java @@ -60,8 +60,7 @@ void testLoadAttachment_Success() throws DocumentStorageException, DocumentStora final Map files = Map.of( "digiwf_logo.png", "image/png", "test-pdf.pdf", "application/pdf", - "test-word.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" - ); + "test-word.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); for (final Map.Entry file : files.entrySet()) { try { diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java index ce504e18..de17fefa 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java @@ -32,8 +32,7 @@ class SendMailPathsUseCaseTest { "This is a test mail", "digiwf@muenchen.de", "fileContext", - "folder/file.txt" - ); + "folder/file.txt"); private final TemplateMailPaths templateMail = new TemplateMailPaths( "mailReceiver1@muenchen.de,mailReceiver2@muenchen.de", "receiverCC@muenchen.de", @@ -43,8 +42,7 @@ class SendMailPathsUseCaseTest { "fileContext", "folder/file.txt", "template", - Map.of("mail", Map.of()) - ); + Map.of("mail", Map.of())); private SendMailPathsInPort sendMailPathsInPort; @BeforeEach From b3735718f24c3a0f5d223b7a14eaed6abad1d8f1 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Tue, 6 Aug 2024 10:00:24 +0200 Subject: [PATCH 04/25] :sparkles: email-integration init starter --- .../refarch-email-integration/pom.xml | 1 + .../refarch-email-integration-starter/pom.xml | 37 ++++++++++++++ .../configuration/MailAutoConfiguration.java | 48 +++++++++++++++++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + 4 files changed, 87 insertions(+) create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-starter/pom.xml create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports diff --git a/refarch-integrations/refarch-email-integration/pom.xml b/refarch-integrations/refarch-email-integration/pom.xml index 02957627..20e332c3 100644 --- a/refarch-integrations/refarch-email-integration/pom.xml +++ b/refarch-integrations/refarch-email-integration/pom.xml @@ -23,5 +23,6 @@ refarch-email refarch-email-integration-core + refarch-email-integration-starter diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/pom.xml b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/pom.xml new file mode 100644 index 00000000..c9a1d6e6 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + de.muenchen.refarch + refarch-email-integration + 1.1.0-SNAPSHOT + + + refarch-email-integration-starter + + + de.muenchen.refarch + refarch-email-integration-core + ${project.version} + + + org.springframework.boot + spring-boot + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-configuration-processor + true + + + org.springframework.boot + spring-boot-starter + + + diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java new file mode 100644 index 00000000..7bd9384c --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java @@ -0,0 +1,48 @@ +package de.muenchen.refarch.email.integration.configuration; + +import de.muenchen.refarch.email.api.DigiwfEmailApi; +import de.muenchen.refarch.email.integration.adapter.out.mail.MailAdapter; +import de.muenchen.refarch.email.integration.adapter.out.s3.S3Adapter; +import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; +import de.muenchen.refarch.email.integration.application.port.out.LoadMailAttachmentOutPort; +import de.muenchen.refarch.email.integration.application.port.out.MailOutPort; +import de.muenchen.refarch.email.integration.application.usecase.SendMailPathsUseCase; +import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFileRepository; +import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFolderRepository; +import de.muenchen.refarch.s3.integration.client.repository.transfer.S3FileTransferRepository; +import de.muenchen.refarch.s3.integration.client.service.FileService; +import de.muenchen.refarch.s3.integration.client.service.S3StorageUrlProvider; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.mail.MailProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@RequiredArgsConstructor +@EnableConfigurationProperties({ MailProperties.class }) +public class MailAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public SendMailPathsInPort getSendMailPathsInPort(final LoadMailAttachmentOutPort loadAttachmentPort, final MailOutPort mailOutPort) { + return new SendMailPathsUseCase(loadAttachmentPort, mailOutPort); + } + + @Bean + @ConditionalOnMissingBean + public LoadMailAttachmentOutPort getLoadMailAttachmentPort(final S3FileTransferRepository s3FileTransferRepository, + final DocumentStorageFileRepository documentStorageFileRepository, + final DocumentStorageFolderRepository documentStorageFolderRepository, + final FileService fileService, + final S3StorageUrlProvider s3DomainService) { + return new S3Adapter(s3FileTransferRepository, documentStorageFileRepository, documentStorageFolderRepository, fileService, s3DomainService); + } + + @Bean + @ConditionalOnMissingBean + public MailOutPort getMailPort(final DigiwfEmailApi digiwfEmailApi) { + return new MailAdapter(digiwfEmailApi); + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..c5a1177d --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +de.muenchen.refarch.email.integration.configuration.MailAutoConfiguration From 89ec08ee8f3ba311f3f562072f1a65bbe77cdde1 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Wed, 7 Aug 2024 08:14:25 +0200 Subject: [PATCH 05/25] :sparkles: email-integration add to pom --- refarch-integrations/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/refarch-integrations/pom.xml b/refarch-integrations/pom.xml index 6c95c566..03ff3861 100644 --- a/refarch-integrations/pom.xml +++ b/refarch-integrations/pom.xml @@ -18,6 +18,7 @@ refarch-s3-integration + refarch-email-integration From d1601ea756721b4f71b8170f43b1e8b35e4edd7d Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Tue, 13 Aug 2024 08:18:09 +0200 Subject: [PATCH 06/25] :recycle: email integrate s3 refactoring --- .../refarch-email-integration-core/pom.xml | 2 +- .../integration/adapter/out/s3/S3Adapter.java | 26 ++++++++----------- .../adapter/out/s3/S3AdapterTest.java | 25 +++++++----------- .../configuration/MailAutoConfiguration.java | 15 +++++------ 4 files changed, 28 insertions(+), 40 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/pom.xml b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/pom.xml index 7b9ef378..6e18f227 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/pom.xml +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/pom.xml @@ -14,7 +14,7 @@ de.muenchen.refarch - refarch-s3-integration-client-starter + refarch-s3-integration-client-core ${project.version} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java index 0fad29a3..19bc4c42 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java @@ -3,14 +3,12 @@ import de.muenchen.refarch.email.integration.application.port.out.LoadMailAttachmentOutPort; import de.muenchen.refarch.email.integration.domain.exception.LoadAttachmentError; import de.muenchen.refarch.email.model.FileAttachment; -import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageClientErrorException; -import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageException; -import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageServerErrorException; -import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFileRepository; -import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFolderRepository; -import de.muenchen.refarch.s3.integration.client.repository.transfer.S3FileTransferRepository; -import de.muenchen.refarch.s3.integration.client.service.FileService; -import de.muenchen.refarch.s3.integration.client.service.S3StorageUrlProvider; +import de.muenchen.refarch.integration.s3.client.exception.DocumentStorageClientErrorException; +import de.muenchen.refarch.integration.s3.client.exception.DocumentStorageException; +import de.muenchen.refarch.integration.s3.client.exception.DocumentStorageServerErrorException; +import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFileRepository; +import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFolderRepository; +import de.muenchen.refarch.integration.s3.client.service.FileValidationService; import jakarta.mail.util.ByteArrayDataSource; import java.util.ArrayList; import java.util.List; @@ -24,11 +22,9 @@ @RequiredArgsConstructor public class S3Adapter implements LoadMailAttachmentOutPort { - private final S3FileTransferRepository s3FileTransferRepository; private final DocumentStorageFileRepository documentStorageFileRepository; private final DocumentStorageFolderRepository documentStorageFolderRepository; - private final FileService fileService; - private final S3StorageUrlProvider s3DomainService; + private final FileValidationService fileValidationService; @Override public List loadAttachments(final String fileContext, final List filePaths) { @@ -48,7 +44,7 @@ private List getFilesFromFolder(final String folderPath) { try { final List contents = new ArrayList<>(); final Set filepath; - filepath = documentStorageFolderRepository.getAllFilesInFolderRecursively(folderPath, s3DomainService.getDefaultDocumentStorageUrl()).block(); + filepath = documentStorageFolderRepository.getAllFilesInFolderRecursively(folderPath); if (Objects.isNull(filepath)) throw new LoadAttachmentError("An folder could not be loaded from url: " + folderPath); filepath.forEach(file -> contents.add(getFile(file))); @@ -61,13 +57,13 @@ private List getFilesFromFolder(final String folderPath) { private FileAttachment getFile(final String filePath) { try { final byte[] bytes; - bytes = this.documentStorageFileRepository.getFile(filePath, 3, s3DomainService.getDefaultDocumentStorageUrl()); - final String mimeType = fileService.detectFileType(bytes); + bytes = this.documentStorageFileRepository.getFile(filePath, 3); + final String mimeType = fileValidationService.detectFileType(bytes); final String filename = FilenameUtils.getName(filePath); final ByteArrayDataSource file = new ByteArrayDataSource(bytes, mimeType); // check if mimeType exists - if (!fileService.isSupported(mimeType)) + if (!fileValidationService.isSupported(mimeType)) throw new LoadAttachmentError("The type of this file is not supported: " + filePath); return new FileAttachment(filename, file); diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java index 6104837e..86a7c9e5 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java @@ -7,14 +7,12 @@ import de.muenchen.refarch.email.integration.domain.exception.LoadAttachmentError; import de.muenchen.refarch.email.model.FileAttachment; -import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageClientErrorException; -import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageException; -import de.muenchen.refarch.s3.integration.client.exception.DocumentStorageServerErrorException; -import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFileRepository; -import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFolderRepository; -import de.muenchen.refarch.s3.integration.client.repository.transfer.S3FileTransferRepository; -import de.muenchen.refarch.s3.integration.client.service.FileService; -import de.muenchen.refarch.s3.integration.client.service.S3StorageUrlProvider; +import de.muenchen.refarch.integration.s3.client.exception.DocumentStorageClientErrorException; +import de.muenchen.refarch.integration.s3.client.exception.DocumentStorageException; +import de.muenchen.refarch.integration.s3.client.exception.DocumentStorageServerErrorException; +import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFileRepository; +import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFolderRepository; +import de.muenchen.refarch.integration.s3.client.service.FileValidationService; import java.io.IOException; import java.util.Arrays; import java.util.List; @@ -27,18 +25,15 @@ @Slf4j class S3AdapterTest { - - private final S3FileTransferRepository s3FileTransferRepository = mock(S3FileTransferRepository.class); private final DocumentStorageFileRepository documentStorageFileRepository = mock(DocumentStorageFileRepository.class); private final DocumentStorageFolderRepository documentStorageFolderRepository = mock(DocumentStorageFolderRepository.class); - private final S3StorageUrlProvider s3DomainService = mock(S3StorageUrlProvider.class); - private final FileService fileService = new FileService(null, DataSize.ofMegabytes(50), DataSize.ofMegabytes(110)); + private final FileValidationService fileValidationService = new FileValidationService(null, DataSize.ofMegabytes(50), DataSize.ofMegabytes(110)); private S3Adapter s3Adapter; @BeforeEach void setup() { - s3Adapter = new S3Adapter(s3FileTransferRepository, documentStorageFileRepository, documentStorageFolderRepository, fileService, s3DomainService); + s3Adapter = new S3Adapter(documentStorageFileRepository, documentStorageFolderRepository, fileValidationService); } @Test @@ -49,7 +44,7 @@ void testLoadAttachment_DocumentStorageException() final String fullPath = context + "/" + path; // DocumentStorageException - when(documentStorageFileRepository.getFile(eq(fullPath), anyInt(), isNull())) + when(documentStorageFileRepository.getFile(eq(fullPath), anyInt())) .thenThrow(new DocumentStorageException("Some error", new RuntimeException("Some error"))); assertThatThrownBy(() -> s3Adapter.loadAttachments(context, List.of(path))) .isInstanceOf(LoadAttachmentError.class); @@ -66,7 +61,7 @@ void testLoadAttachment_Success() throws DocumentStorageException, DocumentStora try { final String path = "files/" + file.getKey(); final byte[] testFile = new ClassPathResource(path).getInputStream().readAllBytes(); - when(documentStorageFileRepository.getFile(anyString(), anyInt(), isNull())).thenReturn(testFile); + when(documentStorageFileRepository.getFile(anyString(), anyInt())).thenReturn(testFile); final List fileAttachment = this.s3Adapter.loadAttachments("fileContext", List.of(path)); diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java index 7bd9384c..f684cb91 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java @@ -7,11 +7,9 @@ import de.muenchen.refarch.email.integration.application.port.out.LoadMailAttachmentOutPort; import de.muenchen.refarch.email.integration.application.port.out.MailOutPort; import de.muenchen.refarch.email.integration.application.usecase.SendMailPathsUseCase; -import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFileRepository; -import de.muenchen.refarch.s3.integration.client.repository.DocumentStorageFolderRepository; -import de.muenchen.refarch.s3.integration.client.repository.transfer.S3FileTransferRepository; -import de.muenchen.refarch.s3.integration.client.service.FileService; -import de.muenchen.refarch.s3.integration.client.service.S3StorageUrlProvider; +import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFileRepository; +import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFolderRepository; +import de.muenchen.refarch.integration.s3.client.service.FileValidationService; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.mail.MailProperties; @@ -32,12 +30,11 @@ public SendMailPathsInPort getSendMailPathsInPort(final LoadMailAttachmentOutPor @Bean @ConditionalOnMissingBean - public LoadMailAttachmentOutPort getLoadMailAttachmentPort(final S3FileTransferRepository s3FileTransferRepository, + public LoadMailAttachmentOutPort getLoadMailAttachmentPort( final DocumentStorageFileRepository documentStorageFileRepository, final DocumentStorageFolderRepository documentStorageFolderRepository, - final FileService fileService, - final S3StorageUrlProvider s3DomainService) { - return new S3Adapter(s3FileTransferRepository, documentStorageFileRepository, documentStorageFolderRepository, fileService, s3DomainService); + final FileValidationService fileValidationService) { + return new S3Adapter(documentStorageFileRepository, documentStorageFolderRepository, fileValidationService); } @Bean From e6408d191d42cd87155e395fedba6523501b8a33 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Tue, 13 Aug 2024 08:30:55 +0200 Subject: [PATCH 07/25] :recycle: email rm digiwf relations --- .../adapter/out/mail/MailAdapter.java | 8 ++-- .../adapter/out/mail/MailAdapterTest.java | 14 +++---- .../adapter/out/s3/S3AdapterTest.java | 2 +- .../usecase/SendMailPathsUseCaseTest.java | 4 +- .../src/test/resources/files/digiwf_logo.png | Bin 100369 -> 0 bytes .../src/test/resources/files/test-logo.png | Bin 0 -> 10699 bytes .../configuration/MailAutoConfiguration.java | 6 +-- .../{DigiwfEmailApi.java => EmailApi.java} | 2 +- ...iwfEmailApiImpl.java => EmailApiImpl.java} | 4 +- ...ApiImplTest.java => EmailApiImplTest.java} | 38 +++++++++--------- .../DigiwfEmailAutoConfiguration.java | 8 ++-- .../properties/CustomMailProperties.java | 2 +- 12 files changed, 44 insertions(+), 44 deletions(-) delete mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/digiwf_logo.png create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-logo.png rename refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/{DigiwfEmailApi.java => EmailApi.java} (95%) rename refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/{DigiwfEmailApiImpl.java => EmailApiImpl.java} (97%) rename refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/{DigiwfEmailApiImplTest.java => EmailApiImplTest.java} (89%) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapter.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapter.java index 39756c2b..ab4c8ecc 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapter.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapter.java @@ -1,6 +1,6 @@ package de.muenchen.refarch.email.integration.adapter.out.mail; -import de.muenchen.refarch.email.api.DigiwfEmailApi; +import de.muenchen.refarch.email.api.EmailApi; import de.muenchen.refarch.email.integration.application.port.out.MailOutPort; import de.muenchen.refarch.email.model.Mail; import freemarker.template.TemplateException; @@ -12,15 +12,15 @@ @RequiredArgsConstructor public class MailAdapter implements MailOutPort { - private final DigiwfEmailApi digiwfEmailApi; + private final EmailApi emailApi; @Override public void sendMail(Mail mail, String logoPath) throws MessagingException { - this.digiwfEmailApi.sendMail(mail, logoPath); + this.emailApi.sendMail(mail, logoPath); } @Override public String getBodyFromTemplate(String templateName, Map content) throws TemplateException, IOException { - return this.digiwfEmailApi.getBodyFromTemplate(templateName, content); + return this.emailApi.getBodyFromTemplate(templateName, content); } } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java index 66e44746..6fcd727b 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java @@ -3,7 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; -import de.muenchen.refarch.email.api.DigiwfEmailApi; +import de.muenchen.refarch.email.api.EmailApi; import de.muenchen.refarch.email.model.Mail; import freemarker.template.TemplateException; import jakarta.mail.MessagingException; @@ -13,11 +13,11 @@ class MailAdapterTest { - private final DigiwfEmailApi digiwfEmailApi = mock(DigiwfEmailApi.class); + private final EmailApi emailApi = mock(EmailApi.class); @Test void sendMail() throws MessagingException { - final MailAdapter mailAdapter = new MailAdapter(digiwfEmailApi); + final MailAdapter mailAdapter = new MailAdapter(emailApi); final Mail mail = Mail.builder() .receivers("receivers") .subject("subject") @@ -27,17 +27,17 @@ void sendMail() throws MessagingException { .receiversBcc("receiversBcc") .build(); mailAdapter.sendMail(mail, "logoPath"); - verify(digiwfEmailApi).sendMail(mail, "logoPath"); + verify(emailApi).sendMail(mail, "logoPath"); } @Test void getBodyFromTemplate() throws TemplateException, IOException { - final MailAdapter mailAdapter = new MailAdapter(digiwfEmailApi); - when(digiwfEmailApi.getBodyFromTemplate(anyString(), anyMap())).thenReturn("generated body"); + final MailAdapter mailAdapter = new MailAdapter(emailApi); + when(emailApi.getBodyFromTemplate(anyString(), anyMap())).thenReturn("generated body"); String body = mailAdapter.getBodyFromTemplate("template", Map.of("key", "value")); assertThat(body).isEqualTo("generated body"); - verify(digiwfEmailApi).getBodyFromTemplate("template", Map.of("key", "value")); + verify(emailApi).getBodyFromTemplate("template", Map.of("key", "value")); } } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java index 86a7c9e5..7d104da5 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java @@ -53,7 +53,7 @@ void testLoadAttachment_DocumentStorageException() @Test void testLoadAttachment_Success() throws DocumentStorageException, DocumentStorageClientErrorException, DocumentStorageServerErrorException { final Map files = Map.of( - "digiwf_logo.png", "image/png", + "test-logo.png", "image/png", "test-pdf.pdf", "application/pdf", "test-word.docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java index de17fefa..921458e1 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java @@ -30,7 +30,7 @@ class SendMailPathsUseCaseTest { "receiverBCC@muenchen.de", "Test Mail", "This is a test mail", - "digiwf@muenchen.de", + "test@muenchen.de", "fileContext", "folder/file.txt"); private final TemplateMailPaths templateMail = new TemplateMailPaths( @@ -38,7 +38,7 @@ class SendMailPathsUseCaseTest { "receiverCC@muenchen.de", "receiverBCC@muenchen.de", "Test Mail", - "digiwf@muenchen.de", + "test@muenchen.de", "fileContext", "folder/file.txt", "template", diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/digiwf_logo.png b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/digiwf_logo.png deleted file mode 100644 index 7aa3566f4c3d59762fe86a1a638d70b5df9dfc27..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100369 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 z 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 Gfd2#Mpf^JR diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-logo.png b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1db73ea06c29c65dfa3b48e937e3824452c9036e GIT binary patch literal 10699 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 UQG21_k|xC9?4>g$y6{{72f+Nn`Tzg` literal 0 HcmV?d00001 diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java index f684cb91..9b9dba2d 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java @@ -1,6 +1,6 @@ package de.muenchen.refarch.email.integration.configuration; -import de.muenchen.refarch.email.api.DigiwfEmailApi; +import de.muenchen.refarch.email.api.EmailApi; import de.muenchen.refarch.email.integration.adapter.out.mail.MailAdapter; import de.muenchen.refarch.email.integration.adapter.out.s3.S3Adapter; import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; @@ -39,7 +39,7 @@ public LoadMailAttachmentOutPort getLoadMailAttachmentPort( @Bean @ConditionalOnMissingBean - public MailOutPort getMailPort(final DigiwfEmailApi digiwfEmailApi) { - return new MailAdapter(digiwfEmailApi); + public MailOutPort getMailPort(final EmailApi emailApi) { + return new MailAdapter(emailApi); } } diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/DigiwfEmailApi.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/EmailApi.java similarity index 95% rename from refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/DigiwfEmailApi.java rename to refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/EmailApi.java index 074a1e7c..f94ad523 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/DigiwfEmailApi.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/api/EmailApi.java @@ -7,7 +7,7 @@ import java.io.IOException; import java.util.Map; -public interface DigiwfEmailApi { +public interface EmailApi { void sendMail(@Valid Mail mail) throws MessagingException; diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/DigiwfEmailApiImpl.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/EmailApiImpl.java similarity index 97% rename from refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/DigiwfEmailApiImpl.java rename to refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/EmailApiImpl.java index 2b712988..9261ceb9 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/DigiwfEmailApiImpl.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/EmailApiImpl.java @@ -1,6 +1,6 @@ package de.muenchen.refarch.email.impl; -import de.muenchen.refarch.email.api.DigiwfEmailApi; +import de.muenchen.refarch.email.api.EmailApi; import de.muenchen.refarch.email.model.Mail; import freemarker.template.Template; import freemarker.template.TemplateException; @@ -27,7 +27,7 @@ @Slf4j @RequiredArgsConstructor -public class DigiwfEmailApiImpl implements DigiwfEmailApi { +public class EmailApiImpl implements EmailApi { private final JavaMailSender mailSender; private final ResourceLoader resourceLoader; diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/DigiwfEmailApiImplTest.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java similarity index 89% rename from refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/DigiwfEmailApiImplTest.java rename to refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java index f174b4ea..6f5b1dc3 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/DigiwfEmailApiImplTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java @@ -4,8 +4,8 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.mockito.Mockito.*; -import de.muenchen.refarch.email.api.DigiwfEmailApi; -import de.muenchen.refarch.email.impl.DigiwfEmailApiImpl; +import de.muenchen.refarch.email.api.EmailApi; +import de.muenchen.refarch.email.impl.EmailApiImpl; import de.muenchen.refarch.email.model.FileAttachment; import de.muenchen.refarch.email.model.Mail; import freemarker.template.Configuration; @@ -32,7 +32,7 @@ import org.springframework.mail.javamail.JavaMailSender; import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer; -class DigiwfEmailApiImplTest { +class EmailApiImplTest { private final JavaMailSender javaMailSender = mock(JavaMailSender.class); private final ResourceLoader resourceLoader = mock(ResourceLoader.class); @@ -43,15 +43,15 @@ class DigiwfEmailApiImplTest { private final String receiverBCC = "receiverBCC@muenchen.de"; private final String subject = "Test Mail"; private final String body = "This is a test mail"; - private final String replyTo = "digiwf@muenchen.de"; + private final String replyTo = "test@muenchen.de"; private final String defaultReplyTo = "noreply@muenchen.de"; private final String sender = "some-custom-sender@muenchen.de"; - private DigiwfEmailApi digiwfEmailApi; + private EmailApi emailApi; @BeforeEach void setUp() { when(this.javaMailSender.createMimeMessage()).thenReturn(new MimeMessage((Session) null)); - this.digiwfEmailApi = new DigiwfEmailApiImpl(this.javaMailSender, this.resourceLoader, freeMarkerConfigurer, "digiwf@muenchen.de", defaultReplyTo); + this.emailApi = new EmailApiImpl(this.javaMailSender, this.resourceLoader, freeMarkerConfigurer, "test@muenchen.de", defaultReplyTo); } @Test @@ -61,7 +61,7 @@ void testSendMail() throws MessagingException, IOException { .subject(this.subject) .body(this.body) .build(); - this.digiwfEmailApi.sendMail(mail); + this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); verify(this.javaMailSender).send(messageArgumentCaptor.capture()); @@ -76,14 +76,14 @@ void testSendMail() throws MessagingException, IOException { @Test void testSendMailNoDefaultReplyTo() throws MessagingException, IOException { - var customAddress = new InternetAddress("custom.digiwf@muenchen.de"); + var customAddress = new InternetAddress("custom.test@muenchen.de"); final Mail mail = Mail.builder() .receivers(this.receiver) .subject(this.subject) .body(this.body) .build(); - new DigiwfEmailApiImpl(this.javaMailSender, this.resourceLoader, freeMarkerConfigurer, customAddress.getAddress(), null).sendMail(mail); + new EmailApiImpl(this.javaMailSender, this.resourceLoader, freeMarkerConfigurer, customAddress.getAddress(), null).sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); verify(this.javaMailSender).send(messageArgumentCaptor.capture()); @@ -107,7 +107,7 @@ void testSendMailWithOptions() throws MessagingException, IOException { .receiversBcc(this.receiverBCC) .sender(this.sender) .build(); - this.digiwfEmailApi.sendMail(mail); + this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); verify(this.javaMailSender).send(messageArgumentCaptor.capture()); @@ -133,7 +133,7 @@ void sendMailWithAttachments() throws MessagingException, IOException { .attachments(List.of( new FileAttachment("Testanhang", new ByteArrayDataSource("FooBar".getBytes(), "text/plain")))) .build(); - this.digiwfEmailApi.sendMail(mail); + this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); verify(this.javaMailSender).send(messageArgumentCaptor.capture()); @@ -155,7 +155,7 @@ void sendMailWithMultipleReplyToAddresses() throws MessagingException, IOExcepti .body(this.body) .replyTo(reply1.getAddress() + "," + reply2.getAddress()) .build(); - this.digiwfEmailApi.sendMail(mail); + this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); verify(this.javaMailSender).send(messageArgumentCaptor.capture()); @@ -177,7 +177,7 @@ void sendMailWithDefaultLogo() throws MessagingException, IOException { .subject(this.subject) .body(this.body) .build(); - this.digiwfEmailApi.sendMailWithDefaultLogo(mail); + this.emailApi.sendMailWithDefaultLogo(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); verify(this.javaMailSender).send(messageArgumentCaptor.capture()); @@ -198,7 +198,7 @@ void sendMailWithCustomLogo() throws MessagingException, IOException { .subject(this.subject) .body(this.body) .build(); - this.digiwfEmailApi.sendMail(mail, "some/random/path/on/classpath"); + this.emailApi.sendMail(mail, "some/random/path/on/classpath"); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); verify(this.javaMailSender).send(messageArgumentCaptor.capture()); @@ -218,7 +218,7 @@ void testGetBodyFromFreemarkerTemplate() throws IOException, TemplateException { configuration.setClassForTemplateLoading(this.getClass(), "/templates/"); when(this.freeMarkerConfigurer.getConfiguration()).thenReturn(configuration); - final String result = this.digiwfEmailApi.getBodyFromTemplate(templateName, content); + final String result = this.emailApi.getBodyFromTemplate(templateName, content); assertThat(result.contains("test")).isTrue(); } @@ -228,7 +228,7 @@ void testGetEmailBodyFromTemplate() { when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("This is a test mail", true)); final String templatePath = "bausteine/mail/email-logo.png"; - final String result = this.digiwfEmailApi.getEmailBodyFromTemplate(templatePath, Map.of()); + final String result = this.emailApi.getEmailBodyFromTemplate(templatePath, Map.of()); assertThat(result).isEqualTo("This is a test mail"); } @@ -238,7 +238,7 @@ void testGetEmailBodyFromTemplateWithContent() { when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("This is a test mail with content", true)); final String templatePath = "bausteine/mail/email-logo.png"; - final String result = this.digiwfEmailApi.getEmailBodyFromTemplate(templatePath, Map.of("content", "some content")); + final String result = this.emailApi.getEmailBodyFromTemplate(templatePath, Map.of("content", "some content")); assertThat(result).isEqualTo("This is a test mail with some content"); } @@ -248,7 +248,7 @@ void testGetEmailBodyFromTemplateWithContentAndNewLines() { when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("This is a test mail with content", true)); final String templatePath = "bausteine/mail/email-logo.png"; - final String result = this.digiwfEmailApi.getEmailBodyFromTemplate(templatePath, Map.of("content", "some content \n with new line")); + final String result = this.emailApi.getEmailBodyFromTemplate(templatePath, Map.of("content", "some content \n with new line")); assertThat(result).isEqualTo("This is a test mail with some content
with new line"); } @@ -259,7 +259,7 @@ void testGetEmailBodyFromTemplateWithContentFailsIfTemplateDoesNotExist() { final String templatePath = "some/temlate/that/does/not/exist"; assertThatThrownBy(() -> { - this.digiwfEmailApi.getEmailBodyFromTemplate(templatePath, Map.of("content", "some content")); + this.emailApi.getEmailBodyFromTemplate(templatePath, Map.of("content", "some content")); }) .isInstanceOf(RuntimeException.class) .hasMessageContaining("Email Template not found: " + templatePath); diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java index bf2897b5..177f3a85 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java @@ -1,7 +1,7 @@ package de.muenchen.refarch.email.configuration; -import de.muenchen.refarch.email.api.DigiwfEmailApi; -import de.muenchen.refarch.email.impl.DigiwfEmailApiImpl; +import de.muenchen.refarch.email.api.EmailApi; +import de.muenchen.refarch.email.impl.EmailApiImpl; import de.muenchen.refarch.email.properties.CustomMailProperties; import jakarta.mail.MessagingException; import java.util.Properties; @@ -46,9 +46,9 @@ public JavaMailSender getJavaMailSender() throws MessagingException { @ConditionalOnMissingBean @Bean - public DigiwfEmailApi digiwfEmailApi(final ResourceLoader resourceLoader, final JavaMailSender javaMailSender, + public EmailApi emailApi(final ResourceLoader resourceLoader, final JavaMailSender javaMailSender, final FreeMarkerConfigurer freeMarkerConfigurer) { - return new DigiwfEmailApiImpl(javaMailSender, resourceLoader, freeMarkerConfigurer, this.customMailProperties.getFromAddress(), + return new EmailApiImpl(javaMailSender, resourceLoader, freeMarkerConfigurer, this.customMailProperties.getFromAddress(), this.customMailProperties.getDefaultReplyToAddress()); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/properties/CustomMailProperties.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/properties/CustomMailProperties.java index b111c5d3..af903421 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/properties/CustomMailProperties.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/properties/CustomMailProperties.java @@ -4,7 +4,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @Data -@ConfigurationProperties(prefix = "io.muenchendigital.digiwf.mail") +@ConfigurationProperties(prefix = "refarch.mail") public class CustomMailProperties { /** From 1609118b31310c0ea65ba93c7928f056d6b28793 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Tue, 13 Aug 2024 08:43:39 +0200 Subject: [PATCH 08/25] :memo: email init README --- refarch-integrations/README.md | 2 ++ .../refarch-email-integration/README.md | 31 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 refarch-integrations/refarch-email-integration/README.md diff --git a/refarch-integrations/README.md b/refarch-integrations/README.md index db260585..280a3158 100644 --- a/refarch-integrations/README.md +++ b/refarch-integrations/README.md @@ -6,6 +6,8 @@ Collection of different integration which can be used as is in RefArch projects. - [s3-integration](./refarch-s3-integration/README.md): For CRUD operations on a s3 storage. Also used for file handling in other integrations. +- [email-integration](./refarch-email-integration/README.md): For sending text and html emails with attachments. Uses + s3-integration for file handling. ## Naming conventions diff --git a/refarch-integrations/refarch-email-integration/README.md b/refarch-integrations/refarch-email-integration/README.md new file mode 100644 index 00000000..6c3fc085 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/README.md @@ -0,0 +1,31 @@ +# RefArch email integration + +Integration for sending text and html emails with attachments. Uses [s3-integration](../refarch-s3-integration) for file +handling. + +## Usage + +```xml + + + + de.muenchen.refarch + refarch-email-integration-starter + ... + + +``` + +and a [s3-integration starter](../refarch-s3-integration/README.md#usage). + +## Configuration + +### refarch-email-integration-starter + +| Property | Description | Example | +|-----------------------------------------|---------------------------------------------------|---------| +| `spring.mail.host` | Host of smtp server used for sending mails. | | +| `spring.mail.username` | Username of smtp server. | | +| `spring.mail.password` | Password of smtp server. | | +| `refarch.mail.from-address` | Default from address used when sending mails. | | +| `refarch.mail.default-reply-to-address` | Default reply to address used when sending mails. | | From b7dc3ca236c0c6a41393e6d1025c3cc6f5d9240f Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Tue, 13 Aug 2024 12:49:33 +0200 Subject: [PATCH 09/25] :fire: email core rm digiwf fileContext --- .../email/integration/adapter/out/s3/S3Adapter.java | 9 ++++----- .../application/port/out/LoadMailAttachmentOutPort.java | 2 +- .../application/usecase/SendMailPathsUseCase.java | 2 +- .../integration/domain/model/paths/BasicMailPaths.java | 7 +------ .../email/integration/adapter/out/s3/S3AdapterTest.java | 8 +++----- .../application/usecase/SendMailPathsUseCaseTest.java | 2 +- 6 files changed, 11 insertions(+), 19 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java index 19bc4c42..35714783 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3Adapter.java @@ -27,14 +27,13 @@ public class S3Adapter implements LoadMailAttachmentOutPort { private final FileValidationService fileValidationService; @Override - public List loadAttachments(final String fileContext, final List filePaths) { + public List loadAttachments(final List filePaths) { final List attachments = new ArrayList<>(); filePaths.forEach(path -> { - final String fullPath = fileContext + "/" + path; - if (fullPath.endsWith("/")) { - attachments.addAll(getFilesFromFolder(fullPath)); + if (path.endsWith("/")) { + attachments.addAll(getFilesFromFolder(path)); } else { - attachments.add(getFile(fullPath)); + attachments.add(getFile(path)); } }); return attachments; diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/LoadMailAttachmentOutPort.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/LoadMailAttachmentOutPort.java index 5c4b0f5a..f56157a4 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/LoadMailAttachmentOutPort.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/out/LoadMailAttachmentOutPort.java @@ -4,5 +4,5 @@ import java.util.List; public interface LoadMailAttachmentOutPort { - List loadAttachments(final String fileContext, final List filePaths); + List loadAttachments(final List filePaths); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java index 60c7f57f..e8655a64 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java @@ -64,7 +64,7 @@ public void sendMailWithTemplate(@Valid final TemplateMailPaths mail) throws Tem private de.muenchen.refarch.email.model.Mail createMail(BasicMailPaths mail) { // load Attachments - List attachments = loadAttachmentOutPort.loadAttachments(mail.getFileContext(), mail.parseFilePaths()); + List attachments = loadAttachmentOutPort.loadAttachments(mail.parseFilePaths()); // send mail return de.muenchen.refarch.email.model.Mail.builder() diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java index eddef682..9db7d855 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java @@ -1,27 +1,22 @@ package de.muenchen.refarch.email.integration.domain.model.paths; import de.muenchen.refarch.email.integration.domain.model.BasicMail; -import jakarta.validation.constraints.NotBlank; +import java.util.List; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.StringUtils; -import java.util.List; - @EqualsAndHashCode(callSuper = true) @Data @RequiredArgsConstructor @AllArgsConstructor public class BasicMailPaths extends BasicMail { - @NotBlank - private String fileContext; private String filePaths; public BasicMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, String fileContext, String filePaths) { super(receivers, receiversCc, receiversBcc, subject, replyTo); - this.fileContext = fileContext; this.filePaths = filePaths; } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java index 7d104da5..8565a45c 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java @@ -40,13 +40,11 @@ void setup() { void testLoadAttachment_DocumentStorageException() throws DocumentStorageException, DocumentStorageClientErrorException, DocumentStorageServerErrorException { final String path = "path/to/some-file.txt"; - final String context = "fileContext"; - final String fullPath = context + "/" + path; // DocumentStorageException - when(documentStorageFileRepository.getFile(eq(fullPath), anyInt())) + when(documentStorageFileRepository.getFile(eq(path), anyInt())) .thenThrow(new DocumentStorageException("Some error", new RuntimeException("Some error"))); - assertThatThrownBy(() -> s3Adapter.loadAttachments(context, List.of(path))) + assertThatThrownBy(() -> s3Adapter.loadAttachments(List.of(path))) .isInstanceOf(LoadAttachmentError.class); } @@ -63,7 +61,7 @@ void testLoadAttachment_Success() throws DocumentStorageException, DocumentStora final byte[] testFile = new ClassPathResource(path).getInputStream().readAllBytes(); when(documentStorageFileRepository.getFile(anyString(), anyInt())).thenReturn(testFile); - final List fileAttachment = this.s3Adapter.loadAttachments("fileContext", List.of(path)); + final List fileAttachment = this.s3Adapter.loadAttachments(List.of(path)); assertThat(Arrays.equals(testFile, fileAttachment.getFirst().getFile().getInputStream().readAllBytes())).isTrue(); assertThat(file.getKey()).isEqualTo(fileAttachment.getFirst().getFileName()); diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java index 921458e1..b5fc70ce 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java @@ -68,7 +68,7 @@ void sendMail() throws MessagingException { @Test void sendMailWithAttachments() throws MessagingException { final FileAttachment fileAttachment = new FileAttachment("test.txt", new ByteArrayDataSource("Anhang Inhalt".getBytes(), "text/plain")); - when(loadMailAttachmentOutPort.loadAttachments("fileContext", List.of("folder/file.txt"))).thenReturn(List.of(fileAttachment)); + when(loadMailAttachmentOutPort.loadAttachments(List.of("folder/file.txt"))).thenReturn(List.of(fileAttachment)); sendMailPathsInPort.sendMailWithText(mail); final de.muenchen.refarch.email.model.Mail mailOutModel = de.muenchen.refarch.email.model.Mail.builder() From 217a25f2064258e02598c377e7ae9454ac291db5 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Wed, 14 Aug 2024 08:21:42 +0200 Subject: [PATCH 10/25] :sparkles: email integration init rest example --- .../refarch-email-integration/pom.xml | 1 + .../pom.xml | 34 +++++++++++++++++ .../EmailRestExampleApplication.java | 23 +++++++++++ .../email/integration/TestService.java | 36 ++++++++++++++++++ .../src/main/resources/application-local.yml | 20 ++++++++++ .../src/main/resources/files/test-pdf.pdf | Bin 0 -> 7386 bytes 6 files changed, 114 insertions(+) create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/pom.xml create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/EmailRestExampleApplication.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/application-local.yml create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/files/test-pdf.pdf diff --git a/refarch-integrations/refarch-email-integration/pom.xml b/refarch-integrations/refarch-email-integration/pom.xml index 20e332c3..dd909136 100644 --- a/refarch-integrations/refarch-email-integration/pom.xml +++ b/refarch-integrations/refarch-email-integration/pom.xml @@ -24,5 +24,6 @@ refarch-email refarch-email-integration-core refarch-email-integration-starter + refarch-email-integration-rest-example diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/pom.xml b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/pom.xml new file mode 100644 index 00000000..cc507097 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + + de.muenchen.refarch + refarch-email-integration + 1.1.0-SNAPSHOT + + + refarch-email-integration-rest-example + + + de.muenchen.refarch + refarch-email-integration-starter + ${project.version} + + + de.muenchen.refarch + refarch-s3-integration-rest-client-starter + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/EmailRestExampleApplication.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/EmailRestExampleApplication.java new file mode 100644 index 00000000..961eafdb --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/EmailRestExampleApplication.java @@ -0,0 +1,23 @@ +package de.muenchen.refarch.email.integration; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; + +@SpringBootApplication +public class EmailRestExampleApplication { + @Autowired + TestService testService; + + public static void main(final String[] args) { + SpringApplication.run(EmailRestExampleApplication.class, args); + } + + @EventListener(ApplicationReadyEvent.class) + void sendTestMail() { + this.testService.testSendMail(); + System.exit(0); + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java new file mode 100644 index 00000000..4221e3c0 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java @@ -0,0 +1,36 @@ +package de.muenchen.refarch.email.integration; + +import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; +import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; +import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFileRepository; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class TestService { + private final SendMailPathsInPort sendMailPathsInPort; + private final DocumentStorageFileRepository documentStorageFileRepository; + + void testSendMail() { + this.uploadTestFile(); + TextMailPaths mail = new TextMailPaths(); + mail.setReceivers("test.receiver@muenchen.de"); + mail.setSubject("Test"); + mail.setBody("This is a test"); + mail.setFilePaths("/test/test-pdf.pdf"); + sendMailPathsInPort.sendMailWithText(mail); + log.info("Test mail sent"); + } + + @SneakyThrows + void uploadTestFile() { + ClassPathResource resource = new ClassPathResource("/files/test-pdf.pdf"); + documentStorageFileRepository.updateFile("/test/test-pdf.pdf", resource.getContentAsByteArray(), 1); + log.info("Test file uploaded"); + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/application-local.yml b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/application-local.yml new file mode 100644 index 00000000..8d04673b --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/application-local.yml @@ -0,0 +1,20 @@ +server: + port: 8087 +spring: + mail: + host: localhost + port: 1025 + username: test@muenchen.de + password: secret +refarch: + mail: + from-address: test@muenchen.de + default-reply-to-address: no_reply@muenchen.de + security: + sso-issuer-url: http://keycloak:8100/auth/realms/local_realm + s3: + client: + document-storage-url: http://localhost:8086 + enable-security: true + client-id: local + client-secret: client_secret diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/files/test-pdf.pdf b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/files/test-pdf.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9ee1f262ec21c89a45509810611753538e0165d8 GIT binary patch literal 7386 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 literal 0 HcmV?d00001 From 7f637d249ed1d5d2fe62d69363784b0792fb3fdc Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Wed, 14 Aug 2024 08:25:21 +0200 Subject: [PATCH 11/25] :sparkles: email integration init java example --- .../refarch-email-integration/pom.xml | 1 + .../pom.xml | 43 ++++++++++++++++++ .../EmailJavaExampleApplication.java | 23 ++++++++++ .../email/integration/TestService.java | 36 +++++++++++++++ .../src/main/resources/application-local.yml | 17 +++++++ .../src/main}/resources/files/test-pdf.pdf | Bin 6 files changed, 120 insertions(+) create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/pom.xml create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/EmailJavaExampleApplication.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/resources/application-local.yml rename refarch-integrations/refarch-email-integration/{refarch-email-integration-core/src/test => refarch-email-integration-java-example/src/main}/resources/files/test-pdf.pdf (100%) diff --git a/refarch-integrations/refarch-email-integration/pom.xml b/refarch-integrations/refarch-email-integration/pom.xml index dd909136..adf903c4 100644 --- a/refarch-integrations/refarch-email-integration/pom.xml +++ b/refarch-integrations/refarch-email-integration/pom.xml @@ -25,5 +25,6 @@ refarch-email-integration-core refarch-email-integration-starter refarch-email-integration-rest-example + refarch-email-integration-java-example diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/pom.xml b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/pom.xml new file mode 100644 index 00000000..b9d98efb --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + de.muenchen.refarch + refarch-email-integration + 1.1.0-SNAPSHOT + + + refarch-email-integration-java-example + + + de.muenchen.refarch + refarch-email-integration-starter + ${project.version} + + + de.muenchen.refarch + refarch-s3-integration-java-client-starter + ${project.version} + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/EmailJavaExampleApplication.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/EmailJavaExampleApplication.java new file mode 100644 index 00000000..fd26a305 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/EmailJavaExampleApplication.java @@ -0,0 +1,23 @@ +package de.muenchen.refarch.email.integration; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; + +@SpringBootApplication +public class EmailJavaExampleApplication { + @Autowired + TestService testService; + + public static void main(final String[] args) { + SpringApplication.run(EmailJavaExampleApplication.class, args); + } + + @EventListener(ApplicationReadyEvent.class) + void sendTestMail() { + this.testService.testSendMail(); + System.exit(0); + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java new file mode 100644 index 00000000..4221e3c0 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java @@ -0,0 +1,36 @@ +package de.muenchen.refarch.email.integration; + +import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; +import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; +import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFileRepository; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.io.ClassPathResource; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +@Slf4j +public class TestService { + private final SendMailPathsInPort sendMailPathsInPort; + private final DocumentStorageFileRepository documentStorageFileRepository; + + void testSendMail() { + this.uploadTestFile(); + TextMailPaths mail = new TextMailPaths(); + mail.setReceivers("test.receiver@muenchen.de"); + mail.setSubject("Test"); + mail.setBody("This is a test"); + mail.setFilePaths("/test/test-pdf.pdf"); + sendMailPathsInPort.sendMailWithText(mail); + log.info("Test mail sent"); + } + + @SneakyThrows + void uploadTestFile() { + ClassPathResource resource = new ClassPathResource("/files/test-pdf.pdf"); + documentStorageFileRepository.updateFile("/test/test-pdf.pdf", resource.getContentAsByteArray(), 1); + log.info("Test file uploaded"); + } +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/resources/application-local.yml b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/resources/application-local.yml new file mode 100644 index 00000000..7af049c8 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/resources/application-local.yml @@ -0,0 +1,17 @@ +server: + port: 8087 +spring: + mail: + host: localhost + port: 1025 + username: test@muenchen.de + password: secret +refarch: + mail: + from-address: test@muenchen.de + default-reply-to-address: no_reply@muenchen.de + s3: + bucket-name: test-bucket + access-key: minio + secret-key: Test1234 + url: http://localhost:9000 diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-pdf.pdf b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/resources/files/test-pdf.pdf similarity index 100% rename from refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-pdf.pdf rename to refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/resources/files/test-pdf.pdf From 46d9aaabdcdd7fcc54795215be2d0cff0247ac93 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Wed, 14 Aug 2024 10:14:49 +0200 Subject: [PATCH 12/25] :recycle: email integration use list for paths instead of splitting --- .../application/usecase/SendMailPathsUseCase.java | 2 +- .../domain/model/paths/BasicMailPaths.java | 12 +++--------- .../domain/model/paths/TemplateMailPaths.java | 8 ++++---- .../domain/model/paths/TextMailPaths.java | 7 ++++--- .../usecase/SendMailPathsUseCaseTest.java | 6 ++---- 5 files changed, 14 insertions(+), 21 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java index e8655a64..d779e701 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java @@ -64,7 +64,7 @@ public void sendMailWithTemplate(@Valid final TemplateMailPaths mail) throws Tem private de.muenchen.refarch.email.model.Mail createMail(BasicMailPaths mail) { // load Attachments - List attachments = loadAttachmentOutPort.loadAttachments(mail.parseFilePaths()); + List attachments = loadAttachmentOutPort.loadAttachments(mail.getFilePaths()); // send mail return de.muenchen.refarch.email.model.Mail.builder() diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java index 9db7d855..d73b5bca 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java @@ -6,23 +6,17 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; -import org.apache.commons.lang3.StringUtils; @EqualsAndHashCode(callSuper = true) @Data @RequiredArgsConstructor @AllArgsConstructor public class BasicMailPaths extends BasicMail { - private String filePaths; + // TODO use List + private List filePaths; - public BasicMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, String fileContext, String filePaths) { + public BasicMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, List filePaths) { super(receivers, receiversCc, receiversBcc, subject, replyTo); this.filePaths = filePaths; } - - public List parseFilePaths() { - if (StringUtils.isBlank(filePaths)) - return List.of(); - return List.of(filePaths.split("[,;]")); - } } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java index 39117ec2..e7f200ce 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java @@ -2,13 +2,13 @@ import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; +import java.util.List; +import java.util.Map; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.RequiredArgsConstructor; -import java.util.Map; - @EqualsAndHashCode(callSuper = true) @Data @RequiredArgsConstructor @@ -27,9 +27,9 @@ public class TemplateMailPaths extends BasicMailPaths { @NotEmpty(message = "No content given") private Map content; - public TemplateMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, String fileContext, String filePaths, + public TemplateMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, List filePaths, String template, Map content) { - super(receivers, receiversCc, receiversBcc, subject, replyTo, fileContext, filePaths); + super(receivers, receiversCc, receiversBcc, subject, replyTo, filePaths); this.template = template; this.content = content; } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java index e65cdea9..a55b44fe 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java @@ -1,6 +1,7 @@ package de.muenchen.refarch.email.integration.domain.model.paths; import jakarta.validation.constraints.NotBlank; +import java.util.List; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; @@ -21,9 +22,9 @@ public class TextMailPaths extends BasicMailPaths { @NotBlank(message = "No body given") private String body; - public TextMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String body, String replyTo, String fileContext, - String filePath) { - super(receivers, receiversCc, receiversBcc, subject, replyTo, fileContext, filePath); + public TextMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String body, String replyTo, + List filePaths) { + super(receivers, receiversCc, receiversBcc, subject, replyTo, filePaths); this.body = body; } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java index b5fc70ce..d9d54424 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java @@ -31,16 +31,14 @@ class SendMailPathsUseCaseTest { "Test Mail", "This is a test mail", "test@muenchen.de", - "fileContext", - "folder/file.txt"); + List.of("folder/file.txt")); private final TemplateMailPaths templateMail = new TemplateMailPaths( "mailReceiver1@muenchen.de,mailReceiver2@muenchen.de", "receiverCC@muenchen.de", "receiverBCC@muenchen.de", "Test Mail", "test@muenchen.de", - "fileContext", - "folder/file.txt", + List.of("folder/file.txt"), "template", Map.of("mail", Map.of())); private SendMailPathsInPort sendMailPathsInPort; From 6809ae6de34b40abdac7cc16a16aa557a6031a15 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Wed, 14 Aug 2024 10:15:34 +0200 Subject: [PATCH 13/25] :recycle: email integration use list for paths instead of splitting --- .../de/muenchen/refarch/email/integration/TestService.java | 3 ++- .../de/muenchen/refarch/email/integration/TestService.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java index 4221e3c0..4f9174f0 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java @@ -3,6 +3,7 @@ import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFileRepository; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -22,7 +23,7 @@ void testSendMail() { mail.setReceivers("test.receiver@muenchen.de"); mail.setSubject("Test"); mail.setBody("This is a test"); - mail.setFilePaths("/test/test-pdf.pdf"); + mail.setFilePaths(List.of("/test/test-pdf.pdf")); sendMailPathsInPort.sendMailWithText(mail); log.info("Test mail sent"); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java index 4221e3c0..4f9174f0 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java @@ -3,6 +3,7 @@ import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFileRepository; +import java.util.List; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -22,7 +23,7 @@ void testSendMail() { mail.setReceivers("test.receiver@muenchen.de"); mail.setSubject("Test"); mail.setBody("This is a test"); - mail.setFilePaths("/test/test-pdf.pdf"); + mail.setFilePaths(List.of("/test/test-pdf.pdf")); sendMailPathsInPort.sendMailWithText(mail); log.info("Test mail sent"); } From 9038a3481b6a96ef292f6e4080070968027278d7 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Wed, 14 Aug 2024 10:19:45 +0200 Subject: [PATCH 14/25] :recycle: email integration rm Path naming --- .../application/port/in/SendMailInPort.java | 13 +++++++++ .../port/in/SendMailPathsInPort.java | 13 --------- ...PathsUseCase.java => SendMailUseCase.java} | 16 +++++------ .../integration/domain/model/BasicMail.java | 5 ++++ ...mplateMailPaths.java => TemplateMail.java} | 6 ++-- .../TextMailPaths.java => TextMail.java} | 6 ++-- .../domain/model/paths/BasicMailPaths.java | 22 --------------- ...CaseTest.java => SendMailUseCaseTest.java} | 28 +++++++++---------- .../email/integration/TestService.java | 10 +++---- .../email/integration/TestService.java | 10 +++---- .../configuration/MailAutoConfiguration.java | 8 +++--- 11 files changed, 60 insertions(+), 77 deletions(-) create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailInPort.java delete mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailPathsInPort.java rename refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/{SendMailPathsUseCase.java => SendMailUseCase.java} (85%) rename refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/{paths/TemplateMailPaths.java => TemplateMail.java} (75%) rename refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/{paths/TextMailPaths.java => TextMail.java} (71%) delete mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java rename refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/{SendMailPathsUseCaseTest.java => SendMailUseCaseTest.java} (84%) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailInPort.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailInPort.java new file mode 100644 index 00000000..33bd8a94 --- /dev/null +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailInPort.java @@ -0,0 +1,13 @@ +package de.muenchen.refarch.email.integration.application.port.in; + +import de.muenchen.refarch.email.integration.domain.model.TemplateMail; +import de.muenchen.refarch.email.integration.domain.model.TextMail; +import jakarta.validation.Valid; + +public interface SendMailInPort { + + void sendMailWithText(@Valid final TextMail mail); + + void sendMailWithTemplate(@Valid final TemplateMail mail); + +} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailPathsInPort.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailPathsInPort.java deleted file mode 100644 index 69ac37df..00000000 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/port/in/SendMailPathsInPort.java +++ /dev/null @@ -1,13 +0,0 @@ -package de.muenchen.refarch.email.integration.application.port.in; - -import de.muenchen.refarch.email.integration.domain.model.paths.TemplateMailPaths; -import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; -import jakarta.validation.Valid; - -public interface SendMailPathsInPort { - - void sendMailWithText(@Valid final TextMailPaths mail); - - void sendMailWithTemplate(@Valid final TemplateMailPaths mail); - -} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java similarity index 85% rename from refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java rename to refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java index d779e701..20c3f00f 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCase.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java @@ -1,12 +1,12 @@ package de.muenchen.refarch.email.integration.application.usecase; -import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; +import de.muenchen.refarch.email.integration.application.port.in.SendMailInPort; import de.muenchen.refarch.email.integration.application.port.out.LoadMailAttachmentOutPort; import de.muenchen.refarch.email.integration.application.port.out.MailOutPort; import de.muenchen.refarch.email.integration.domain.exception.TemplateError; -import de.muenchen.refarch.email.integration.domain.model.paths.BasicMailPaths; -import de.muenchen.refarch.email.integration.domain.model.paths.TemplateMailPaths; -import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; +import de.muenchen.refarch.email.integration.domain.model.BasicMail; +import de.muenchen.refarch.email.integration.domain.model.TemplateMail; +import de.muenchen.refarch.email.integration.domain.model.TextMail; import de.muenchen.refarch.email.model.FileAttachment; import freemarker.template.TemplateException; import jakarta.mail.MessagingException; @@ -23,7 +23,7 @@ @Slf4j @RequiredArgsConstructor @Validated -public class SendMailPathsUseCase implements SendMailPathsInPort { +public class SendMailUseCase implements SendMailInPort { private final LoadMailAttachmentOutPort loadAttachmentOutPort; private final MailOutPort mailOutPort; @@ -34,7 +34,7 @@ public class SendMailPathsUseCase implements SendMailPathsInPort { * @param mail mail that is sent */ @Override - public void sendMailWithText(@Valid final TextMailPaths mail) { + public void sendMailWithText(@Valid final TextMail mail) { de.muenchen.refarch.email.model.Mail mailModel = createMail(mail); mailModel.setBody(mail.getBody()); @@ -42,7 +42,7 @@ public void sendMailWithText(@Valid final TextMailPaths mail) { } @Override - public void sendMailWithTemplate(@Valid final TemplateMailPaths mail) throws TemplateError { + public void sendMailWithTemplate(@Valid final TemplateMail mail) throws TemplateError { // get body from template try { Map content = new HashMap<>(mail.getContent()); @@ -62,7 +62,7 @@ public void sendMailWithTemplate(@Valid final TemplateMailPaths mail) throws Tem } } - private de.muenchen.refarch.email.model.Mail createMail(BasicMailPaths mail) { + private de.muenchen.refarch.email.model.Mail createMail(BasicMail mail) { // load Attachments List attachments = loadAttachmentOutPort.loadAttachments(mail.getFilePaths()); diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java index ac2526bf..4faa8db3 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java @@ -1,6 +1,7 @@ package de.muenchen.refarch.email.integration.domain.model; import jakarta.validation.constraints.NotBlank; +import java.util.List; import lombok.AllArgsConstructor; import lombok.Data; import lombok.RequiredArgsConstructor; @@ -35,4 +36,8 @@ public class BasicMail { * Reply to address */ private String replyTo; + /** + * List of attachment paths. + */ + private List filePaths; } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TemplateMail.java similarity index 75% rename from refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java rename to refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TemplateMail.java index e7f200ce..32312800 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TemplateMailPaths.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TemplateMail.java @@ -1,4 +1,4 @@ -package de.muenchen.refarch.email.integration.domain.model.paths; +package de.muenchen.refarch.email.integration.domain.model; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotEmpty; @@ -13,7 +13,7 @@ @Data @RequiredArgsConstructor @AllArgsConstructor -public class TemplateMailPaths extends BasicMailPaths { +public class TemplateMail extends BasicMail { /** * Template of the mail. @@ -27,7 +27,7 @@ public class TemplateMailPaths extends BasicMailPaths { @NotEmpty(message = "No content given") private Map content; - public TemplateMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, List filePaths, + public TemplateMail(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, List filePaths, String template, Map content) { super(receivers, receiversCc, receiversBcc, subject, replyTo, filePaths); this.template = template; diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TextMail.java similarity index 71% rename from refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java rename to refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TextMail.java index a55b44fe..3d325d06 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/TextMailPaths.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TextMail.java @@ -1,4 +1,4 @@ -package de.muenchen.refarch.email.integration.domain.model.paths; +package de.muenchen.refarch.email.integration.domain.model; import jakarta.validation.constraints.NotBlank; import java.util.List; @@ -14,7 +14,7 @@ @Data @RequiredArgsConstructor @AllArgsConstructor -public class TextMailPaths extends BasicMailPaths { +public class TextMail extends BasicMail { /** * Body of the mail. @@ -22,7 +22,7 @@ public class TextMailPaths extends BasicMailPaths { @NotBlank(message = "No body given") private String body; - public TextMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String body, String replyTo, + public TextMail(String receivers, String receiversCc, String receiversBcc, String subject, String body, String replyTo, List filePaths) { super(receivers, receiversCc, receiversBcc, subject, replyTo, filePaths); this.body = body; diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java deleted file mode 100644 index d73b5bca..00000000 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/paths/BasicMailPaths.java +++ /dev/null @@ -1,22 +0,0 @@ -package de.muenchen.refarch.email.integration.domain.model.paths; - -import de.muenchen.refarch.email.integration.domain.model.BasicMail; -import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; - -@EqualsAndHashCode(callSuper = true) -@Data -@RequiredArgsConstructor -@AllArgsConstructor -public class BasicMailPaths extends BasicMail { - // TODO use List - private List filePaths; - - public BasicMailPaths(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, List filePaths) { - super(receivers, receiversCc, receiversBcc, subject, replyTo); - this.filePaths = filePaths; - } -} diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java similarity index 84% rename from refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java rename to refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java index d9d54424..8f656ed0 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailPathsUseCaseTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java @@ -3,12 +3,12 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; +import de.muenchen.refarch.email.integration.application.port.in.SendMailInPort; import de.muenchen.refarch.email.integration.application.port.out.LoadMailAttachmentOutPort; import de.muenchen.refarch.email.integration.application.port.out.MailOutPort; import de.muenchen.refarch.email.integration.domain.exception.TemplateError; -import de.muenchen.refarch.email.integration.domain.model.paths.TemplateMailPaths; -import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; +import de.muenchen.refarch.email.integration.domain.model.TemplateMail; +import de.muenchen.refarch.email.integration.domain.model.TextMail; import de.muenchen.refarch.email.model.FileAttachment; import freemarker.template.TemplateException; import jakarta.mail.MessagingException; @@ -20,11 +20,11 @@ import org.junit.jupiter.api.Test; import org.springframework.mail.MailSendException; -class SendMailPathsUseCaseTest { +class SendMailUseCaseTest { private final LoadMailAttachmentOutPort loadMailAttachmentOutPort = mock(LoadMailAttachmentOutPort.class); private final MailOutPort mailOutPort = mock(MailOutPort.class); - private final TextMailPaths mail = new TextMailPaths( + private final TextMail mail = new TextMail( "mailReceiver1@muenchen.de,mailReceiver2@muenchen.de", "receiverCC@muenchen.de", "receiverBCC@muenchen.de", @@ -32,7 +32,7 @@ class SendMailPathsUseCaseTest { "This is a test mail", "test@muenchen.de", List.of("folder/file.txt")); - private final TemplateMailPaths templateMail = new TemplateMailPaths( + private final TemplateMail templateMail = new TemplateMail( "mailReceiver1@muenchen.de,mailReceiver2@muenchen.de", "receiverCC@muenchen.de", "receiverBCC@muenchen.de", @@ -41,16 +41,16 @@ class SendMailPathsUseCaseTest { List.of("folder/file.txt"), "template", Map.of("mail", Map.of())); - private SendMailPathsInPort sendMailPathsInPort; + private SendMailInPort sendMailInPort; @BeforeEach void setUp() { - this.sendMailPathsInPort = new SendMailPathsUseCase(loadMailAttachmentOutPort, mailOutPort); + this.sendMailInPort = new SendMailUseCase(loadMailAttachmentOutPort, mailOutPort); } @Test void sendMail() throws MessagingException { - sendMailPathsInPort.sendMailWithText(mail); + sendMailInPort.sendMailWithText(mail); final de.muenchen.refarch.email.model.Mail mailOutModel = de.muenchen.refarch.email.model.Mail.builder() .receivers(mail.getReceivers()) .subject(mail.getSubject()) @@ -68,7 +68,7 @@ void sendMailWithAttachments() throws MessagingException { final FileAttachment fileAttachment = new FileAttachment("test.txt", new ByteArrayDataSource("Anhang Inhalt".getBytes(), "text/plain")); when(loadMailAttachmentOutPort.loadAttachments(List.of("folder/file.txt"))).thenReturn(List.of(fileAttachment)); - sendMailPathsInPort.sendMailWithText(mail); + sendMailInPort.sendMailWithText(mail); final de.muenchen.refarch.email.model.Mail mailOutModel = de.muenchen.refarch.email.model.Mail.builder() .receivers(mail.getReceivers()) .subject(mail.getSubject()) @@ -84,13 +84,13 @@ void sendMailWithAttachments() throws MessagingException { @Test void sendMailThrowsMailSendException() throws MessagingException { doThrow(new MessagingException("Test Exception")).when(mailOutPort).sendMail(any(), any()); - assertThatThrownBy(() -> sendMailPathsInPort.sendMailWithText(mail)).isInstanceOf(MailSendException.class); + assertThatThrownBy(() -> sendMailInPort.sendMailWithText(mail)).isInstanceOf(MailSendException.class); } @Test void sendMailWithTemplate() throws MessagingException, TemplateException, IOException { when(mailOutPort.getBodyFromTemplate(anyString(), anyMap())).thenReturn("generated body"); - sendMailPathsInPort.sendMailWithTemplate(templateMail); + sendMailInPort.sendMailWithTemplate(templateMail); final de.muenchen.refarch.email.model.Mail mailOutModel = de.muenchen.refarch.email.model.Mail.builder() .receivers(mail.getReceivers()) .subject(mail.getSubject()) @@ -107,7 +107,7 @@ void sendMailWithTemplate() throws MessagingException, TemplateException, IOExce @Test void sendMailWithTemplateThrowsIOException() throws TemplateException, IOException { doThrow(new IOException("IO Exception")).when(mailOutPort).getBodyFromTemplate(anyString(), anyMap()); - TemplateError error = catchThrowableOfType(() -> sendMailPathsInPort.sendMailWithTemplate(templateMail), TemplateError.class); + TemplateError error = catchThrowableOfType(() -> sendMailInPort.sendMailWithTemplate(templateMail), TemplateError.class); String expectedMessage = "The template " + templateMail.getTemplate() + " could not be loaded"; String actualMessage = error.getMessage(); @@ -120,7 +120,7 @@ void sendMailWithTemplateThrowsTemplateException() throws TemplateException, IOE TemplateException templateException = mock(TemplateException.class); when(templateException.getMessage()).thenReturn("Template Exception Message"); doThrow(templateException).when(mailOutPort).getBodyFromTemplate(anyString(), anyMap()); - TemplateError error = catchThrowableOfType(() -> sendMailPathsInPort.sendMailWithTemplate(templateMail), TemplateError.class); + TemplateError error = catchThrowableOfType(() -> sendMailInPort.sendMailWithTemplate(templateMail), TemplateError.class); String expectedMessage = "Template Exception Message"; String actualMessage = error.getMessage(); diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java index 4f9174f0..552de389 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java @@ -1,7 +1,7 @@ package de.muenchen.refarch.email.integration; -import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; -import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; +import de.muenchen.refarch.email.integration.application.port.in.SendMailInPort; +import de.muenchen.refarch.email.integration.domain.model.TextMail; import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFileRepository; import java.util.List; import lombok.RequiredArgsConstructor; @@ -14,17 +14,17 @@ @RequiredArgsConstructor @Slf4j public class TestService { - private final SendMailPathsInPort sendMailPathsInPort; + private final SendMailInPort sendMailInPort; private final DocumentStorageFileRepository documentStorageFileRepository; void testSendMail() { this.uploadTestFile(); - TextMailPaths mail = new TextMailPaths(); + TextMail mail = new TextMail(); mail.setReceivers("test.receiver@muenchen.de"); mail.setSubject("Test"); mail.setBody("This is a test"); mail.setFilePaths(List.of("/test/test-pdf.pdf")); - sendMailPathsInPort.sendMailWithText(mail); + sendMailInPort.sendMailWithText(mail); log.info("Test mail sent"); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java index 4f9174f0..552de389 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java @@ -1,7 +1,7 @@ package de.muenchen.refarch.email.integration; -import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; -import de.muenchen.refarch.email.integration.domain.model.paths.TextMailPaths; +import de.muenchen.refarch.email.integration.application.port.in.SendMailInPort; +import de.muenchen.refarch.email.integration.domain.model.TextMail; import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFileRepository; import java.util.List; import lombok.RequiredArgsConstructor; @@ -14,17 +14,17 @@ @RequiredArgsConstructor @Slf4j public class TestService { - private final SendMailPathsInPort sendMailPathsInPort; + private final SendMailInPort sendMailInPort; private final DocumentStorageFileRepository documentStorageFileRepository; void testSendMail() { this.uploadTestFile(); - TextMailPaths mail = new TextMailPaths(); + TextMail mail = new TextMail(); mail.setReceivers("test.receiver@muenchen.de"); mail.setSubject("Test"); mail.setBody("This is a test"); mail.setFilePaths(List.of("/test/test-pdf.pdf")); - sendMailPathsInPort.sendMailWithText(mail); + sendMailInPort.sendMailWithText(mail); log.info("Test mail sent"); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java index 9b9dba2d..f4200f60 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-starter/src/main/java/de/muenchen/refarch/email/integration/configuration/MailAutoConfiguration.java @@ -3,10 +3,10 @@ import de.muenchen.refarch.email.api.EmailApi; import de.muenchen.refarch.email.integration.adapter.out.mail.MailAdapter; import de.muenchen.refarch.email.integration.adapter.out.s3.S3Adapter; -import de.muenchen.refarch.email.integration.application.port.in.SendMailPathsInPort; +import de.muenchen.refarch.email.integration.application.port.in.SendMailInPort; import de.muenchen.refarch.email.integration.application.port.out.LoadMailAttachmentOutPort; import de.muenchen.refarch.email.integration.application.port.out.MailOutPort; -import de.muenchen.refarch.email.integration.application.usecase.SendMailPathsUseCase; +import de.muenchen.refarch.email.integration.application.usecase.SendMailUseCase; import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFileRepository; import de.muenchen.refarch.integration.s3.client.repository.DocumentStorageFolderRepository; import de.muenchen.refarch.integration.s3.client.service.FileValidationService; @@ -24,8 +24,8 @@ public class MailAutoConfiguration { @Bean @ConditionalOnMissingBean - public SendMailPathsInPort getSendMailPathsInPort(final LoadMailAttachmentOutPort loadAttachmentPort, final MailOutPort mailOutPort) { - return new SendMailPathsUseCase(loadAttachmentPort, mailOutPort); + public SendMailInPort getSendMailPathsInPort(final LoadMailAttachmentOutPort loadAttachmentPort, final MailOutPort mailOutPort) { + return new SendMailUseCase(loadAttachmentPort, mailOutPort); } @Bean From 0953620d79bbeae8fd31c49782433ce52f74b5bd Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Wed, 14 Aug 2024 13:26:56 +0200 Subject: [PATCH 15/25] :memo: mail update README.md add properties examples --- .../refarch-email-integration/README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/README.md b/refarch-integrations/refarch-email-integration/README.md index 6c3fc085..bfb2d331 100644 --- a/refarch-integrations/refarch-email-integration/README.md +++ b/refarch-integrations/refarch-email-integration/README.md @@ -22,10 +22,13 @@ and a [s3-integration starter](../refarch-s3-integration/README.md#usage). ### refarch-email-integration-starter -| Property | Description | Example | -|-----------------------------------------|---------------------------------------------------|---------| -| `spring.mail.host` | Host of smtp server used for sending mails. | | -| `spring.mail.username` | Username of smtp server. | | -| `spring.mail.password` | Password of smtp server. | | -| `refarch.mail.from-address` | Default from address used when sending mails. | | -| `refarch.mail.default-reply-to-address` | Default reply to address used when sending mails. | | +| Property | Description | Example | +|-----------------------------------------|---------------------------------------------------|------------------------| +| `spring.mail.host` | Host of smtp server used for sending mails. | `mail.example.com` | +| `spring.mail.port` | Host of smtp server used for sending mails. | `1025` | +| `spring.mail.username` | Username of smtp server. | | +| `spring.mail.password` | Password of smtp server. | | +| `refarch.mail.from-address` | Default from address used when sending mails. | `test@example.com` | +| `refarch.mail.default-reply-to-address` | Default reply to address used when sending mails. | `no_reply@example.com` | + +In addition, properties of selected [s3-integration starter](../refarch-s3-integration/README.md#usage). From e5e3f3ebdde906585055a53de3470d15c1c57700 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Wed, 14 Aug 2024 15:05:32 +0200 Subject: [PATCH 16/25] :bug: email core re add test file --- .../src/test/resources/files/test-pdf.pdf | Bin 0 -> 7386 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-pdf.pdf diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-pdf.pdf b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/resources/files/test-pdf.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9ee1f262ec21c89a45509810611753538e0165d8 GIT binary patch literal 7386 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 literal 0 HcmV?d00001 From 80f249980320598c1381d399314f755ab1261f02 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Fri, 16 Aug 2024 11:23:13 +0200 Subject: [PATCH 17/25] :recycle: integrations replace custom properties aliases with env vars --- .../src/main/resources/application-local.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/application-local.yml b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/application-local.yml index 8d04673b..f7438700 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/application-local.yml +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/resources/application-local.yml @@ -10,11 +10,10 @@ refarch: mail: from-address: test@muenchen.de default-reply-to-address: no_reply@muenchen.de - security: - sso-issuer-url: http://keycloak:8100/auth/realms/local_realm s3: client: document-storage-url: http://localhost:8086 enable-security: true - client-id: local - client-secret: client_secret +SSO_ISSUER_URL: http://keycloak:8100/auth/realms/local_realm +SSO_S3_CLIENT_ID: local +SSO_S3_CLIENT_SECRET: client_secret From 571dde2ef442885efaf6702e20d81a721ad1280d Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Mon, 2 Sep 2024 08:09:03 +0200 Subject: [PATCH 18/25] :art: mail replace fqn with import --- .../integration/application/usecase/SendMailUseCase.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java index 20c3f00f..edead843 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java @@ -8,6 +8,7 @@ import de.muenchen.refarch.email.integration.domain.model.TemplateMail; import de.muenchen.refarch.email.integration.domain.model.TextMail; import de.muenchen.refarch.email.model.FileAttachment; +import de.muenchen.refarch.email.model.Mail; import freemarker.template.TemplateException; import jakarta.mail.MessagingException; import jakarta.validation.Valid; @@ -49,7 +50,7 @@ public void sendMailWithTemplate(@Valid final TemplateMail mail) throws Template content.put("footer", "DigiWF 2.0
IT-Referat der Stadt München"); String body = this.mailOutPort.getBodyFromTemplate(mail.getTemplate(), content); - de.muenchen.refarch.email.model.Mail mailModel = createMail(mail); + Mail mailModel = createMail(mail); mailModel.setBody(body); mailModel.setHtmlBody(true); @@ -62,12 +63,12 @@ public void sendMailWithTemplate(@Valid final TemplateMail mail) throws Template } } - private de.muenchen.refarch.email.model.Mail createMail(BasicMail mail) { + private Mail createMail(BasicMail mail) { // load Attachments List attachments = loadAttachmentOutPort.loadAttachments(mail.getFilePaths()); // send mail - return de.muenchen.refarch.email.model.Mail.builder() + return Mail.builder() .receivers(mail.getReceivers()) .subject(mail.getSubject()) .replyTo(mail.getReplyTo()) @@ -77,7 +78,7 @@ private de.muenchen.refarch.email.model.Mail createMail(BasicMail mail) { .build(); } - private void sendMail(de.muenchen.refarch.email.model.Mail mailModel, String logoPath) throws MailSendException { + private void sendMail(Mail mailModel, String logoPath) throws MailSendException { try { this.mailOutPort.sendMail(mailModel, logoPath); } catch (final MessagingException ex) { From 8a69c38edf951b62bce56455c1674133161e523c Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Mon, 2 Sep 2024 08:13:52 +0200 Subject: [PATCH 19/25] :art: mail rm digiwf from class name --- ...fEmailAutoConfiguration.java => EmailAutoConfiguration.java} | 2 +- ...springframework.boot.autoconfigure.AutoConfiguration.imports | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/{DigiwfEmailAutoConfiguration.java => EmailAutoConfiguration.java} (98%) diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/EmailAutoConfiguration.java similarity index 98% rename from refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java rename to refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/EmailAutoConfiguration.java index 177f3a85..976b08cd 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/DigiwfEmailAutoConfiguration.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/java/de/muenchen/refarch/email/configuration/EmailAutoConfiguration.java @@ -17,7 +17,7 @@ @RequiredArgsConstructor @EnableConfigurationProperties({ MailProperties.class, CustomMailProperties.class }) -public class DigiwfEmailAutoConfiguration { +public class EmailAutoConfiguration { private final MailProperties mailProperties; private final CustomMailProperties customMailProperties; diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index e38c58a2..e67a5bac 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1 +1 @@ -de.muenchen.refarch.email.configuration.DigiwfEmailAutoConfiguration +de.muenchen.refarch.email.configuration.EmailAutoConfiguration From edad1d358e8c967627cae38681828062838cb635 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Mon, 2 Sep 2024 08:15:26 +0200 Subject: [PATCH 20/25] :fire: mail rm default digiwf template footer --- .../email/integration/application/usecase/SendMailUseCase.java | 1 - 1 file changed, 1 deletion(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java index edead843..d30a1590 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java @@ -47,7 +47,6 @@ public void sendMailWithTemplate(@Valid final TemplateMail mail) throws Template // get body from template try { Map content = new HashMap<>(mail.getContent()); - content.put("footer", "DigiWF 2.0
IT-Referat der Stadt München"); String body = this.mailOutPort.getBodyFromTemplate(mail.getTemplate(), content); Mail mailModel = createMail(mail); From 41b93585edde09925d7a74eddee79a045bd945bd Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Mon, 2 Sep 2024 08:26:19 +0200 Subject: [PATCH 21/25] :art: mail rm star imports --- .../adapter/out/mail/MailAdapterTest.java | 6 +++++- .../integration/adapter/out/s3/S3AdapterTest.java | 8 ++++++-- .../application/usecase/SendMailUseCaseTest.java | 12 ++++++++++-- .../de/muenchen/refarch/email/EmailApiImplTest.java | 5 ++++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java index 6fcd727b..e530bb9d 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java @@ -1,7 +1,11 @@ package de.muenchen.refarch.email.integration.adapter.out.mail; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyMap; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import de.muenchen.refarch.email.api.EmailApi; import de.muenchen.refarch.email.model.Mail; diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java index 8565a45c..de13332f 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java @@ -1,7 +1,11 @@ package de.muenchen.refarch.email.integration.adapter.out.s3; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java index 8f656ed0..49b228f3 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java @@ -1,7 +1,15 @@ package de.muenchen.refarch.email.integration.application.usecase; -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.catchThrowableOfType; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyMap; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import de.muenchen.refarch.email.integration.application.port.in.SendMailInPort; import de.muenchen.refarch.email.integration.application.port.out.LoadMailAttachmentOutPort; diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java index 6f5b1dc3..5c5c742a 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java @@ -2,7 +2,10 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import de.muenchen.refarch.email.api.EmailApi; import de.muenchen.refarch.email.impl.EmailApiImpl; From 3dd327dff431832d2c218eb7f3c8ccafa4df7f3f Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Mon, 2 Sep 2024 08:58:41 +0200 Subject: [PATCH 22/25] :recycle: mail make model classes record or not modifiable --- .../application/usecase/SendMailUseCase.java | 28 ++-- .../integration/domain/model/BasicMail.java | 6 +- .../domain/model/TemplateMail.java | 12 +- .../integration/domain/model/TextMail.java | 10 +- .../adapter/out/mail/MailAdapterTest.java | 19 +-- .../adapter/out/s3/S3AdapterTest.java | 6 +- .../usecase/SendMailUseCaseTest.java | 62 +++++---- .../refarch/email/impl/EmailApiImpl.java | 22 ++-- .../refarch/email/model/FileAttachment.java | 16 +-- .../de/muenchen/refarch/email/model/Mail.java | 42 +++--- .../refarch/email/EmailApiImplTest.java | 124 ++++++++++++------ 11 files changed, 184 insertions(+), 163 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java index d30a1590..6be3e333 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java @@ -36,8 +36,7 @@ public class SendMailUseCase implements SendMailInPort { */ @Override public void sendMailWithText(@Valid final TextMail mail) { - de.muenchen.refarch.email.model.Mail mailModel = createMail(mail); - mailModel.setBody(mail.getBody()); + Mail mailModel = createMail(mail, mail.getBody(), false); this.sendMail(mailModel, null); } @@ -49,9 +48,7 @@ public void sendMailWithTemplate(@Valid final TemplateMail mail) throws Template Map content = new HashMap<>(mail.getContent()); String body = this.mailOutPort.getBodyFromTemplate(mail.getTemplate(), content); - Mail mailModel = createMail(mail); - mailModel.setBody(body); - mailModel.setHtmlBody(true); + Mail mailModel = createMail(mail, body, true); this.sendMail(mailModel, "templates/email-logo.png"); @@ -62,19 +59,22 @@ public void sendMailWithTemplate(@Valid final TemplateMail mail) throws Template } } - private Mail createMail(BasicMail mail) { + private Mail createMail(final BasicMail mail, final String body, final boolean htmlBody) { // load Attachments List attachments = loadAttachmentOutPort.loadAttachments(mail.getFilePaths()); // send mail - return Mail.builder() - .receivers(mail.getReceivers()) - .subject(mail.getSubject()) - .replyTo(mail.getReplyTo()) - .receiversCc(mail.getReceiversCc()) - .receiversBcc(mail.getReceiversBcc()) - .attachments(attachments) - .build(); + return new Mail( + mail.getReceivers(), + mail.getReceiversCc(), + mail.getReceiversBcc(), + mail.getSubject(), + body, + htmlBody, + null, + mail.getReplyTo(), + attachments + ); } private void sendMail(Mail mailModel, String logoPath) throws MailSendException { diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java index 4faa8db3..824e9720 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/BasicMail.java @@ -3,11 +3,9 @@ import jakarta.validation.constraints.NotBlank; import java.util.List; import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.RequiredArgsConstructor; +import lombok.Getter; -@Data -@RequiredArgsConstructor +@Getter @AllArgsConstructor public class BasicMail { /** diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TemplateMail.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TemplateMail.java index 32312800..56ca2683 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TemplateMail.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TemplateMail.java @@ -4,28 +4,24 @@ import jakarta.validation.constraints.NotEmpty; import java.util.List; import java.util.Map; -import lombok.AllArgsConstructor; -import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; +import lombok.Getter; @EqualsAndHashCode(callSuper = true) -@Data -@RequiredArgsConstructor -@AllArgsConstructor +@Getter public class TemplateMail extends BasicMail { /** * Template of the mail. */ @NotBlank(message = "No template given") - private String template; + private final String template; /** * Bottom body of the mail. */ @NotEmpty(message = "No content given") - private Map content; + private final Map content; public TemplateMail(String receivers, String receiversCc, String receiversBcc, String subject, String replyTo, List filePaths, String template, Map content) { diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TextMail.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TextMail.java index 3d325d06..315a381c 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TextMail.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/domain/model/TextMail.java @@ -2,25 +2,21 @@ import jakarta.validation.constraints.NotBlank; import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.RequiredArgsConstructor; +import lombok.Getter; /** * Object contains all the information needed to send a mail. */ @EqualsAndHashCode(callSuper = true) -@Data -@RequiredArgsConstructor -@AllArgsConstructor +@Getter public class TextMail extends BasicMail { /** * Body of the mail. */ @NotBlank(message = "No body given") - private String body; + private final String body; public TextMail(String receivers, String receiversCc, String receiversBcc, String subject, String body, String replyTo, List filePaths) { diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java index e530bb9d..351f6c26 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java @@ -22,14 +22,17 @@ class MailAdapterTest { @Test void sendMail() throws MessagingException { final MailAdapter mailAdapter = new MailAdapter(emailApi); - final Mail mail = Mail.builder() - .receivers("receivers") - .subject("subject") - .body("body") - .replyTo("replyTo") - .receiversCc("receiversCc") - .receiversBcc("receiversBcc") - .build(); + final Mail mail = new Mail( + "receivers", + "receiversCc", + "receiversBcc", + "subject", + "body", + false, + null, + "replyTo", + null + ); mailAdapter.sendMail(mail, "logoPath"); verify(emailApi).sendMail(mail, "logoPath"); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java index de13332f..cff7d114 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/s3/S3AdapterTest.java @@ -67,9 +67,9 @@ void testLoadAttachment_Success() throws DocumentStorageException, DocumentStora final List fileAttachment = this.s3Adapter.loadAttachments(List.of(path)); - assertThat(Arrays.equals(testFile, fileAttachment.getFirst().getFile().getInputStream().readAllBytes())).isTrue(); - assertThat(file.getKey()).isEqualTo(fileAttachment.getFirst().getFileName()); - assertThat(file.getValue()).isEqualTo(fileAttachment.getFirst().getFile().getContentType()); + assertThat(Arrays.equals(testFile, fileAttachment.getFirst().file().getInputStream().readAllBytes())).isTrue(); + assertThat(file.getKey()).isEqualTo(fileAttachment.getFirst().fileName()); + assertThat(file.getValue()).isEqualTo(fileAttachment.getFirst().file().getContentType()); } catch (final IOException e) { log.warn("Could not read file: {}", file); fail(e.getMessage()); diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java index 49b228f3..23129795 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java @@ -18,6 +18,7 @@ import de.muenchen.refarch.email.integration.domain.model.TemplateMail; import de.muenchen.refarch.email.integration.domain.model.TextMail; import de.muenchen.refarch.email.model.FileAttachment; +import de.muenchen.refarch.email.model.Mail; import freemarker.template.TemplateException; import jakarta.mail.MessagingException; import jakarta.mail.util.ByteArrayDataSource; @@ -59,15 +60,17 @@ void setUp() { @Test void sendMail() throws MessagingException { sendMailInPort.sendMailWithText(mail); - final de.muenchen.refarch.email.model.Mail mailOutModel = de.muenchen.refarch.email.model.Mail.builder() - .receivers(mail.getReceivers()) - .subject(mail.getSubject()) - .body(mail.getBody()) - .replyTo(mail.getReplyTo()) - .receiversCc(mail.getReceiversCc()) - .receiversBcc(mail.getReceiversBcc()) - .attachments(List.of()) - .build(); + final Mail mailOutModel = new Mail( + mail.getReceivers(), + mail.getReceiversCc(), + mail.getReceiversBcc(), + mail.getSubject(), + mail.getBody(), + false, + null, + mail.getReplyTo(), + List.of() + ); verify(mailOutPort).sendMail(mailOutModel, null); } @@ -77,15 +80,17 @@ void sendMailWithAttachments() throws MessagingException { when(loadMailAttachmentOutPort.loadAttachments(List.of("folder/file.txt"))).thenReturn(List.of(fileAttachment)); sendMailInPort.sendMailWithText(mail); - final de.muenchen.refarch.email.model.Mail mailOutModel = de.muenchen.refarch.email.model.Mail.builder() - .receivers(mail.getReceivers()) - .subject(mail.getSubject()) - .body(mail.getBody()) - .replyTo(mail.getReplyTo()) - .receiversCc(mail.getReceiversCc()) - .receiversBcc(mail.getReceiversBcc()) - .attachments(List.of(fileAttachment)) - .build(); + final Mail mailOutModel = new Mail( + mail.getReceivers(), + mail.getReceiversCc(), + mail.getReceiversBcc(), + mail.getSubject(), + mail.getBody(), + false, + null, + mail.getReplyTo(), + List.of(fileAttachment) + ); verify(mailOutPort).sendMail(mailOutModel, null); } @@ -99,16 +104,17 @@ void sendMailThrowsMailSendException() throws MessagingException { void sendMailWithTemplate() throws MessagingException, TemplateException, IOException { when(mailOutPort.getBodyFromTemplate(anyString(), anyMap())).thenReturn("generated body"); sendMailInPort.sendMailWithTemplate(templateMail); - final de.muenchen.refarch.email.model.Mail mailOutModel = de.muenchen.refarch.email.model.Mail.builder() - .receivers(mail.getReceivers()) - .subject(mail.getSubject()) - .htmlBody(true) - .body("generated body") - .replyTo(mail.getReplyTo()) - .receiversCc(mail.getReceiversCc()) - .receiversBcc(mail.getReceiversBcc()) - .attachments(List.of()) - .build(); + final Mail mailOutModel = new Mail( + mail.getReceivers(), + mail.getReceiversCc(), + mail.getReceiversBcc(), + mail.getSubject(), + "generated body", + true, + null, + mail.getReplyTo(), + List.of() + ); verify(mailOutPort).sendMail(mailOutModel, "templates/email-logo.png"); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/EmailApiImpl.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/EmailApiImpl.java index 9261ceb9..c8eb2121 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/EmailApiImpl.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/impl/EmailApiImpl.java @@ -54,32 +54,32 @@ public void sendMailWithDefaultLogo(@Valid Mail mail) throws MessagingException public void sendMail(@Valid Mail mail, String logoPath) throws MessagingException { final MimeMessage mimeMessage = this.mailSender.createMimeMessage(); - mimeMessage.setRecipients(Message.RecipientType.TO, InternetAddress.parse(mail.getReceivers())); + mimeMessage.setRecipients(Message.RecipientType.TO, InternetAddress.parse(mail.receivers())); if (mail.hasReceiversCc()) { - mimeMessage.setRecipients(Message.RecipientType.CC, InternetAddress.parse(mail.getReceiversCc())); + mimeMessage.setRecipients(Message.RecipientType.CC, InternetAddress.parse(mail.receiversCc())); } if (mail.hasReceiversBcc()) { - mimeMessage.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(mail.getReceiversBcc())); + mimeMessage.setRecipients(Message.RecipientType.BCC, InternetAddress.parse(mail.receiversBcc())); } if (mail.hasReplyTo()) { - mimeMessage.setReplyTo(InternetAddress.parse(mail.getReplyTo())); + mimeMessage.setReplyTo(InternetAddress.parse(mail.replyTo())); } else if (defaultReplyToAddress != null) { mimeMessage.setReplyTo(InternetAddress.parse(defaultReplyToAddress)); } final var helper = new MimeMessageHelper(mimeMessage, true, StandardCharsets.UTF_8.name()); - helper.setSubject(mail.getSubject()); - helper.setText(mail.getBody(), mail.isHtmlBody()); + helper.setSubject(mail.subject()); + helper.setText(mail.body(), mail.htmlBody()); // use custom sender - helper.setFrom(mail.hasSender() ? mail.getSender() : this.fromAddress); + helper.setFrom(mail.hasSender() ? mail.sender() : this.fromAddress); // mail attachments - if (mail.hasAttachement()) { - for (val attachment : mail.getAttachments()) { - helper.addAttachment(attachment.getFileName(), attachment.getFile()); + if (mail.hasAttachment()) { + for (val attachment : mail.attachments()) { + helper.addAttachment(attachment.fileName(), attachment.file()); } } @@ -90,7 +90,7 @@ public void sendMail(@Valid Mail mail, String logoPath) throws MessagingExceptio } this.mailSender.send(mimeMessage); - log.info("Mail {} sent to {}.", mail.getSubject(), mail.getReceivers()); + log.info("Mail {} sent to {}.", mail.subject(), mail.receivers()); } @Override diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java index cd356618..9bfdc459 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java @@ -1,17 +1,9 @@ package de.muenchen.refarch.email.model; import jakarta.mail.util.ByteArrayDataSource; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.RequiredArgsConstructor; - -@Data -@RequiredArgsConstructor -@AllArgsConstructor -public class FileAttachment { - - private String fileName; - - private ByteArrayDataSource file; +public record FileAttachment( + String fileName, + ByteArrayDataSource file +) { } diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java index 0988f68a..062e10b7 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java @@ -2,33 +2,24 @@ import jakarta.validation.constraints.NotBlank; import java.util.List; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class Mail { - - @NotBlank - private String receivers; - @NotBlank - private String subject; - @NotBlank - private String body; - @Builder.Default - private boolean htmlBody = false; - private String replyTo; - private String receiversCc; - private String receiversBcc; - private String sender; - private List attachments; - - public boolean hasAttachement() { +public record Mail( + @NotBlank + String receivers, + String receiversCc, + String receiversBcc, + @NotBlank + String subject, + @NotBlank + String body, + boolean htmlBody, + String sender, + String replyTo, + List attachments +) { + + public boolean hasAttachment() { return attachments != null && !attachments.isEmpty(); } @@ -47,5 +38,4 @@ public boolean hasReceiversBcc() { public boolean hasSender() { return StringUtils.isNotBlank(this.sender); } - } diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java index 5c5c742a..8da3a5bd 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java @@ -59,11 +59,17 @@ void setUp() { @Test void testSendMail() throws MessagingException, IOException { - final Mail mail = Mail.builder() - .receivers(this.receiver) - .subject(this.subject) - .body(this.body) - .build(); + final Mail mail = new Mail( + this.receiver, + null, + null, + this.subject, + this.body, + false, + null, + null, + null + ); this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -81,11 +87,17 @@ void testSendMail() throws MessagingException, IOException { void testSendMailNoDefaultReplyTo() throws MessagingException, IOException { var customAddress = new InternetAddress("custom.test@muenchen.de"); - final Mail mail = Mail.builder() - .receivers(this.receiver) - .subject(this.subject) - .body(this.body) - .build(); + final Mail mail = new Mail( + this.receiver, + null, + null, + this.subject, + this.body, + false, + null, + null, + null + ); new EmailApiImpl(this.javaMailSender, this.resourceLoader, freeMarkerConfigurer, customAddress.getAddress(), null).sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -101,15 +113,17 @@ void testSendMailNoDefaultReplyTo() throws MessagingException, IOException { @Test void testSendMailWithOptions() throws MessagingException, IOException { - final Mail mail = Mail.builder() - .receivers(this.receiver) - .subject(this.subject) - .body(this.body) - .replyTo(this.replyTo) - .receiversCc(this.receiverCC) - .receiversBcc(this.receiverBCC) - .sender(this.sender) - .build(); + final Mail mail = new Mail( + this.receiver, + this.receiverCC, + this.receiverBCC, + this.subject, + this.body, + false, + this.sender, + this.replyTo, + null + ); this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -129,13 +143,22 @@ void testSendMailWithOptions() throws MessagingException, IOException { @Test void sendMailWithAttachments() throws MessagingException, IOException { - final Mail mail = Mail.builder() - .receivers(this.receiver) - .subject(this.subject) - .body(this.body) - .attachments(List.of( - new FileAttachment("Testanhang", new ByteArrayDataSource("FooBar".getBytes(), "text/plain")))) - .build(); + final Mail mail = new Mail( + this.receiver, + null, + null, + this.subject, + this.body, + false, + null, + null, + List.of( + new FileAttachment( + "Testanhang", + new ByteArrayDataSource("FooBar".getBytes(), "text/plain") + ) + ) + ); this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -152,12 +175,17 @@ void sendMailWithMultipleReplyToAddresses() throws MessagingException, IOExcepti var reply1 = new InternetAddress("address1@muenchen.de"); var reply2 = new InternetAddress("address2@muenchen.de"); - final Mail mail = Mail.builder() - .receivers(this.receiver) - .subject(this.subject) - .body(this.body) - .replyTo(reply1.getAddress() + "," + reply2.getAddress()) - .build(); + final Mail mail = new Mail( + this.receiver, + null, + null, + this.subject, + this.body, + false, + null, + reply1.getAddress() + "," + reply2.getAddress(), + null + ); this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -175,11 +203,17 @@ void sendMailWithMultipleReplyToAddresses() throws MessagingException, IOExcepti void sendMailWithDefaultLogo() throws MessagingException, IOException { when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("Default Logo", true)); - final Mail mail = Mail.builder() - .receivers(this.receiver) - .subject(this.subject) - .body(this.body) - .build(); + final Mail mail = new Mail( + this.receiver, + null, + null, + this.subject, + this.body, + false, + null, + null, + null + ); this.emailApi.sendMailWithDefaultLogo(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -196,11 +230,17 @@ void sendMailWithDefaultLogo() throws MessagingException, IOException { void sendMailWithCustomLogo() throws MessagingException, IOException { when(this.resourceLoader.getResource(anyString())).thenReturn(this.getResourceForText("Custom Logo", true)); - final Mail mail = Mail.builder() - .receivers(this.receiver) - .subject(this.subject) - .body(this.body) - .build(); + final Mail mail = new Mail( + this.receiver, + null, + null, + this.subject, + this.body, + false, + null, + null, + null + ); this.emailApi.sendMail(mail, "some/random/path/on/classpath"); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); From 9deb1f4b889c5d4688ee8f61b72a00f4c82fd1bc Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Mon, 2 Sep 2024 08:58:57 +0200 Subject: [PATCH 23/25] :recycle: mail make model classes record or not modifiable --- .../refarch/email/integration/TestService.java | 14 +++++++++----- .../refarch/email/integration/TestService.java | 14 +++++++++----- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java index 552de389..82d514ba 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java @@ -19,11 +19,15 @@ public class TestService { void testSendMail() { this.uploadTestFile(); - TextMail mail = new TextMail(); - mail.setReceivers("test.receiver@muenchen.de"); - mail.setSubject("Test"); - mail.setBody("This is a test"); - mail.setFilePaths(List.of("/test/test-pdf.pdf")); + TextMail mail = new TextMail( + "test.receiver@muenchen.de", + null, + null, + "Test", + "This is a test", + null, + List.of("/test/test-pdf.pdf") + ); sendMailInPort.sendMailWithText(mail); log.info("Test mail sent"); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java index 552de389..82d514ba 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java @@ -19,11 +19,15 @@ public class TestService { void testSendMail() { this.uploadTestFile(); - TextMail mail = new TextMail(); - mail.setReceivers("test.receiver@muenchen.de"); - mail.setSubject("Test"); - mail.setBody("This is a test"); - mail.setFilePaths(List.of("/test/test-pdf.pdf")); + TextMail mail = new TextMail( + "test.receiver@muenchen.de", + null, + null, + "Test", + "This is a test", + null, + List.of("/test/test-pdf.pdf") + ); sendMailInPort.sendMailWithText(mail); log.info("Test mail sent"); } From 09148118bb4727a3ebef464f028ea1d5216b043f Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Mon, 2 Sep 2024 09:01:32 +0200 Subject: [PATCH 24/25] :recycle: mail format spotless --- .../application/usecase/SendMailUseCase.java | 3 +-- .../adapter/out/mail/MailAdapterTest.java | 3 +-- .../usecase/SendMailUseCaseTest.java | 9 +++----- .../email/integration/TestService.java | 3 +-- .../email/integration/TestService.java | 3 +-- .../refarch/email/model/FileAttachment.java | 3 +-- .../de/muenchen/refarch/email/model/Mail.java | 12 ++++------ .../refarch/email/EmailApiImplTest.java | 23 ++++++------------- 8 files changed, 19 insertions(+), 40 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java index 6be3e333..7b460417 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/main/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCase.java @@ -73,8 +73,7 @@ private Mail createMail(final BasicMail mail, final String body, final boolean h htmlBody, null, mail.getReplyTo(), - attachments - ); + attachments); } private void sendMail(Mail mailModel, String logoPath) throws MailSendException { diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java index 351f6c26..d39bc472 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/adapter/out/mail/MailAdapterTest.java @@ -31,8 +31,7 @@ void sendMail() throws MessagingException { false, null, "replyTo", - null - ); + null); mailAdapter.sendMail(mail, "logoPath"); verify(emailApi).sendMail(mail, "logoPath"); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java index 23129795..f43cda9f 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-core/src/test/java/de/muenchen/refarch/email/integration/application/usecase/SendMailUseCaseTest.java @@ -69,8 +69,7 @@ void sendMail() throws MessagingException { false, null, mail.getReplyTo(), - List.of() - ); + List.of()); verify(mailOutPort).sendMail(mailOutModel, null); } @@ -89,8 +88,7 @@ void sendMailWithAttachments() throws MessagingException { false, null, mail.getReplyTo(), - List.of(fileAttachment) - ); + List.of(fileAttachment)); verify(mailOutPort).sendMail(mailOutModel, null); } @@ -113,8 +111,7 @@ void sendMailWithTemplate() throws MessagingException, TemplateException, IOExce true, null, mail.getReplyTo(), - List.of() - ); + List.of()); verify(mailOutPort).sendMail(mailOutModel, "templates/email-logo.png"); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java index 82d514ba..eebbf175 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java @@ -26,8 +26,7 @@ void testSendMail() { "Test", "This is a test", null, - List.of("/test/test-pdf.pdf") - ); + List.of("/test/test-pdf.pdf")); sendMailInPort.sendMailWithText(mail); log.info("Test mail sent"); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java index 82d514ba..eebbf175 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/TestService.java @@ -26,8 +26,7 @@ void testSendMail() { "Test", "This is a test", null, - List.of("/test/test-pdf.pdf") - ); + List.of("/test/test-pdf.pdf")); sendMailInPort.sendMailWithText(mail); log.info("Test mail sent"); } diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java index 9bfdc459..284e839b 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/FileAttachment.java @@ -4,6 +4,5 @@ public record FileAttachment( String fileName, - ByteArrayDataSource file -) { + ByteArrayDataSource file) { } diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java index 062e10b7..7bdb35d3 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/main/java/de/muenchen/refarch/email/model/Mail.java @@ -5,19 +5,15 @@ import org.apache.commons.lang3.StringUtils; public record Mail( - @NotBlank - String receivers, + @NotBlank String receivers, String receiversCc, String receiversBcc, - @NotBlank - String subject, - @NotBlank - String body, + @NotBlank String subject, + @NotBlank String body, boolean htmlBody, String sender, String replyTo, - List attachments -) { + List attachments) { public boolean hasAttachment() { return attachments != null && !attachments.isEmpty(); diff --git a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java index 8da3a5bd..c622ed23 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java +++ b/refarch-integrations/refarch-email-integration/refarch-email/refarch-email-api/src/test/java/de/muenchen/refarch/email/EmailApiImplTest.java @@ -68,8 +68,7 @@ void testSendMail() throws MessagingException, IOException { false, null, null, - null - ); + null); this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -96,8 +95,7 @@ void testSendMailNoDefaultReplyTo() throws MessagingException, IOException { false, null, null, - null - ); + null); new EmailApiImpl(this.javaMailSender, this.resourceLoader, freeMarkerConfigurer, customAddress.getAddress(), null).sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -122,8 +120,7 @@ void testSendMailWithOptions() throws MessagingException, IOException { false, this.sender, this.replyTo, - null - ); + null); this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -155,10 +152,7 @@ void sendMailWithAttachments() throws MessagingException, IOException { List.of( new FileAttachment( "Testanhang", - new ByteArrayDataSource("FooBar".getBytes(), "text/plain") - ) - ) - ); + new ByteArrayDataSource("FooBar".getBytes(), "text/plain")))); this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -184,8 +178,7 @@ void sendMailWithMultipleReplyToAddresses() throws MessagingException, IOExcepti false, null, reply1.getAddress() + "," + reply2.getAddress(), - null - ); + null); this.emailApi.sendMail(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -212,8 +205,7 @@ void sendMailWithDefaultLogo() throws MessagingException, IOException { false, null, null, - null - ); + null); this.emailApi.sendMailWithDefaultLogo(mail); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); @@ -239,8 +231,7 @@ void sendMailWithCustomLogo() throws MessagingException, IOException { false, null, null, - null - ); + null); this.emailApi.sendMail(mail, "some/random/path/on/classpath"); final ArgumentCaptor messageArgumentCaptor = ArgumentCaptor.forClass(MimeMessage.class); From 761938b5de55931b96d948080a8076e958e47f45 Mon Sep 17 00:00:00 2001 From: Simon Hirtreiter Date: Mon, 2 Sep 2024 09:03:27 +0200 Subject: [PATCH 25/25] :recycle: mail fix autowire syntax --- .../email/integration/EmailJavaExampleApplication.java | 6 +++--- .../email/integration/EmailRestExampleApplication.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/EmailJavaExampleApplication.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/EmailJavaExampleApplication.java index fd26a305..ceb02d27 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/EmailJavaExampleApplication.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-java-example/src/main/java/de/muenchen/refarch/email/integration/EmailJavaExampleApplication.java @@ -1,15 +1,15 @@ package de.muenchen.refarch.email.integration; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; @SpringBootApplication +@RequiredArgsConstructor public class EmailJavaExampleApplication { - @Autowired - TestService testService; + private final TestService testService; public static void main(final String[] args) { SpringApplication.run(EmailJavaExampleApplication.class, args); diff --git a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/EmailRestExampleApplication.java b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/EmailRestExampleApplication.java index 961eafdb..fee5c8b9 100644 --- a/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/EmailRestExampleApplication.java +++ b/refarch-integrations/refarch-email-integration/refarch-email-integration-rest-example/src/main/java/de/muenchen/refarch/email/integration/EmailRestExampleApplication.java @@ -1,15 +1,15 @@ package de.muenchen.refarch.email.integration; -import org.springframework.beans.factory.annotation.Autowired; +import lombok.RequiredArgsConstructor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; @SpringBootApplication +@RequiredArgsConstructor public class EmailRestExampleApplication { - @Autowired - TestService testService; + private final TestService testService; public static void main(final String[] args) { SpringApplication.run(EmailRestExampleApplication.class, args);