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>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-mail</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
<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.contact.domain.model.ContactRequestDto;
|
||||||
import dev.rheinsw.server.usecase.contact.SubmitContactUseCase;
|
import dev.rheinsw.server.contact.usecase.SubmitContactUseCase;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.Column;
|
||||||
import jakarta.persistence.Entity;
|
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;
|
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.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
@@ -15,5 +15,4 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
@ConfigurationProperties(prefix = "hcaptcha")
|
@ConfigurationProperties(prefix = "hcaptcha")
|
||||||
public class HCaptchaConfig {
|
public class HCaptchaConfig {
|
||||||
private String secret;
|
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;
|
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;
|
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.contact.domain.model.ContactRequest;
|
||||||
import dev.rheinsw.server.domain.contact.model.ContactRequestDto;
|
import dev.rheinsw.server.contact.domain.model.ContactRequestDto;
|
||||||
import dev.rheinsw.server.repository.contact.ContactRequestsRepo;
|
import dev.rheinsw.server.contact.repository.ContactRequestsRepo;
|
||||||
import dev.rheinsw.shared.mail.MailServiceClient;
|
import dev.rheinsw.server.contact.util.HCaptchaValidator;
|
||||||
import dev.rheinsw.server.controller.contact.HCaptchaValidator;
|
import dev.rheinsw.server.mail.domain.MailRequest;
|
||||||
|
import dev.rheinsw.server.mail.usecase.SendMailUseCase;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
@@ -24,12 +25,14 @@ public class SubmitContactUseCaseImpl implements SubmitContactUseCase {
|
|||||||
|
|
||||||
private final HCaptchaValidator captchaValidator;
|
private final HCaptchaValidator captchaValidator;
|
||||||
private final ContactRequestsRepo contactRepository;
|
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.captchaValidator = captchaValidator;
|
||||||
this.contactRepository = contactRepository;
|
this.contactRepository = contactRepository;
|
||||||
this.mailServiceClient = mailServiceClient;
|
this.sendMailUseCase = sendMailUseCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -59,7 +62,13 @@ public class SubmitContactUseCaseImpl implements SubmitContactUseCase {
|
|||||||
|
|
||||||
contactRepository.save(message);
|
contactRepository.save(message);
|
||||||
|
|
||||||
|
// Send notifications
|
||||||
|
try {
|
||||||
notifyContactAndTeam(request);
|
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");
|
return ResponseEntity.ok("Contact form submitted successfully");
|
||||||
}
|
}
|
||||||
@@ -83,7 +92,12 @@ public class SubmitContactUseCaseImpl implements SubmitContactUseCase {
|
|||||||
Rhein Software
|
Rhein Software
|
||||||
""".formatted(request.name(), request.message());
|
""".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
|
// Team notification
|
||||||
String teamSubject = "Neue Kontaktanfrage";
|
String teamSubject = "Neue Kontaktanfrage";
|
||||||
@@ -105,7 +119,12 @@ public class SubmitContactUseCaseImpl implements SubmitContactUseCase {
|
|||||||
request.message()
|
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) {
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.stereotype.Service;
|
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.AllArgsConstructor;
|
||||||
import lombok.Data;
|
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:
|
hibernate:
|
||||||
format_sql: true
|
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:
|
eureka:
|
||||||
client:
|
client:
|
||||||
service-url:
|
service-url:
|
||||||
|
|||||||
Reference in New Issue
Block a user