diff --git a/reactive-programming/completable-future/build.gradle b/reactive-programming/completable-future/build.gradle index 853bcaab..46693842 100644 --- a/reactive-programming/completable-future/build.gradle +++ b/reactive-programming/completable-future/build.gradle @@ -13,6 +13,10 @@ dependencies { compileOnly 'org.projectlombok:lombok:1.18.28' annotationProcessor 'org.projectlombok:lombok:1.18.28' + implementation 'org.slf4j:slf4j-api:2.0.7' + implementation 'ch.qos.logback:logback-classic:1.4.8' + implementation 'ch.qos.logback:logback-core:1.4.8' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' } diff --git a/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/UserBlockingService.java b/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/UserBlockingService.java new file mode 100644 index 00000000..b747049a --- /dev/null +++ b/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/UserBlockingService.java @@ -0,0 +1,47 @@ +package org.example.completablefuture.blocking; + +import lombok.RequiredArgsConstructor; +import org.example.completablefuture.blocking.repository.ArticleRepository; +import org.example.completablefuture.blocking.repository.FollowRepository; +import org.example.completablefuture.blocking.repository.ImageRepository; +import org.example.completablefuture.blocking.repository.UserRepository; +import org.example.completablefuture.common.Article; +import org.example.completablefuture.common.Image; +import org.example.completablefuture.common.User; + +import java.util.Optional; +import java.util.stream.Collectors; + +@RequiredArgsConstructor +public class UserBlockingService { + private final UserRepository userRepository; + private final ArticleRepository articleRepository; + private final ImageRepository imageRepository; + private final FollowRepository followRepository; + + public Optional getUserById(String id) { + return userRepository.findById(id) + .map(user -> { + var image = imageRepository.findById(user.getProfileImageId()) + .map(imageEntity -> { + return new Image(imageEntity.getId(), imageEntity.getName(), imageEntity.getUrl()); + }); + + var articles = articleRepository.findAllByUserId(user.getId()) + .stream().map(articleEntity -> + new Article(articleEntity.getId(), articleEntity.getTitle(), articleEntity.getContent())) + .collect(Collectors.toList()); + + var followCount = followRepository.countByUserId(user.getId()); + + return new User( + user.getId(), + user.getName(), + user.getAge(), + image, + articles, + followCount + ); + }); + } +} \ No newline at end of file diff --git a/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/ArticleRepository.java b/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/ArticleRepository.java new file mode 100644 index 00000000..c6d73177 --- /dev/null +++ b/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/ArticleRepository.java @@ -0,0 +1,30 @@ +package org.example.completablefuture.blocking.repository; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.example.completablefuture.common.repository.ArticleEntity; + +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +public class ArticleRepository { + private static List articleEntities; + + public ArticleRepository() { + articleEntities = List.of( + new ArticleEntity("1", "소식1", "내용1", "1234"), + new ArticleEntity("2", "소식2", "내용2", "1234"), + new ArticleEntity("3", "소식3", "내용3", "10000") + ); + } + + @SneakyThrows + public List findAllByUserId(String userId) { + log.info("ArticleRepository.findAllByUserId: {}", userId); + Thread.sleep(1000); + return articleEntities.stream() + .filter(articleEntity -> articleEntity.getUserId().equals(userId)) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/FollowRepository.java b/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/FollowRepository.java new file mode 100644 index 00000000..bc6f14ea --- /dev/null +++ b/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/FollowRepository.java @@ -0,0 +1,22 @@ +package org.example.completablefuture.blocking.repository; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +@Slf4j +public class FollowRepository { + private Map userFollowCountMap; + + public FollowRepository() { + userFollowCountMap = Map.of("1234", 1000L); + } + + @SneakyThrows + public Long countByUserId(String userId) { + log.info("FollowRepository.countByUserId: {}", userId); + Thread.sleep(1000); + return userFollowCountMap.getOrDefault(userId, 0L); + } +} \ No newline at end of file diff --git a/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/ImageRepository.java b/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/ImageRepository.java new file mode 100644 index 00000000..4df2b1ca --- /dev/null +++ b/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/ImageRepository.java @@ -0,0 +1,26 @@ +package org.example.completablefuture.blocking.repository; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.example.completablefuture.common.repository.ImageEntity; + +import java.util.Map; +import java.util.Optional; + +@Slf4j +public class ImageRepository { + private final Map imageMap; + + public ImageRepository() { + imageMap = Map.of( + "image#1000", new ImageEntity("image#1000", "profileImage", "https://dailyone.com/images/1000") + ); + } + + @SneakyThrows + public Optional findById(String id) { + log.info("ImageRepository.findById: {}", id); + Thread.sleep(1000); + return Optional.ofNullable(imageMap.get(id)); + } +} \ No newline at end of file diff --git a/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/UserRepository.java b/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/UserRepository.java new file mode 100644 index 00000000..f0ad22b1 --- /dev/null +++ b/reactive-programming/completable-future/src/main/java/org/example/completablefuture/blocking/repository/UserRepository.java @@ -0,0 +1,27 @@ +package org.example.completablefuture.blocking.repository; + +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.example.completablefuture.common.repository.UserEntity; + +import java.util.Map; +import java.util.Optional; + +@Slf4j +public class UserRepository { + private final Map userMap; + + public UserRepository() { + var user = new UserEntity("1234", "taewoo", 32, "image#1000"); + + userMap = Map.of("1234", user); + } + + @SneakyThrows + public Optional findById(String userId) { + log.info("UserRepository.findById: {}", userId); + Thread.sleep(1000); + var user = userMap.get(userId); + return Optional.ofNullable(user); + } +} \ No newline at end of file diff --git a/reactive-programming/completable-future/src/test/java/org/example/completablefuture/blocking/UserBlockingServiceTest.java b/reactive-programming/completable-future/src/test/java/org/example/completablefuture/blocking/UserBlockingServiceTest.java new file mode 100644 index 00000000..6497684c --- /dev/null +++ b/reactive-programming/completable-future/src/test/java/org/example/completablefuture/blocking/UserBlockingServiceTest.java @@ -0,0 +1,70 @@ +package org.example.completablefuture.blocking; + +import org.example.completablefuture.blocking.repository.ArticleRepository; +import org.example.completablefuture.blocking.repository.FollowRepository; +import org.example.completablefuture.blocking.repository.ImageRepository; +import org.example.completablefuture.blocking.repository.UserRepository; +import org.example.completablefuture.common.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +class UserBlockingServiceTest { + UserBlockingService userBlockingService; + UserRepository userRepository; + ArticleRepository articleRepository; + ImageRepository imageRepository; + FollowRepository followRepository; + + @BeforeEach + void setUp() { + userRepository = new UserRepository(); + articleRepository = new ArticleRepository(); + imageRepository = new ImageRepository(); + followRepository = new FollowRepository(); + + userBlockingService = new UserBlockingService( + userRepository, articleRepository, imageRepository, followRepository + ); + } + + @Test + void getUserEmptyIfInvalidUserIdIsGiven() { + // given + String userId = "invalid_user_id"; + + // when + Optional user = userBlockingService.getUserById(userId); + + // then + assertTrue(user.isEmpty()); + } + + @Test + void testGetUser() { + // given + String userId = "1234"; + + // when + Optional optionalUser = userBlockingService.getUserById(userId); + + // then + assertFalse(optionalUser.isEmpty()); + var user = optionalUser.get(); + assertEquals(user.getName(), "taewoo"); + assertEquals(user.getAge(), 32); + + assertFalse(user.getProfileImage().isEmpty()); + var image = user.getProfileImage().get(); + assertEquals(image.getId(), "image#1000"); + assertEquals(image.getName(), "profileImage"); + assertEquals(image.getUrl(), "https://dailyone.com/images/1000"); + + assertEquals(2, user.getArticleList().size()); + + assertEquals(1000, user.getFollowCount()); + } +} \ No newline at end of file