Skip to content

Commit

Permalink
Merge pull request #54 from taco-official/KL-153/태그-응답-형식-변경
Browse files Browse the repository at this point in the history
fix(KL-153): change tag response structure
  • Loading branch information
min3m authored Aug 19, 2024
2 parents cda6b86 + 4379517 commit aec771a
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 106 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package taco.klkl.domain.category.controller;

import java.util.List;
import java.util.Set;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -12,12 +13,12 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import taco.klkl.domain.category.domain.Subcategory;
import taco.klkl.domain.category.dto.response.SubcategoryWithTagsResponse;
import taco.klkl.domain.category.dto.response.TagResponse;
import taco.klkl.domain.category.service.SubcategoryService;
import taco.klkl.domain.category.service.SubcategoryTagService;

@Slf4j
@Tag(name = "7. 서브카테고리", description = "서브카테고리 관련 API")
@Tag(name = "7. 태그", description = "태그 관련 API")
@RestController
@RequestMapping("/v1/tags")
@RequiredArgsConstructor
Expand All @@ -26,10 +27,11 @@ public class TagController {
private final SubcategoryTagService subcategoryTagService;

@GetMapping
@Operation(summary = "선택된 소분류의 태그 목록 조회", description = "Subcategory 포함된 Tag 반환")
public List<SubcategoryWithTagsResponse> findAllTagsBySubcategoryIds(
@RequestParam(value = "subcategory_id") List<Long> subcategoryIds) {
@Operation(summary = "선택된 소분류의 태그 목록 조회", description = "선택된 소분류의 태그 목록을 조회합니다.")
public Set<TagResponse> findAllTagsBySubcategoryIds(
@RequestParam(value = "subcategory_id") final List<Long> subcategoryIds
) {
final List<Subcategory> subcategoryList = subcategoryService.findSubcategoriesBySubcategoryIds(subcategoryIds);
return subcategoryTagService.findSubcategoryTagsBySubcategoryList(subcategoryList);
return subcategoryTagService.findTagsBySubcategoryList(subcategoryList);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package taco.klkl.domain.category.dao;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import taco.klkl.domain.category.domain.Subcategory;
import taco.klkl.domain.category.domain.SubcategoryTag;

@Repository
public interface SubcategoryTagRepository extends JpaRepository<SubcategoryTag, Long> {
List<SubcategoryTag> findAllBySubcategory(final Subcategory subcategory);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package taco.klkl.domain.category.service;

import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Service;

import taco.klkl.domain.category.domain.Subcategory;
import taco.klkl.domain.category.dto.response.SubcategoryWithTagsResponse;
import taco.klkl.domain.category.dto.response.TagResponse;

@Service
public interface SubcategoryTagService {

List<SubcategoryWithTagsResponse> findSubcategoryTagsBySubcategoryList(final List<Subcategory> subcategoryList);
Set<TagResponse> findTagsBySubcategoryList(final List<Subcategory> subcategoryList);

}
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package taco.klkl.domain.category.service;

import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import taco.klkl.domain.category.dao.SubcategoryTagRepository;
import taco.klkl.domain.category.domain.Subcategory;
import taco.klkl.domain.category.dto.response.SubcategoryWithTagsResponse;
import taco.klkl.domain.category.domain.SubcategoryTag;
import taco.klkl.domain.category.dto.response.TagResponse;

@Slf4j
@Primary
Expand All @@ -18,12 +23,17 @@
@RequiredArgsConstructor
public class SubcategoryTagServiceImpl implements SubcategoryTagService {

private final SubcategoryTagRepository subcategoryTagRepository;

@Override
public List<SubcategoryWithTagsResponse> findSubcategoryTagsBySubcategoryList(
public Set<TagResponse> findTagsBySubcategoryList(
final List<Subcategory> subcategoryList
) {
return subcategoryList.stream()
.map(SubcategoryWithTagsResponse::from)
.toList();
.map(subcategoryTagRepository::findAllBySubcategory)
.flatMap(Collection::stream)
.map(SubcategoryTag::getTag)
.map(TagResponse::from)
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -18,14 +23,15 @@
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import taco.klkl.domain.category.dao.SubcategoryTagRepository;
import taco.klkl.domain.category.domain.Category;
import taco.klkl.domain.category.domain.CategoryName;
import taco.klkl.domain.category.domain.Subcategory;
import taco.klkl.domain.category.domain.SubcategoryName;
import taco.klkl.domain.category.domain.SubcategoryTag;
import taco.klkl.domain.category.domain.Tag;
import taco.klkl.domain.category.domain.TagName;
import taco.klkl.domain.category.dto.response.SubcategoryWithTagsResponse;
import taco.klkl.domain.category.dto.response.TagResponse;
import taco.klkl.domain.category.exception.SubcategoryNotFoundException;
import taco.klkl.domain.category.service.SubcategoryService;
import taco.klkl.domain.category.service.SubcategoryTagService;
Expand All @@ -43,11 +49,14 @@ public class TagControllerTest {
@MockBean
private SubcategoryTagService subcategoryTagService;

@MockBean
private SubcategoryTagRepository subcategoryTagRepository;

@Test
@DisplayName("존재하는 Subcategory Id 쿼리가 들어왔을 경우, Subcategory와 Filter가 잘 나오는지 테스트")
public void testGetTagsByidsWithValidQuery() throws Exception {
@DisplayName("존재하는 Subcategory Id 쿼리가 들어왔을 경우, Tag가 잘 나오는지 테스트")
public void testGetTagsByIdsWithValidQuery() throws Exception {
// given
List<Long> ids = Arrays.asList(1L, 2L);
List<Long> subcategoryIds = Arrays.asList(1L, 2L);

Subcategory mockSubcategory1 = mock(Subcategory.class);
Subcategory mockSubcategory2 = mock(Subcategory.class);
Expand All @@ -60,52 +69,50 @@ public void testGetTagsByidsWithValidQuery() throws Exception {

List<Subcategory> mockSubcategoryList = Arrays.asList(mockSubcategory1, mockSubcategory2);

List<SubcategoryTag> mockSubcategory1FilterList = Arrays.asList(subcategoryTag1, subcategoryTag2);
List<SubcategoryTag> mockSubcategory2FilterList = Arrays.asList(subcategoryTag3);

when(mockSubcategory1.getId()).thenReturn(1L);
when(mockSubcategory1.getName()).thenReturn(SubcategoryName.INSTANT_FOOD);
when(mockSubcategory1.getSubcategoryTags()).thenReturn(mockSubcategory1FilterList);
when(mockSubcategory2.getId()).thenReturn(2L);
when(mockSubcategory2.getName()).thenReturn(SubcategoryName.SNACK);
when(mockSubcategory2.getSubcategoryTags()).thenReturn(mockSubcategory2FilterList);

when(mockTag1.getId()).thenReturn(1L);
when(mockTag1.getName()).thenReturn(TagName.CONVENIENCE_STORE);
when(mockTag2.getId()).thenReturn(2L);
when(mockTag2.getName()).thenReturn(TagName.CILANTRO);

List<SubcategoryWithTagsResponse> mockResponse = mockSubcategoryList.stream()
.map(SubcategoryWithTagsResponse::from)
.toList();
when(subcategoryTagRepository.findAllBySubcategory(mockSubcategory1))
.thenReturn(Arrays.asList(subcategoryTag1, subcategoryTag2));
when(subcategoryTagRepository.findAllBySubcategory(mockSubcategory2))
.thenReturn(Collections.singletonList(subcategoryTag3));

when(subcategoryService.findSubcategoriesBySubcategoryIds(ids)).thenReturn(mockSubcategoryList);
when(subcategoryTagService.findSubcategoryTagsBySubcategoryList(anyList())).thenReturn(mockResponse);
Set<TagResponse> expectedResponse = new HashSet<>(Arrays.asList(
TagResponse.from(mockTag1),
TagResponse.from(mockTag2)
));

when(subcategoryService.findSubcategoriesBySubcategoryIds(subcategoryIds)).thenReturn(mockSubcategoryList);
when(subcategoryTagService.findTagsBySubcategoryList(mockSubcategoryList)).thenReturn(expectedResponse);

// when & then
mockMvc.perform(get("/v1/tags")
.param("subcategory_id", "1,2")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.isSuccess", is(true)))
.andExpect(jsonPath("$.data[0].id", is(1)))
.andExpect(jsonPath("$.data[0].name", is(SubcategoryName.INSTANT_FOOD.getKoreanName())))
.andExpect(jsonPath("$.data[0].tags[0].id", is(1)))
.andExpect(jsonPath("$.data[0].tags[0].name", is(TagName.CONVENIENCE_STORE.getKoreanName())))
.andExpect(jsonPath("$.data[0].tags[1].id", is(2)))
.andExpect(jsonPath("$.data[0].tags[1].name", is(TagName.CILANTRO.getKoreanName())))
.andExpect(jsonPath("$.data[1].id", is(2)))
.andExpect(jsonPath("$.data[1].name", is(SubcategoryName.SNACK.getKoreanName())))
.andExpect(jsonPath("$.data[1].tags[0].id", is(1)))
.andExpect(jsonPath("$.data[1].tags[0].name", is(TagName.CONVENIENCE_STORE.getKoreanName())))
.andExpect(jsonPath("$.data", hasSize(2)))
.andExpect(jsonPath("$.data[*].id", containsInAnyOrder(1, 2)))
.andExpect(jsonPath("$.data[*].name", containsInAnyOrder(
TagName.CONVENIENCE_STORE.getKoreanName(),
TagName.CILANTRO.getKoreanName()
)))
.andExpect(jsonPath("$.timestamp", notNullValue()));

verify(subcategoryService, times(1)).findSubcategoriesBySubcategoryIds(ids);
verify(subcategoryService, times(1)).findSubcategoriesBySubcategoryIds(subcategoryIds);
verify(subcategoryTagService, times(1)).findTagsBySubcategoryList(mockSubcategoryList);
}

@Test
@DisplayName("존재하는 Subcategory Id 쿼리가 들어왔지만 Filter가 없는 Subcategory일 경우, Subcategory와 Filter가 잘 나오는지 테스트")
public void testGetTagsByidsWithValidQueryButEmptyOne() throws Exception {
@DisplayName("존재하는 Subcategory Id 쿼리가 들어왔지만 Tag가 없는 Subcategory일 경우, Tag가 잘 나오는지 테스트")
public void testGetTagsByIdsWithValidQueryButEmptyOne() throws Exception {
// given
List<Long> ids = Arrays.asList(1L, 2L);

Expand All @@ -119,50 +126,50 @@ public void testGetTagsByidsWithValidQueryButEmptyOne() throws Exception {

List<Subcategory> mockSubcategoryList = Arrays.asList(mockSubcategory1, mockSubcategory2);

List<SubcategoryTag> mockSubcategory1FilterList = Arrays.asList(subcategoryTag1, subcategoryTag2);

when(mockSubcategory1.getId()).thenReturn(1L);
when(mockSubcategory1.getName()).thenReturn(SubcategoryName.INSTANT_FOOD);
when(mockSubcategory1.getSubcategoryTags()).thenReturn(mockSubcategory1FilterList);
when(mockSubcategory2.getId()).thenReturn(2L);
when(mockSubcategory2.getName()).thenReturn(SubcategoryName.SNACK);
when(mockSubcategory2.getSubcategoryTags()).thenReturn(Collections.emptyList());

when(mockTag1.getId()).thenReturn(1L);
when(mockTag1.getName()).thenReturn(TagName.CONVENIENCE_STORE);
when(mockTag2.getId()).thenReturn(2L);
when(mockTag2.getName()).thenReturn(TagName.CILANTRO);

List<SubcategoryWithTagsResponse> mockResponse = mockSubcategoryList.stream()
.map(SubcategoryWithTagsResponse::from)
.toList();
when(subcategoryTagRepository.findAllBySubcategory(mockSubcategory1))
.thenReturn(Arrays.asList(subcategoryTag1, subcategoryTag2));
when(subcategoryTagRepository.findAllBySubcategory(mockSubcategory2))
.thenReturn(Collections.emptyList());

Set<TagResponse> expectedResponse = new HashSet<>(Arrays.asList(
TagResponse.from(mockTag1),
TagResponse.from(mockTag2)
));

when(subcategoryService.findSubcategoriesBySubcategoryIds(ids)).thenReturn(mockSubcategoryList);
when(subcategoryTagService.findSubcategoryTagsBySubcategoryList(anyList())).thenReturn(mockResponse);
when(subcategoryTagService.findTagsBySubcategoryList(mockSubcategoryList)).thenReturn(expectedResponse);

// when & then
mockMvc.perform(get("/v1/tags")
.param("subcategory_id", "1,2")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.isSuccess", is(true)))
.andExpect(jsonPath("$.data[0].id", is(1)))
.andExpect(jsonPath("$.data[0].name", is(SubcategoryName.INSTANT_FOOD.getKoreanName())))
.andExpect(jsonPath("$.data[0].tags[0].id", is(1)))
.andExpect(jsonPath("$.data[0].tags[0].name", is(TagName.CONVENIENCE_STORE.getKoreanName())))
.andExpect(jsonPath("$.data[0].tags[1].id", is(2)))
.andExpect(jsonPath("$.data[0].tags[1].name", is(TagName.CILANTRO.getKoreanName())))
.andExpect(jsonPath("$.data[1].id", is(2)))
.andExpect(jsonPath("$.data[1].name", is(SubcategoryName.SNACK.getKoreanName())))
.andExpect(jsonPath("$.data[1].tags.size()", is(0)))
.andExpect(jsonPath("$.data", hasSize(2)))
.andExpect(jsonPath("$.data[*].id", containsInAnyOrder(1, 2)))
.andExpect(jsonPath("$.data[*].name", containsInAnyOrder(
TagName.CONVENIENCE_STORE.getKoreanName(),
TagName.CILANTRO.getKoreanName()
)))
.andExpect(jsonPath("$.timestamp", notNullValue()));

verify(subcategoryService, times(1)).findSubcategoriesBySubcategoryIds(ids);
verify(subcategoryTagService, times(1)).findTagsBySubcategoryList(mockSubcategoryList);
}

@Test
@DisplayName("존재하지 않는 Subcategory Id 쿼리가 들어올 경우, SubcategoryNotFound Error Response를 반환하는지 테스트")
public void testGetTagsByidsWithValidQueryButNotExist() throws Exception {
public void testGetTagsByIdsWithValidQueryButNotExist() throws Exception {
//given
when(subcategoryService.findSubcategoriesBySubcategoryIds(anyList()))
.thenThrow(new SubcategoryNotFoundException());
Expand All @@ -179,7 +186,7 @@ public void testGetTagsByidsWithValidQueryButNotExist() throws Exception {

@Test
@DisplayName("올바르지 않은 쿼리가 들어올 경우, INVALID_QUERY_FORMAT을 반환하는지 테스트")
public void testGetTagsByidsWithInvalidQueryFormat() throws Exception {
public void testGetTagsByIdsWithInvalidQueryFormat() throws Exception {
//given
when(subcategoryService.findSubcategoriesBySubcategoryIds(anyList()))
.thenThrow(MethodArgumentTypeMismatchException.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.util.Arrays;
import java.util.List;
import java.util.Set;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand All @@ -17,7 +18,7 @@
import org.springframework.transaction.annotation.Transactional;

import taco.klkl.domain.category.domain.Subcategory;
import taco.klkl.domain.category.dto.response.SubcategoryWithTagsResponse;
import taco.klkl.domain.category.dto.response.TagResponse;
import taco.klkl.domain.category.service.SubcategoryService;
import taco.klkl.domain.category.service.SubcategoryTagService;
import taco.klkl.global.error.exception.ErrorCode;
Expand Down Expand Up @@ -48,8 +49,8 @@ public void testGetFilterApiWithValidQuery() throws Exception {
.map(Long::parseLong)
.toList()
);
List<SubcategoryWithTagsResponse> response =
subcategoryTagService.findSubcategoryTagsBySubcategoryList(subcategoryList);
Set<TagResponse> response =
subcategoryTagService.findTagsBySubcategoryList(subcategoryList);

//then
mockMvc.perform(get("/v1/tags")
Expand All @@ -73,8 +74,8 @@ public void testGetFilterApiWithValidQueryButEmptyOne() throws Exception {
.map(Long::parseLong)
.toList()
);
List<SubcategoryWithTagsResponse> response =
subcategoryTagService.findSubcategoryTagsBySubcategoryList(subcategoryList);
Set<TagResponse> response =
subcategoryTagService.findTagsBySubcategoryList(subcategoryList);

//then
mockMvc.perform(get("/v1/tags")
Expand Down
Loading

0 comments on commit aec771a

Please sign in to comment.