Initial Commit

This commit is contained in:
2025-05-04 12:18:13 +02:00
commit a4f1a58f15
113 changed files with 11439 additions and 0 deletions

View File

@@ -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();
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;
}

View File

@@ -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();
}
}

View File

@@ -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 {
}

View File

@@ -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");
}
}

View File

@@ -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();
}
}

View File

@@ -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");
}
}