From f1445b9a77ad4b250374551023068287ff1dca65 Mon Sep 17 00:00:00 2001 From: seojin Yoon <90759319+7zrv@users.noreply.github.com> Date: Mon, 2 Dec 2024 10:49:24 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=84=A0=ED=98=B8=EB=AC=BC?= =?UTF-8?q?=ED=92=88=20=EB=93=B1=EB=A1=9D=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 컨트롤러 구현 - 기관 권한 설정에 따른 요청 Dto 수정 - 요청 Dto 변화에 따른 서비스 레이어 파라미터 값 수정 - 테스트 코드 작성및 검증 완 --- .../PreferItemCommandApiController.java | 38 +++++++++ .../request/PreferItemCreateRequestDto.java | 12 ++- .../response/PreferItemCreateResponseDto.java | 25 ++++++ .../command/CreatePreferItemService.java | 14 +++- .../command/CreatePreferItemUseCase.java | 5 +- .../PreferItemCommandApiControllerTest.java | 83 +++++++++++++++++++ .../command/CreatePreferItemServiceTest.java | 51 +++++++++--- 7 files changed, 208 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/somemore/center/controller/PreferItemCommandApiController.java create mode 100644 src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java create mode 100644 src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java diff --git a/src/main/java/com/somemore/center/controller/PreferItemCommandApiController.java b/src/main/java/com/somemore/center/controller/PreferItemCommandApiController.java new file mode 100644 index 000000000..cde5cfc10 --- /dev/null +++ b/src/main/java/com/somemore/center/controller/PreferItemCommandApiController.java @@ -0,0 +1,38 @@ +package com.somemore.center.controller; + +import com.somemore.center.dto.request.PreferItemCreateRequestDto; +import com.somemore.center.dto.response.PreferItemCreateResponseDto; +import com.somemore.center.usecase.command.CreatePreferItemUseCase; +import com.somemore.global.common.response.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.UUID; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/preferItem") +@Tag(name = "PreferItem Command API", description = "선호 물품 등록, 삭제 기능을 제공합니다") +public class PreferItemCommandApiController { + + private final CreatePreferItemUseCase createPreferItemUseCase; + + @Secured("ROLE_CENTER") + @Operation(summary = "기관 선호물품 등록 API") + @PostMapping() + public ApiResponse registerPreferItem(@Valid @RequestBody PreferItemCreateRequestDto requestDto, + @AuthenticationPrincipal String userId) { + + PreferItemCreateResponseDto responseDto = createPreferItemUseCase.createPreferItem(UUID.fromString(userId), requestDto); + + return ApiResponse.ok(200, responseDto, "관심 기관 등록 성공"); + } +} diff --git a/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java b/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java index bda87dae5..29a266231 100644 --- a/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java +++ b/src/main/java/com/somemore/center/dto/request/PreferItemCreateRequestDto.java @@ -1,18 +1,22 @@ package com.somemore.center.dto.request; - +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.somemore.center.domain.PreferItem; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import java.util.UUID; - +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public record PreferItemCreateRequestDto( - UUID centerId, + @Schema(description = "선호물품 이름", example = "어린이 도서") + @NotNull(message = "물품 이름은 필수값입니다.") String itemName ) { - public PreferItem createPreferItem() { + public PreferItem toEntity(UUID centerId) { return PreferItem.create(centerId, itemName); } diff --git a/src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java b/src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java new file mode 100644 index 000000000..a7db84040 --- /dev/null +++ b/src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java @@ -0,0 +1,25 @@ +package com.somemore.center.dto.response; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import com.somemore.center.domain.PreferItem; +import lombok.Builder; + +import java.util.UUID; + +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +@Builder +public record PreferItemCreateResponseDto( + Long id, + UUID centerId, + String itemName +) { + public static PreferItemCreateResponseDto from(PreferItem preferItem) { + return PreferItemCreateResponseDto.builder() + .id(preferItem.getId()) + .centerId(preferItem.getCenterId()) + .itemName(preferItem.getItemName()) + .build(); + } +} + diff --git a/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java b/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java index 5a22f8679..d9565837c 100644 --- a/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java +++ b/src/main/java/com/somemore/center/service/command/CreatePreferItemService.java @@ -1,6 +1,8 @@ package com.somemore.center.service.command; +import com.somemore.center.domain.PreferItem; import com.somemore.center.dto.request.PreferItemCreateRequestDto; +import com.somemore.center.dto.response.PreferItemCreateResponseDto; import com.somemore.center.repository.PreferItemRepository; import com.somemore.center.usecase.command.CreatePreferItemUseCase; import com.somemore.center.usecase.query.CenterQueryUseCase; @@ -8,6 +10,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.UUID; + @RequiredArgsConstructor @Transactional @Service @@ -17,11 +21,15 @@ public class CreatePreferItemService implements CreatePreferItemUseCase { private final PreferItemRepository preferItemRepository; @Override - public void createPreferItem(PreferItemCreateRequestDto requestDto) { + public PreferItemCreateResponseDto createPreferItem(UUID userId, PreferItemCreateRequestDto requestDto) { + + centerQueryUseCase.validateCenterExists(userId); + + PreferItem preferItem = requestDto.toEntity(userId); - centerQueryUseCase.validateCenterExists(requestDto.centerId()); + preferItemRepository.save(preferItem); - preferItemRepository.save(requestDto.createPreferItem()); + return PreferItemCreateResponseDto.from(preferItem); } } diff --git a/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java b/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java index 78c793edc..f500a6248 100644 --- a/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java +++ b/src/main/java/com/somemore/center/usecase/command/CreatePreferItemUseCase.java @@ -1,8 +1,11 @@ package com.somemore.center.usecase.command; import com.somemore.center.dto.request.PreferItemCreateRequestDto; +import com.somemore.center.dto.response.PreferItemCreateResponseDto; + +import java.util.UUID; public interface CreatePreferItemUseCase { - void createPreferItem(PreferItemCreateRequestDto requestDto); + PreferItemCreateResponseDto createPreferItem(UUID userId, PreferItemCreateRequestDto requestDto); } diff --git a/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java b/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java new file mode 100644 index 000000000..101c30426 --- /dev/null +++ b/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java @@ -0,0 +1,83 @@ +package com.somemore.center.controller; + +import com.somemore.ControllerTestSupport; +import com.somemore.WithMockCustomUser; +import com.somemore.center.dto.request.PreferItemCreateRequestDto; +import com.somemore.center.dto.response.PreferItemCreateResponseDto; +import com.somemore.center.usecase.command.CreatePreferItemUseCase; +import com.somemore.global.exception.BadRequestException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; + +import java.util.UUID; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +class PreferItemCommandApiControllerTest extends ControllerTestSupport { + + @MockBean + private CreatePreferItemUseCase createPreferItemUseCase; + + + @DisplayName("기관은 선호물품을 등록할 수 있다. (controller)") + @Test + @WithMockCustomUser(role = "CENTER") + void registerPreferItem() throws Exception { + // given + UUID userId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); // 고정된 UUID 사용 + PreferItemCreateRequestDto requestDto = new PreferItemCreateRequestDto("어린이 도서"); + PreferItemCreateResponseDto responseDto = new PreferItemCreateResponseDto( + 1L, + userId, + "어린이 도서" + ); + + given(createPreferItemUseCase.createPreferItem(userId, requestDto)) + .willReturn(responseDto); + + // when & then + mockMvc.perform(post("/api/preferItem") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + .principal(() -> userId.toString())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.code").value(200)) + .andExpect(jsonPath("$.message").value("관심 기관 등록 성공")) + .andExpect(jsonPath("$.data.id").value(responseDto.id())) + .andExpect(jsonPath("$.data.center_id").value(responseDto.centerId().toString())) + .andExpect(jsonPath("$.data.item_name").value(responseDto.itemName())); + + // verify + verify(createPreferItemUseCase).createPreferItem(userId, requestDto); + } + + @DisplayName("존재하지 않는 기관 ID로 선호물품 등록 시 실패한다. (controller)") + @Test + @WithMockCustomUser(role = "CENTER") + void registerPreferItem_Fail_WhenCenterNotExists() throws Exception { + // given + UUID userId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000"); + PreferItemCreateRequestDto requestDto = new PreferItemCreateRequestDto("어린이 도서"); + + given(createPreferItemUseCase.createPreferItem(userId, requestDto)) + .willThrow(new BadRequestException("존재하지 않는 기관입니다.")); + + // when & then + mockMvc.perform(post("/api/preferItem") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(requestDto)) + .principal(() -> userId.toString())) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.status").value(400)) + .andExpect(jsonPath("$.detail").value("존재하지 않는 기관입니다.")); + + // verify + verify(createPreferItemUseCase).createPreferItem(userId, requestDto); + } +} \ No newline at end of file diff --git a/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java b/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java index a7db8c925..ca56042d5 100644 --- a/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java +++ b/src/test/java/com/somemore/center/service/command/CreatePreferItemServiceTest.java @@ -6,12 +6,16 @@ import com.somemore.center.dto.request.PreferItemCreateRequestDto; import com.somemore.center.repository.CenterRepository; import com.somemore.center.repository.PreferItemRepository; +import com.somemore.global.exception.BadRequestException; import jakarta.transaction.Transactional; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.util.UUID; + import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; @Transactional class CreatePreferItemServiceTest extends IntegrationTestSupport { @@ -29,26 +33,16 @@ class CreatePreferItemServiceTest extends IntegrationTestSupport { @Test void createPreferItem() { //given - Center center = Center.create( - "기본 기관 이름", - "010-1234-5678", - "http://example.com/image.jpg", - "기관 소개 내용", - "http://example.com", - "account123", - "password123" - ); + Center center = createCenter(); centerRepository.save(center); String itemName = "어린이 도서"; - PreferItemCreateRequestDto requestDto = new PreferItemCreateRequestDto( - center.getId(), itemName ); //when - createPreferItemService.createPreferItem(requestDto); + createPreferItemService.createPreferItem(center.getId(), requestDto); //then PreferItem savedItem = preferItemRepository.findAll().stream() @@ -61,4 +55,37 @@ void createPreferItem() { assertThat(savedItem.getItemName()).isEqualTo(itemName); } + @DisplayName("존재하지 않는 기관 아이디로 선호물품을 등록하려고 하면 예외를 발생시킨다.") + @Test + void createPreferItemThrowsExceptionWhenCenterDoesNotExist() { + // given + UUID invalidCenterId = UUID.randomUUID(); + String itemName = "어린이 도서"; + + PreferItemCreateRequestDto requestDto = new PreferItemCreateRequestDto( + itemName + ); + + // when & then + assertThatThrownBy(() -> createPreferItemService.createPreferItem(invalidCenterId, requestDto)) + .isInstanceOf(BadRequestException.class) + .hasMessageContaining("존재하지 않는 기관입니다."); + } + + private Center createCenter() { + Center center = Center.create( + "기본 기관 이름", + "010-1234-5678", + "http://example.com/image.jpg", + "기관 소개 내용", + "http://example.com", + "account123", + "password123" + ); + + centerRepository.save(center); + + return center; + } + } From 38d24ecd2a26841263bc8269853702a8d8c4b6be Mon Sep 17 00:00:00 2001 From: seojin Yoon <90759319+7zrv@users.noreply.github.com> Date: Mon, 2 Dec 2024 10:51:40 +0900 Subject: [PATCH 2/3] =?UTF-8?q?chore:=20test=20displayname=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 도메인 관점의 설명으로 변경 --- .../center/controller/PreferItemCommandApiControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java b/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java index 101c30426..1249eb907 100644 --- a/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java +++ b/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java @@ -57,7 +57,7 @@ void registerPreferItem() throws Exception { verify(createPreferItemUseCase).createPreferItem(userId, requestDto); } - @DisplayName("존재하지 않는 기관 ID로 선호물품 등록 시 실패한다. (controller)") + @DisplayName("존재하지 않는 기관 ID로 선호물품을 등록할 수 없다. (controller)") @Test @WithMockCustomUser(role = "CENTER") void registerPreferItem_Fail_WhenCenterNotExists() throws Exception { From a2d0845de332e70d55ded7fa72b791f35140e517 Mon Sep 17 00:00:00 2001 From: seojin Yoon <90759319+7zrv@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:22:31 +0900 Subject: [PATCH 3/3] =?UTF-8?q?chore:=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 응답 객체에 swagger 스키마 적용 - 개행 추 --- .../center/dto/response/PreferItemCreateResponseDto.java | 4 ++++ .../center/controller/PreferItemCommandApiControllerTest.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java b/src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java index a7db84040..faa0a8f20 100644 --- a/src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java +++ b/src/main/java/com/somemore/center/dto/response/PreferItemCreateResponseDto.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.somemore.center.domain.PreferItem; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import java.util.UUID; @@ -10,8 +11,11 @@ @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) @Builder public record PreferItemCreateResponseDto( + @Schema(description = "선호물품의 ID", example = "111") Long id, + @Schema(description = "기관의 ID", example = "123e4567-e89b-12d3-a456-426614174000") UUID centerId, + @Schema(description = "선호물품 이름", example = "어린이 도서") String itemName ) { public static PreferItemCreateResponseDto from(PreferItem preferItem) { diff --git a/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java b/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java index 1249eb907..eac612b02 100644 --- a/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java +++ b/src/test/java/com/somemore/center/controller/PreferItemCommandApiControllerTest.java @@ -80,4 +80,4 @@ void registerPreferItem_Fail_WhenCenterNotExists() throws Exception { // verify verify(createPreferItemUseCase).createPreferItem(userId, requestDto); } -} \ No newline at end of file +}