diff --git a/Api-Module/src/docs/asciidoc/Tag.adoc b/Api-Module/src/docs/asciidoc/Tag.adoc index 088305da..e832a146 100644 --- a/Api-Module/src/docs/asciidoc/Tag.adoc +++ b/Api-Module/src/docs/asciidoc/Tag.adoc @@ -11,11 +11,21 @@ operation::TagControllerTest/createParentTagTest/[snippets='http-request,request operation::TagControllerTest/createChildTagTest/[snippets='http-request,path-parameters,request-fields,http-response,response-fields'] +[[GetAllTagTest]] +=== 상위 & 하위 태그 전체 조회 API + +operation::TagControllerTest/getAllTagsTest/[snippets='http-request,http-response,response-fields'] + [[GetParentTagTest]] === 상위 태그 조회 API operation::TagControllerTest/getAllParentTagByUserTest/[snippets='http-request,request-headers,http-response,response-fields'] +[[GetChildTagTest]] +=== 하위 태그 전체 조회 API + +operation::TagControllerTest/getAllChildTagTest/[snippets='http-request,path-parameters,http-response,response-fields'] + [[GetTopRankTagTest]] === 연도 내 경험 최근 추가 순 태그 조회 API @@ -24,12 +34,7 @@ operation::TagControllerTest/getTopRankParentTagTest/[snippets='http-request,req [[GetParentTagsByFilter]] === 연도 내 상위 태그 조회 API -operation::TagControllerTest/getParentTagsByFilter/[snippets='http-request,http-response,response-fields'] - -[[GetChildTagTest]] -=== 하위 태그 전체 조회 API - -operation::TagControllerTest/getAllChildTagTest/[snippets='http-request,path-parameters,http-response,response-fields'] +operation::TagControllerTest/getParentTagsByYear/[snippets='http-request,http-response,response-fields'] [[GetChildTagsByFilter]] === 상위 태그 내 하위 태그 조회 API diff --git a/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/application/dto/GetTag.kt b/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/application/dto/GetTag.kt new file mode 100644 index 00000000..8c9490b7 --- /dev/null +++ b/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/application/dto/GetTag.kt @@ -0,0 +1,20 @@ +package com.bamyanggang.apimodule.domain.tag.application.dto + +import java.util.* + +class GetTag { + data class Response( + val tags: List + ) + + data class ParentTagDetail( + val id: UUID, + val name: String, + val childTags: List + ) + + data class ChildTagDetail( + val id: UUID, + val name: String, + ) +} diff --git a/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/application/service/TagGetService.kt b/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/application/service/TagGetService.kt index 73fe8425..226f692f 100644 --- a/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/application/service/TagGetService.kt +++ b/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/application/service/TagGetService.kt @@ -3,6 +3,7 @@ package com.bamyanggang.apimodule.domain.tag.application.service import com.bamyanggang.apimodule.common.getAuthenticationPrincipal import com.bamyanggang.apimodule.domain.tag.application.dto.GetChildTag import com.bamyanggang.apimodule.domain.tag.application.dto.GetParentTag +import com.bamyanggang.apimodule.domain.tag.application.dto.GetTag import com.bamyanggang.domainmodule.domain.experience.service.ExperienceReader import com.bamyanggang.domainmodule.domain.tag.service.TagReader import org.springframework.stereotype.Service @@ -104,6 +105,7 @@ class TagGetService( ) } + @Transactional(readOnly = true) fun getAllYearsByParentTagId(parentTagId: UUID): GetParentTag.Years { val experiences = getAuthenticationPrincipal().let { experienceReader.readByUserIdAndParentTagId(it, parentTagId) @@ -116,4 +118,28 @@ class TagGetService( return GetParentTag.Years(years) } + + @Transactional(readOnly = true) + fun getAllTags(): GetTag.Response { + val parentTags = getAuthenticationPrincipal().let { + tagReader.readAllParentTagsByUserId(it) + } + + val parentTagDetails = parentTags.map { + val childTagDetails = tagReader.readChildTagsByParentTagId(it.id).map { + GetTag.ChildTagDetail( + it.id, + it.name + ) + } + + GetTag.ParentTagDetail( + it.id, + it.name, + childTagDetails + ) + } + + return GetTag.Response(parentTagDetails) + } } diff --git a/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagApi.kt b/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagApi.kt index b165e9a3..ae5bab59 100644 --- a/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagApi.kt +++ b/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagApi.kt @@ -2,6 +2,7 @@ package com.bamyanggang.apimodule.domain.tag.presentation object TagApi { const val BASE_URL = "/api/tags" + const val ALL_TAGS = "${BASE_URL}/all-tags" const val MY_PARENT_TAG_URL = "$BASE_URL/my" const val TOP_RANK_TAG_URL = "$BASE_URL/top-rank" const val TAG_PATH_VARIABLE_URL = "$BASE_URL/{tagId}" diff --git a/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagController.kt b/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagController.kt index dbe9a2b9..127e42a5 100644 --- a/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagController.kt +++ b/Api-Module/src/main/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagController.kt @@ -3,6 +3,7 @@ package com.bamyanggang.apimodule.domain.tag.presentation import com.bamyanggang.apimodule.domain.tag.application.dto.CreateTag import com.bamyanggang.apimodule.domain.tag.application.dto.GetChildTag import com.bamyanggang.apimodule.domain.tag.application.dto.GetParentTag +import com.bamyanggang.apimodule.domain.tag.application.dto.GetTag import com.bamyanggang.apimodule.domain.tag.application.service.TagCreateService import com.bamyanggang.apimodule.domain.tag.application.service.TagDeleteService import com.bamyanggang.apimodule.domain.tag.application.service.TagGetService @@ -38,6 +39,11 @@ class TagController( return tagGetService.getAllChildTagsByParentTagId(parentTagId) } + @GetMapping(TagApi.ALL_TAGS) + fun getAllTags(): GetTag.Response { + return tagGetService.getAllTags() + } + @GetMapping(TagApi.BASE_URL) fun getParentTagsByYear(@RequestParam("year") year: Int): GetParentTag.TotalTagInfo { return tagGetService.getAllParentTagsByYear(year) diff --git a/Api-Module/src/test/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagControllerTest.kt b/Api-Module/src/test/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagControllerTest.kt index e4554a00..3cccd759 100644 --- a/Api-Module/src/test/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagControllerTest.kt +++ b/Api-Module/src/test/kotlin/com/bamyanggang/apimodule/domain/tag/presentation/TagControllerTest.kt @@ -4,6 +4,7 @@ import com.bamyanggang.apimodule.BaseRestDocsTest import com.bamyanggang.apimodule.domain.tag.application.dto.CreateTag import com.bamyanggang.apimodule.domain.tag.application.dto.GetChildTag import com.bamyanggang.apimodule.domain.tag.application.dto.GetParentTag +import com.bamyanggang.apimodule.domain.tag.application.dto.GetTag import com.bamyanggang.commonmodule.exception.ExceptionHandler import com.bamyanggang.commonmodule.fixture.generateFixture import com.bamyanggang.domainmodule.domain.tag.exception.TagException @@ -209,6 +210,54 @@ class TagControllerTest : BaseRestDocsTest() { ) } + @Test + @DisplayName("상위 태그와 하위 태그 전체를 조회한다.") + fun getAllTagsTest() { + //given + val childTagDetails = arrayListOf( + GetTag.ChildTagDetail(UUID.randomUUID(), "하위 태그 이름 1"), + GetTag.ChildTagDetail(UUID.randomUUID(), "하위 태그 이름 2") + ) + + val parentTagDetails = arrayListOf( + GetTag.ParentTagDetail(UUID.randomUUID(), + "상위, 태그 이름 1", + childTagDetails + ), + GetTag.ParentTagDetail(UUID.randomUUID(), + "상위, 태그 이름 1", + childTagDetails + ), + ) + + val tagResponse = GetTag.Response(parentTagDetails) + + given(tagController.getAllTags()).willReturn(tagResponse) + + val request = RestDocumentationRequestBuilders.get(TagApi.ALL_TAGS) + .header("Authorization", "Bearer Access Token") + + //when + val result = mockMvc.perform(request) + + //then + result.andExpect(status().isOk) + .andDo(resultHandler.document( + requestHeaders( + headerWithName("Authorization").description("엑세스 토큰") + ), + responseFields( + fieldWithPath("tags").description("태그 리스트"), + fieldWithPath("tags[].id").description("상위 태그 id"), + fieldWithPath("tags[].name").description("상위 태그 이름"), + fieldWithPath("tags[].childTags").description("하위 태그 리스트"), + fieldWithPath("tags[].childTags[].id").description("하위 태그 id"), + fieldWithPath("tags[].childTags[].name").description("하위 태그 이름"), + ) + ) + ) + } + @Test @DisplayName("하위 태그를 전체 조회한다.") fun getAllChildTagTest() { diff --git a/Domain-Module/src/main/kotlin/com/bamyanggang/domainmodule/domain/tag/repository/TagRepository.kt b/Domain-Module/src/main/kotlin/com/bamyanggang/domainmodule/domain/tag/repository/TagRepository.kt index 98db685d..7fb536ca 100644 --- a/Domain-Module/src/main/kotlin/com/bamyanggang/domainmodule/domain/tag/repository/TagRepository.kt +++ b/Domain-Module/src/main/kotlin/com/bamyanggang/domainmodule/domain/tag/repository/TagRepository.kt @@ -11,4 +11,5 @@ interface TagRepository { fun deleteByTagId(tagId: UUID) fun isExistById(tagId: UUID): Boolean fun findByParentTagIds(tagParentTagIds: List): List + fun findAllChildTagsByParentTagId(parentTagId: UUID) : List } diff --git a/Domain-Module/src/main/kotlin/com/bamyanggang/domainmodule/domain/tag/service/TagReader.kt b/Domain-Module/src/main/kotlin/com/bamyanggang/domainmodule/domain/tag/service/TagReader.kt index 5fbfe60c..e636aaad 100644 --- a/Domain-Module/src/main/kotlin/com/bamyanggang/domainmodule/domain/tag/service/TagReader.kt +++ b/Domain-Module/src/main/kotlin/com/bamyanggang/domainmodule/domain/tag/service/TagReader.kt @@ -24,4 +24,8 @@ class TagReader( fun readByIds(parentTagIds: List): List { return tagRepository.findByParentTagIds(parentTagIds) } + + fun readChildTagsByParentTagId(parentTagId: UUID) : List { + return tagRepository.findAllChildTagsByParentTagId(parentTagId) + } } diff --git a/Infrastructure-Module/persistence/src/main/java/com/bamyanggang/persistence/tag/TagRepositoryImpl.java b/Infrastructure-Module/persistence/src/main/java/com/bamyanggang/persistence/tag/TagRepositoryImpl.java index 1c967db8..63449998 100644 --- a/Infrastructure-Module/persistence/src/main/java/com/bamyanggang/persistence/tag/TagRepositoryImpl.java +++ b/Infrastructure-Module/persistence/src/main/java/com/bamyanggang/persistence/tag/TagRepositoryImpl.java @@ -3,7 +3,6 @@ import com.bamyanggang.domainmodule.domain.tag.aggregate.Tag; import com.bamyanggang.domainmodule.domain.tag.exception.TagException.NotFoundTag; import com.bamyanggang.domainmodule.domain.tag.repository.TagRepository; -import com.bamyanggang.persistence.common.exception.PersistenceException.NotFound; import com.bamyanggang.persistence.tag.jpa.entity.TagJpaEntity; import com.bamyanggang.persistence.tag.jpa.repository.TagJpaRepository; import com.bamyanggang.persistence.tag.mapper.TagMapper; @@ -57,4 +56,10 @@ public Tag findById(UUID tagId) { TagJpaEntity tagJpaEntity = tagJpaRepository.findById(tagId).orElseThrow(NotFoundTag::new); return tagMapper.toDomainEntity(tagJpaEntity); } + + @Override + public List findAllChildTagsByParentTagId(UUID parentTagId) { + List tagJpaEntities = tagJpaRepository.findAllByParentTagId(parentTagId); + return tagJpaEntities.stream().map(tagMapper::toDomainEntity).toList(); + } } diff --git a/Infrastructure-Module/persistence/src/main/java/com/bamyanggang/persistence/tag/jpa/repository/TagJpaRepository.java b/Infrastructure-Module/persistence/src/main/java/com/bamyanggang/persistence/tag/jpa/repository/TagJpaRepository.java index 885f69da..6936fa9b 100644 --- a/Infrastructure-Module/persistence/src/main/java/com/bamyanggang/persistence/tag/jpa/repository/TagJpaRepository.java +++ b/Infrastructure-Module/persistence/src/main/java/com/bamyanggang/persistence/tag/jpa/repository/TagJpaRepository.java @@ -7,6 +7,6 @@ public interface TagJpaRepository extends JpaRepository { List findAllByUserIdAndParentTagIdIsNull(UUID userId); - - List findAllByUserIdAndParentTagId(UUID parentTagId, UUID parentId); + List findAllByUserIdAndParentTagId(UUID userId, UUID parentId); + List findAllByParentTagId(UUID parentTagId); }