Backend migration

This commit is contained in:
2025-04-27 17:42:02 +00:00
parent e114f9553a
commit 8b1d6eb7cf
52 changed files with 2326 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.rheinsw</groupId>
<artifactId>rheinsw-backend</artifactId>
<version>1.0.0</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>mailService</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- Spring Boot starter for REST -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/jakarta.mail/jakarta.mail-api -->
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.4.0-jre</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>dev.rheinsw</groupId>
<artifactId>shared</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
<!-- test suite -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,18 @@
package dev.rheinsw.mail;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author Thatsaphorn Atchariyaphap
* @since 22.04.25
*/
@SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration.class,
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration.class
})
public class MailServiceApplication {
public static void main(String[] args) {
SpringApplication.run(MailServiceApplication.class, args);
}
}

View File

@@ -0,0 +1,33 @@
package dev.rheinsw.mail.controller;
import dev.rheinsw.shared.mail.dto.MailRequest;
import dev.rheinsw.mail.service.MailService;
import jakarta.mail.MessagingException;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestBody;
/**
* @author Thatsaphorn Atchariyaphap
* @since 22.04.25
*/
@RestController
@RequestMapping("/mail")
@RequiredArgsConstructor
public class MailController {
private final MailService mailService;
@PostMapping("/send")
public ResponseEntity<String> sendEmail(@RequestBody MailRequest request) {
try {
mailService.sendEmail(request);
return ResponseEntity.ok("Email sent successfully");
} catch (MessagingException e) {
return ResponseEntity.status(500).body("Failed to send email: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,33 @@
package dev.rheinsw.mail.service;
import dev.rheinsw.shared.mail.dto.MailRequest;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
/**
* @author Thatsaphorn Atchariyaphap
* @since 22.04.25
*/
@Service
@RequiredArgsConstructor
public class MailService {
private final JavaMailSender mailSender;
public 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);
}
}

View File

@@ -0,0 +1,24 @@
server:
port: 0 # random port
spring:
application:
name: mailService
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:
defaultZone: http://localhost:8761/eureka/

View File

@@ -0,0 +1,63 @@
package dev.rheinsw.mail.controller;
import dev.rheinsw.mail.service.MailService;
import dev.rheinsw.shared.mail.dto.MailRequest;
import jakarta.mail.MessagingException;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;
import org.springframework.http.ResponseEntity;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
class MailControllerTest {
@Mock
private MailService mailService;
@InjectMocks
private MailController mailController;
private AutoCloseable closeable;
@BeforeEach
void setUp() {
closeable = MockitoAnnotations.openMocks(this);
}
@Test
void sendEmail_shouldReturnOk_whenEmailSentSuccessfully() throws MessagingException {
// Arrange
MailRequest request = new MailRequest("user@example.com", "Test Subject", "Message");
// Act
ResponseEntity<String> response = mailController.sendEmail(request);
// Assert
verify(mailService).sendEmail(request);
assertEquals(200, response.getStatusCodeValue());
assertEquals("Email sent successfully", response.getBody());
}
@Test
void sendEmail_shouldReturnServerError_whenMessagingExceptionThrown() throws MessagingException {
// Arrange
MailRequest request = new MailRequest("user@example.com", "Test Subject", "Message");
doThrow(new MessagingException("SMTP failed")).when(mailService).sendEmail(request);
// Act
ResponseEntity<String> response = mailController.sendEmail(request);
// Assert
verify(mailService).sendEmail(request);
assertEquals(500, response.getStatusCodeValue());
assertTrue(response.getBody().contains("Failed to send email: SMTP failed"));
}
@AfterEach
void tearDown() throws Exception {
closeable.close();
}
}

View File

@@ -0,0 +1,60 @@
package dev.rheinsw.mail.service;
import dev.rheinsw.shared.mail.dto.MailRequest;
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.*;
class MailServiceTest {
@Mock
private JavaMailSender mailSender;
@InjectMocks
private MailService mailService;
private AutoCloseable closeable;
@BeforeEach
void setUp() {
closeable = MockitoAnnotations.openMocks(this);
}
@Test
void sendEmail_shouldSendMessageSuccessfully() throws MessagingException {
// Arrange
MailRequest request = new MailRequest("user@example.com", "Hello", "This is a test message.");
MimeMessage mimeMessage = mock(MimeMessage.class);
when(mailSender.createMimeMessage()).thenReturn(mimeMessage);
// Act & Assert
assertDoesNotThrow(() -> mailService.sendEmail(request));
verify(mailSender).send(mimeMessage);
}
@Test
void sendEmail_shouldThrowMessagingException_whenMailSenderFails() {
// Arrange
MailRequest request = new MailRequest("user@example.com", "Hello", "This is a test message.");
MimeMessage mimeMessage = mock(MimeMessage.class);
when(mailSender.createMimeMessage()).thenReturn(mimeMessage);
doThrow(new RuntimeException("Simulated failure")).when(mailSender).send(mimeMessage);
// Act & Assert
RuntimeException exception = assertThrows(RuntimeException.class, () -> {
mailService.sendEmail(request);
});
assertEquals("Simulated failure", exception.getMessage());
}
}