Refactoring + migrate mail package to server.
This commit is contained in:
@@ -1,46 +0,0 @@
|
||||
package dev.rheinsw.shared.mail;
|
||||
|
||||
import dev.rheinsw.shared.mail.dto.MailRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 22.04.25
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MailServiceClient {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MailServiceClient.class);
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
private static final String MAIL_ENDPOINT = "http://gateway/api/mail";
|
||||
|
||||
@Async
|
||||
public void sendMail(String email, String subject, String userMessage) {
|
||||
MailRequest request = new MailRequest(email, subject, userMessage);
|
||||
postEmail(request);
|
||||
}
|
||||
|
||||
private void postEmail(MailRequest request) {
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
|
||||
HttpEntity<MailRequest> entity = new HttpEntity<>(request, headers);
|
||||
|
||||
restTemplate.postForEntity(MAIL_ENDPOINT + "/send", entity, String.class);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to send email to {}: {}", request.getTo(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package dev.rheinsw.shared.mail;
|
||||
|
||||
import dev.rheinsw.shared.mail.dto.MailRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class MailServiceClientTest {
|
||||
|
||||
@Mock
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@InjectMocks
|
||||
private MailServiceClient mailServiceClient;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<HttpEntity<MailRequest>> httpEntityCaptor;
|
||||
|
||||
private AutoCloseable closeable;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
closeable = MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendMail_shouldSendCorrectRequest() {
|
||||
// Arrange
|
||||
String email = "user@example.com";
|
||||
String subject = "Test Subject";
|
||||
String message = "This is a test message.";
|
||||
|
||||
// Act
|
||||
mailServiceClient.sendMail(email, subject, message);
|
||||
|
||||
// Assert
|
||||
verify(restTemplate).postForEntity(
|
||||
eq("http://gateway/api/mail/send"),
|
||||
httpEntityCaptor.capture(),
|
||||
eq(String.class)
|
||||
);
|
||||
|
||||
MailRequest captured = httpEntityCaptor.getValue().getBody(); // extract the MailRequest
|
||||
assert captured != null;
|
||||
assert captured.getTo().equals(email);
|
||||
assert captured.getSubject().equals(subject);
|
||||
assert captured.getMessage().equals(message);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendMail_shouldHandleExceptionDuringPost() {
|
||||
// Arrange
|
||||
String email = "user@example.com";
|
||||
String subject = "Test Subject";
|
||||
String message = "This is a test message.";
|
||||
|
||||
doThrow(new RuntimeException("Simulated error")).when(restTemplate).postForEntity(
|
||||
anyString(),
|
||||
any(),
|
||||
eq(String.class)
|
||||
);
|
||||
|
||||
// Act & Assert
|
||||
assertDoesNotThrow(() -> mailServiceClient.sendMail(email, subject, message),
|
||||
"sendMail should handle exception internally and not throw it");
|
||||
}
|
||||
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception {
|
||||
closeable.close();
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,10 @@
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package dev.rheinsw.server.controller.contact;
|
||||
package dev.rheinsw.server.contact.controller;
|
||||
|
||||
import dev.rheinsw.server.domain.contact.model.ContactRequestDto;
|
||||
import dev.rheinsw.server.usecase.contact.SubmitContactUseCase;
|
||||
import dev.rheinsw.server.contact.domain.model.ContactRequestDto;
|
||||
import dev.rheinsw.server.contact.usecase.SubmitContactUseCase;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -1,4 +1,4 @@
|
||||
package dev.rheinsw.server.domain.contact.model;
|
||||
package dev.rheinsw.server.contact.domain.model;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
@@ -1,4 +1,4 @@
|
||||
package dev.rheinsw.server.domain.contact.model;
|
||||
package dev.rheinsw.server.contact.domain.model;
|
||||
|
||||
import dev.rheinsw.shared.transport.Dto;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package dev.rheinsw.server.domain.contact.config;
|
||||
package dev.rheinsw.server.contact.domain.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -15,5 +15,4 @@ import org.springframework.context.annotation.Configuration;
|
||||
@ConfigurationProperties(prefix = "hcaptcha")
|
||||
public class HCaptchaConfig {
|
||||
private String secret;
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package dev.rheinsw.server.repository.contact;
|
||||
package dev.rheinsw.server.contact.repository;
|
||||
|
||||
import dev.rheinsw.server.domain.contact.model.ContactRequest;
|
||||
import dev.rheinsw.server.contact.domain.model.ContactRequest;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
/**
|
||||
@@ -1,6 +1,6 @@
|
||||
package dev.rheinsw.server.usecase.contact;
|
||||
package dev.rheinsw.server.contact.usecase;
|
||||
|
||||
import dev.rheinsw.server.domain.contact.model.ContactRequestDto;
|
||||
import dev.rheinsw.server.contact.domain.model.ContactRequestDto;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
@@ -1,10 +1,11 @@
|
||||
package dev.rheinsw.server.usecase.contact;
|
||||
package dev.rheinsw.server.contact.usecase;
|
||||
|
||||
import dev.rheinsw.server.domain.contact.model.ContactRequest;
|
||||
import dev.rheinsw.server.domain.contact.model.ContactRequestDto;
|
||||
import dev.rheinsw.server.repository.contact.ContactRequestsRepo;
|
||||
import dev.rheinsw.shared.mail.MailServiceClient;
|
||||
import dev.rheinsw.server.controller.contact.HCaptchaValidator;
|
||||
import dev.rheinsw.server.contact.domain.model.ContactRequest;
|
||||
import dev.rheinsw.server.contact.domain.model.ContactRequestDto;
|
||||
import dev.rheinsw.server.contact.repository.ContactRequestsRepo;
|
||||
import dev.rheinsw.server.contact.util.HCaptchaValidator;
|
||||
import dev.rheinsw.server.mail.domain.MailRequest;
|
||||
import dev.rheinsw.server.mail.usecase.SendMailUseCase;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -24,12 +25,14 @@ public class SubmitContactUseCaseImpl implements SubmitContactUseCase {
|
||||
|
||||
private final HCaptchaValidator captchaValidator;
|
||||
private final ContactRequestsRepo contactRepository;
|
||||
private final MailServiceClient mailServiceClient;
|
||||
private final SendMailUseCase sendMailUseCase; // Inject SendMailUseCase
|
||||
|
||||
public SubmitContactUseCaseImpl(HCaptchaValidator captchaValidator, ContactRequestsRepo contactRepository, MailServiceClient mailServiceClient) {
|
||||
public SubmitContactUseCaseImpl(HCaptchaValidator captchaValidator,
|
||||
ContactRequestsRepo contactRepository,
|
||||
SendMailUseCase sendMailUseCase) {
|
||||
this.captchaValidator = captchaValidator;
|
||||
this.contactRepository = contactRepository;
|
||||
this.mailServiceClient = mailServiceClient;
|
||||
this.sendMailUseCase = sendMailUseCase;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,7 +62,13 @@ public class SubmitContactUseCaseImpl implements SubmitContactUseCase {
|
||||
|
||||
contactRepository.save(message);
|
||||
|
||||
notifyContactAndTeam(request);
|
||||
// Send notifications
|
||||
try {
|
||||
notifyContactAndTeam(request);
|
||||
} catch (Exception e) {
|
||||
log.error("Error sending email notifications: ", e);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error sending notifications");
|
||||
}
|
||||
|
||||
return ResponseEntity.ok("Contact form submitted successfully");
|
||||
}
|
||||
@@ -83,7 +92,12 @@ public class SubmitContactUseCaseImpl implements SubmitContactUseCase {
|
||||
Rhein Software
|
||||
""".formatted(request.name(), request.message());
|
||||
|
||||
mailServiceClient.sendMail(request.email(), userSubject, userBody);
|
||||
// Send confirmation email to user
|
||||
MailRequest userMailRequest = new MailRequest();
|
||||
userMailRequest.setTo(request.email());
|
||||
userMailRequest.setSubject(userSubject);
|
||||
userMailRequest.setMessage(userBody);
|
||||
sendMailUseCase.execute(userMailRequest);
|
||||
|
||||
// Team notification
|
||||
String teamSubject = "Neue Kontaktanfrage";
|
||||
@@ -105,7 +119,12 @@ public class SubmitContactUseCaseImpl implements SubmitContactUseCase {
|
||||
request.message()
|
||||
);
|
||||
|
||||
mailServiceClient.sendMail("rhein.software@gmail.com", teamSubject, teamBody);
|
||||
// Send team notification email
|
||||
MailRequest teamMailRequest = new MailRequest();
|
||||
teamMailRequest.setTo("rhein.software@gmail.com");
|
||||
teamMailRequest.setSubject(teamSubject);
|
||||
teamMailRequest.setMessage(teamBody);
|
||||
sendMailUseCase.execute(teamMailRequest);
|
||||
}
|
||||
|
||||
private String safe(String value) {
|
||||
@@ -1,6 +1,6 @@
|
||||
package dev.rheinsw.server.controller.contact;
|
||||
package dev.rheinsw.server.contact.util;
|
||||
|
||||
import dev.rheinsw.server.domain.contact.config.HCaptchaConfig;
|
||||
import dev.rheinsw.server.contact.domain.model.HCaptchaConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.rheinsw.server.mail.controller;
|
||||
|
||||
import dev.rheinsw.server.mail.usecase.SendMailUseCase;
|
||||
import dev.rheinsw.server.mail.domain.MailRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 04.05.25
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/mail")
|
||||
@RequiredArgsConstructor
|
||||
public class MailController {
|
||||
|
||||
private final SendMailUseCase sendMailUseCase;
|
||||
|
||||
@PostMapping("/send")
|
||||
public ResponseEntity<String> sendEmail(@RequestBody MailRequest request) {
|
||||
return sendMailUseCase.execute(request);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package dev.rheinsw.shared.mail.dto;
|
||||
package dev.rheinsw.server.mail.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@@ -0,0 +1,12 @@
|
||||
package dev.rheinsw.server.mail.usecase;
|
||||
|
||||
import dev.rheinsw.server.mail.domain.MailRequest;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 04.05.25
|
||||
*/
|
||||
public interface ISendMailUseCase {
|
||||
ResponseEntity<String> execute(MailRequest request);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package dev.rheinsw.server.mail.usecase;
|
||||
|
||||
|
||||
import dev.rheinsw.server.mail.domain.MailRequest;
|
||||
import jakarta.mail.MessagingException;
|
||||
import jakarta.mail.internet.MimeMessage;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.mail.javamail.MimeMessageHelper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.mail.javamail.JavaMailSender;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 04.05.25
|
||||
*/
|
||||
@Service
|
||||
public class SendMailUseCase implements ISendMailUseCase {
|
||||
private final JavaMailSender mailSender;
|
||||
|
||||
public SendMailUseCase(JavaMailSender mailSender) {
|
||||
this.mailSender = mailSender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResponseEntity<String> execute(MailRequest request) {
|
||||
try {
|
||||
sendEmail(request);
|
||||
return ResponseEntity.ok("Email sent successfully");
|
||||
} catch (Exception e) {
|
||||
return ResponseEntity.status(500).body("Failed to send email: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void sendEmail(MailRequest request) throws MessagingException {
|
||||
MimeMessage message = mailSender.createMimeMessage();
|
||||
MimeMessageHelper helper = new MimeMessageHelper(message, true);
|
||||
|
||||
helper.setFrom("noreply@rhein-software.dev");
|
||||
helper.setTo(request.getTo());
|
||||
helper.setSubject(request.getSubject());
|
||||
helper.setText(request.getMessage(), false);
|
||||
|
||||
mailSender.send(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,6 +17,19 @@ spring:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
|
||||
mail:
|
||||
host: smtp.resend.com
|
||||
port: 587
|
||||
username: resend
|
||||
password: re_JnLD5ndg_GnKtXcTqskXm1bg7Wxnghna3
|
||||
properties:
|
||||
mail:
|
||||
smtp:
|
||||
auth: true
|
||||
starttls:
|
||||
enable: true
|
||||
default-encoding: UTF-8
|
||||
|
||||
eureka:
|
||||
client:
|
||||
service-url:
|
||||
|
||||
Reference in New Issue
Block a user