diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java new file mode 100644 index 000000000..a8f1b30c1 --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/RoadMapService.java @@ -0,0 +1,41 @@ +package wooteco.prolog.roadmap.application; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import wooteco.prolog.roadmap.application.dto.KeywordsResponse; +import wooteco.prolog.roadmap.domain.Curriculum; +import wooteco.prolog.roadmap.domain.Keyword; +import wooteco.prolog.roadmap.domain.repository.CurriculumRepository; +import wooteco.prolog.roadmap.domain.repository.KeywordRepository; +import wooteco.prolog.session.domain.Session; +import wooteco.prolog.session.domain.repository.SessionRepository; + +@RequiredArgsConstructor +@Transactional +@Service +public class RoadMapService { + + private final CurriculumRepository curriculumRepository; + private final SessionRepository sessionRepository; + private final KeywordRepository keywordRepository; + + @Transactional(readOnly = true) + public KeywordsResponse findAllKeywords(final Long curriculumId) { + final Curriculum curriculum = curriculumRepository.findById(curriculumId) + .orElseThrow(() -> new IllegalArgumentException( + "해당 커리큘럼이 존재하지 않습니다. curriculumId = " + curriculumId)); + + final Set sessionIds = sessionRepository.findAllByCurriculumId(curriculum.getId()) + .stream() + .map(Session::getId) + .collect(Collectors.toSet()); + + final List keywords = keywordRepository.findBySessionIdIn(sessionIds); + + return KeywordsResponse.createResponseWithChildren(keywords); + } +} diff --git a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java index aee3cd48b..bbd9c3018 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/application/dto/KeywordsResponse.java @@ -23,4 +23,12 @@ public static KeywordsResponse createResponse(final List keywords) { .collect(Collectors.toList()); return new KeywordsResponse(keywordsResponse); } + + public static KeywordsResponse createResponseWithChildren(final List keywords) { + List keywordsResponse = keywords.stream() + .filter(it -> it.getParent() == null) + .map(KeywordResponse::createWithAllChildResponse) + .collect(Collectors.toList()); + return new KeywordsResponse(keywordsResponse); + } } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java index 3ef198afc..d15d4e7ae 100644 --- a/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java +++ b/backend/src/main/java/wooteco/prolog/roadmap/domain/repository/KeywordRepository.java @@ -1,6 +1,7 @@ package wooteco.prolog.roadmap.domain.repository; import java.util.List; +import java.util.Set; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -17,4 +18,6 @@ public interface KeywordRepository extends JpaRepository { @Query("SELECT k FROM Keyword k " + "WHERE k.sessionId = :sessionId AND k.parent IS NULL") List findBySessionIdAndParentIsNull(@Param("sessionId") Long sessionId); + + List findBySessionIdIn(final Set sessionIds); } diff --git a/backend/src/main/java/wooteco/prolog/roadmap/ui/RoadmapController.java b/backend/src/main/java/wooteco/prolog/roadmap/ui/RoadmapController.java new file mode 100644 index 000000000..16500a192 --- /dev/null +++ b/backend/src/main/java/wooteco/prolog/roadmap/ui/RoadmapController.java @@ -0,0 +1,20 @@ +package wooteco.prolog.roadmap.ui; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import wooteco.prolog.roadmap.application.RoadMapService; +import wooteco.prolog.roadmap.application.dto.KeywordsResponse; + +@RequiredArgsConstructor +@RestController +public class RoadmapController { + + private final RoadMapService roadMapService; + + @GetMapping("/roadmaps") + public KeywordsResponse findKeywords(@RequestParam final Long curriculumId) { + return roadMapService.findAllKeywords(curriculumId); + } +} diff --git a/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java b/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java new file mode 100644 index 000000000..469f81277 --- /dev/null +++ b/backend/src/test/java/wooteco/prolog/roadmap/application/RoadMapServiceTest.java @@ -0,0 +1,67 @@ +package wooteco.prolog.roadmap.application; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import wooteco.prolog.roadmap.application.dto.KeywordsResponse; +import wooteco.prolog.roadmap.domain.Curriculum; +import wooteco.prolog.roadmap.domain.Keyword; +import wooteco.prolog.roadmap.domain.repository.CurriculumRepository; +import wooteco.prolog.roadmap.domain.repository.KeywordRepository; +import wooteco.prolog.session.domain.Session; +import wooteco.prolog.session.domain.repository.SessionRepository; + +@ExtendWith(MockitoExtension.class) +class RoadMapServiceTest { + + @Mock + private CurriculumRepository curriculumRepository; + @Mock + private SessionRepository sessionRepository; + @Mock + private KeywordRepository keywordRepository; + @InjectMocks + private RoadMapService roadMapService; + + @Test + @DisplayName("curriculumId가 주어지면 해당 커리큘럼의 키워드들을 전부 조회할 수 있다.") + void findAllKeywords() throws Exception { + //given + final Curriculum curriculum = new Curriculum(1L, "커리큘럼1"); + final Session session = new Session(1L, curriculum.getId(), "세션1"); + final List sessions = Arrays.asList(session); + final Keyword keyword = Keyword.createKeyword("자바1", "자바 설명1", 1, 5, session.getId(), + null); + + when(curriculumRepository.findById(anyLong())) + .thenReturn(Optional.of(curriculum)); + + when(sessionRepository.findAllByCurriculumId(anyLong())) + .thenReturn(sessions); + + when(keywordRepository.findBySessionIdIn(any())) + .thenReturn(Arrays.asList(keyword)); + + final KeywordsResponse expected = KeywordsResponse.createResponse(Arrays.asList(keyword)); + + //when + final KeywordsResponse actual = + roadMapService.findAllKeywords(curriculum.getId()); + + //then + assertThat(actual) + .usingRecursiveComparison() + .isEqualTo(expected); + } +} diff --git a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java index c010def07..70255a8ba 100644 --- a/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java +++ b/backend/src/test/java/wooteco/prolog/roadmap/repository/KeywordRepositoryTest.java @@ -3,12 +3,16 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; import javax.persistence.EntityManager; -import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import wooteco.prolog.roadmap.domain.Curriculum; import wooteco.prolog.roadmap.domain.Keyword; +import wooteco.prolog.roadmap.domain.repository.CurriculumRepository; import wooteco.prolog.roadmap.domain.repository.KeywordRepository; import wooteco.prolog.session.domain.Session; import wooteco.prolog.session.domain.repository.SessionRepository; @@ -23,6 +27,8 @@ class KeywordRepositoryTest { private SessionRepository sessionRepository; @Autowired private EntityManager em; + @Autowired + private CurriculumRepository curriculumRepository; @Test void 부모_키워드와_1뎁스까지의_자식_키워드를_함께_조회할_수_있다() { @@ -105,7 +111,54 @@ class KeywordRepositoryTest { sessionId); // then - Assertions.assertThat(extractParentKeyword.size()).isEqualTo(1); + assertThat(extractParentKeyword.size()).isEqualTo(1); + } + + @Test + @DisplayName("세션 ID 리스트로 키워드 리스트를 조회한다") + void findBySessionIdIn() { + //given + final Curriculum curriculum = curriculumRepository.save(new Curriculum("커리큘럼1")); + + final Session session1 = sessionRepository.save(new Session(curriculum.getId(), "세션1")); + final Session session2 = sessionRepository.save(new Session(curriculum.getId(), "세션2")); + final Session session3 = sessionRepository.save(new Session(curriculum.getId(), "세션3")); + final Session session4 = sessionRepository.save(new Session(curriculum.getId(), "세션4")); + final Session session5 = sessionRepository.save(new Session(curriculum.getId(), "세션5")); + + final Keyword keyword1 = keywordRepository.save( + Keyword.createKeyword("자바1", "자바 설명1", 1, 5, session1.getId(), null)); + final Keyword keyword2 = keywordRepository.save( + Keyword.createKeyword("자바2", "자바 설명2", 2, 5, session1.getId(), keyword1)); + final Keyword keyword3 = keywordRepository.save( + Keyword.createKeyword("자바3", "자바 설명3", 3, 5, session1.getId(), null)); + final Keyword keyword4 = keywordRepository.save( + Keyword.createKeyword("자바4", "자바 설명4", 4, 5, session1.getId(), keyword3)); + keywordRepository.save( + Keyword.createKeyword("자바5", "자바 설명5", 5, 5, session2.getId(), null)); + keywordRepository.save( + Keyword.createKeyword("자바6", "자바 설명6", 6, 5, session2.getId(), keyword1)); + keywordRepository.save( + Keyword.createKeyword("자바7", "자바 설명7", 7, 5, session2.getId(), null)); + keywordRepository.save( + Keyword.createKeyword("자바8", "자바 설명8", 8, 5, session3.getId(), keyword2)); + final Keyword keyword9 = keywordRepository.save( + Keyword.createKeyword("자바9", "자바 설명9", 9, 5, session4.getId(), keyword2)); + final Keyword keyword10 = keywordRepository.save( + Keyword.createKeyword("자바10", "자바 설명10", 10, 5, session5.getId(), null)); + + final HashSet sessionIds = new HashSet<>( + Arrays.asList(session1.getId(), session4.getId(), session5.getId()) + ); + + //when + final List keywords = keywordRepository.findBySessionIdIn(sessionIds); + + //then + assertThat(keywords) + .usingRecursiveComparison() + .ignoringFields("id", "parent.id") + .isEqualTo(Arrays.asList(keyword1, keyword2, keyword3, keyword4, keyword9, keyword10)); } private Keyword createKeywordParent(final Keyword keyword) {