Backend migration
This commit is contained in:
88
backend/shared/pom.xml
Normal file
88
backend/shared/pom.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<?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>
|
||||
</parent>
|
||||
|
||||
<artifactId>shared</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
||||
<jackson-databind.version>2.18.3</jackson-databind.version>
|
||||
<lombok.version>1.18.38</lombok.version>
|
||||
</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 stuff -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Others -->
|
||||
<!-- https://mvnrepository.com/artifact/jakarta.persistence/jakarta.persistence-api -->
|
||||
<dependency>
|
||||
<groupId>jakarta.persistence</groupId>
|
||||
<artifactId>jakarta.persistence-api</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Tests -->
|
||||
<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>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson-databind.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,37 @@
|
||||
package dev.rheinsw.shared.entity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.PrePersist;
|
||||
import jakarta.persistence.PreUpdate;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 26.04.25
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@MappedSuperclass
|
||||
public abstract class BaseEntity {
|
||||
|
||||
@Column(name = "createdDateTime", nullable = false, updatable = false)
|
||||
private LocalDateTime createdDateTime;
|
||||
|
||||
@Column(name = "modifiedDateTime")
|
||||
private LocalDateTime modifiedDateTime;
|
||||
|
||||
@PrePersist
|
||||
protected void onCreate() {
|
||||
createdDateTime = LocalDateTime.now();
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
protected void onUpdate() {
|
||||
modifiedDateTime = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
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";
|
||||
|
||||
@Value("${INTERNAL_API_KEY}")
|
||||
private String internalApiKey;
|
||||
|
||||
@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();
|
||||
headers.set("X-Internal-Auth", internalApiKey);
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package dev.rheinsw.shared.mail.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 22.04.25
|
||||
*/
|
||||
@Data
|
||||
@Getter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MailRequest {
|
||||
private String to;
|
||||
private String subject;
|
||||
private String message;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package dev.rheinsw.shared.rest;
|
||||
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 23.04.25
|
||||
*/
|
||||
@Configuration
|
||||
public class RestTemplateConfig {
|
||||
|
||||
@LoadBalanced
|
||||
@Bean
|
||||
public RestTemplate mailRestTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.rheinsw.shared.transport;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Bummsa / BoomerHD / Thatsaphorn Atchariyaphap
|
||||
* @since 21.04.25
|
||||
*/
|
||||
public interface Dto extends Serializable {
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package dev.rheinsw.shared.entity;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class BaseEntityTest {
|
||||
|
||||
// Dummy entity for testing
|
||||
static class DummyEntity extends BaseEntity {
|
||||
}
|
||||
|
||||
@Test
|
||||
void onCreate_shouldSetCreatedDateTime() {
|
||||
// Arrange
|
||||
DummyEntity entity = new DummyEntity();
|
||||
|
||||
// Act
|
||||
entity.onCreate();
|
||||
|
||||
// Assert
|
||||
assertNotNull(entity.getCreatedDateTime(), "createdDateTime should be set");
|
||||
assertNull(entity.getModifiedDateTime(), "modifiedDateTime should still be null after creation");
|
||||
}
|
||||
|
||||
@Test
|
||||
void onUpdate_shouldSetModifiedDateTime() {
|
||||
// Arrange
|
||||
DummyEntity entity = new DummyEntity();
|
||||
|
||||
// Act
|
||||
entity.onUpdate();
|
||||
|
||||
// Assert
|
||||
assertNotNull(entity.getModifiedDateTime(), "modifiedDateTime should be set");
|
||||
assertNull(entity.getCreatedDateTime(), "createdDateTime should still be null if onCreate() is not called");
|
||||
}
|
||||
|
||||
@Test
|
||||
void onCreate_thenOnUpdate_shouldSetBothTimestamps() throws InterruptedException {
|
||||
// Arrange
|
||||
DummyEntity entity = new DummyEntity();
|
||||
|
||||
// Act
|
||||
entity.onCreate();
|
||||
LocalDateTime created = entity.getCreatedDateTime();
|
||||
Thread.sleep(10); // slight pause to differentiate timestamps
|
||||
entity.onUpdate();
|
||||
LocalDateTime modified = entity.getModifiedDateTime();
|
||||
|
||||
// Assert
|
||||
assertNotNull(created);
|
||||
assertNotNull(modified);
|
||||
assertTrue(modified.isAfter(created), "modifiedDateTime should be after createdDateTime");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package dev.rheinsw.shared.rest;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class RestTemplateConfigTest {
|
||||
|
||||
private final RestTemplateConfig restTemplateConfig = new RestTemplateConfig();
|
||||
|
||||
@Test
|
||||
void mailRestTemplate_shouldReturnNonNullRestTemplate() {
|
||||
// Act
|
||||
RestTemplate restTemplate = restTemplateConfig.mailRestTemplate();
|
||||
|
||||
// Assert
|
||||
assertNotNull(restTemplate, "RestTemplate should not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void mailRestTemplate_shouldCreateNewInstanceEachTime() {
|
||||
// Act
|
||||
RestTemplate restTemplate1 = restTemplateConfig.mailRestTemplate();
|
||||
RestTemplate restTemplate2 = restTemplateConfig.mailRestTemplate();
|
||||
|
||||
// Assert
|
||||
assertNotSame(restTemplate1, restTemplate2, "Each call should create a new RestTemplate instance");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user