generated from pricelees/issue-pr-template
[#20] 도메인별 예외 분리 #21
@ -6,7 +6,7 @@ import roomescape.member.web.MemberRetrieveResponse
|
|||||||
import roomescape.member.web.toRetrieveResponse
|
import roomescape.member.web.toRetrieveResponse
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||||
import roomescape.theme.web.ThemeResponse
|
import roomescape.theme.web.ThemeRetrieveResponse
|
||||||
import roomescape.theme.web.toResponse
|
import roomescape.theme.web.toResponse
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
@ -41,7 +41,7 @@ data class ReservationRetrieveResponse(
|
|||||||
val time: TimeCreateResponse,
|
val time: TimeCreateResponse,
|
||||||
|
|
||||||
@field:JsonProperty("theme")
|
@field:JsonProperty("theme")
|
||||||
val theme: ThemeResponse,
|
val theme: ThemeRetrieveResponse,
|
||||||
|
|
||||||
val status: ReservationStatus
|
val status: ReservationStatus
|
||||||
)
|
)
|
||||||
|
|||||||
@ -27,11 +27,11 @@ class ThemeService(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
fun findThemes(): ThemesResponse = themeRepository.findAll()
|
fun findThemes(): ThemeRetrieveListResponse = themeRepository.findAll()
|
||||||
.toResponse()
|
.toResponse()
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
fun findMostReservedThemes(count: Int): ThemesResponse {
|
fun findMostReservedThemes(count: Int): ThemeRetrieveListResponse {
|
||||||
val today = LocalDate.now()
|
val today = LocalDate.now()
|
||||||
val startDate = today.minusDays(7)
|
val startDate = today.minusDays(7)
|
||||||
val endDate = today.minusDays(1)
|
val endDate = today.minusDays(1)
|
||||||
@ -41,7 +41,7 @@ class ThemeService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
fun createTheme(request: ThemeRequest): ThemeResponse {
|
fun createTheme(request: ThemeCreateRequest): ThemeRetrieveResponse {
|
||||||
if (themeRepository.existsByName(request.name)) {
|
if (themeRepository.existsByName(request.name)) {
|
||||||
throw RoomescapeException(
|
throw RoomescapeException(
|
||||||
ErrorType.THEME_DUPLICATED,
|
ErrorType.THEME_DUPLICATED,
|
||||||
|
|||||||
@ -13,9 +13,9 @@ import org.springframework.web.bind.annotation.RequestParam
|
|||||||
import roomescape.auth.web.support.Admin
|
import roomescape.auth.web.support.Admin
|
||||||
import roomescape.auth.web.support.LoginRequired
|
import roomescape.auth.web.support.LoginRequired
|
||||||
import roomescape.common.dto.response.CommonApiResponse
|
import roomescape.common.dto.response.CommonApiResponse
|
||||||
import roomescape.theme.web.ThemeRequest
|
import roomescape.theme.web.ThemeCreateRequest
|
||||||
import roomescape.theme.web.ThemeResponse
|
import roomescape.theme.web.ThemeRetrieveResponse
|
||||||
import roomescape.theme.web.ThemesResponse
|
import roomescape.theme.web.ThemeRetrieveListResponse
|
||||||
|
|
||||||
@Tag(name = "5. 테마 API", description = "테마를 조회 / 추가 / 삭제할 때 사용합니다.")
|
@Tag(name = "5. 테마 API", description = "테마를 조회 / 추가 / 삭제할 때 사용합니다.")
|
||||||
interface ThemeAPI {
|
interface ThemeAPI {
|
||||||
@ -23,13 +23,13 @@ interface ThemeAPI {
|
|||||||
@LoginRequired
|
@LoginRequired
|
||||||
@Operation(summary = "모든 테마 조회", description = "모든 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
|
@Operation(summary = "모든 테마 조회", description = "모든 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
|
||||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||||
fun findThemes(): ResponseEntity<CommonApiResponse<ThemesResponse>>
|
fun findThemes(): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>>
|
||||||
|
|
||||||
@Operation(summary = "가장 많이 예약된 테마 조회")
|
@Operation(summary = "가장 많이 예약된 테마 조회")
|
||||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||||
fun findMostReservedThemes(
|
fun findMostReservedThemes(
|
||||||
@RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") count: Int
|
@RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") count: Int
|
||||||
): ResponseEntity<CommonApiResponse<ThemesResponse>>
|
): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>>
|
||||||
|
|
||||||
@Admin
|
@Admin
|
||||||
@Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"])
|
@Operation(summary = "테마 추가", tags = ["관리자 로그인이 필요한 API"])
|
||||||
@ -37,8 +37,8 @@ interface ThemeAPI {
|
|||||||
ApiResponse(responseCode = "201", description = "성공", useReturnTypeSchema = true),
|
ApiResponse(responseCode = "201", description = "성공", useReturnTypeSchema = true),
|
||||||
)
|
)
|
||||||
fun createTheme(
|
fun createTheme(
|
||||||
@Valid @RequestBody request: ThemeRequest,
|
@Valid @RequestBody request: ThemeCreateRequest,
|
||||||
): ResponseEntity<CommonApiResponse<ThemeResponse>>
|
): ResponseEntity<CommonApiResponse<ThemeRetrieveResponse>>
|
||||||
|
|
||||||
@Admin
|
@Admin
|
||||||
@Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"])
|
@Operation(summary = "테마 삭제", tags = ["관리자 로그인이 필요한 API"])
|
||||||
|
|||||||
@ -15,8 +15,8 @@ class ThemeController(
|
|||||||
) : ThemeAPI {
|
) : ThemeAPI {
|
||||||
|
|
||||||
@GetMapping("/themes")
|
@GetMapping("/themes")
|
||||||
override fun findThemes(): ResponseEntity<CommonApiResponse<ThemesResponse>> {
|
override fun findThemes(): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>> {
|
||||||
val response: ThemesResponse = themeService.findThemes()
|
val response: ThemeRetrieveListResponse = themeService.findThemes()
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
}
|
}
|
||||||
@ -24,17 +24,17 @@ class ThemeController(
|
|||||||
@GetMapping("/themes/most-reserved-last-week")
|
@GetMapping("/themes/most-reserved-last-week")
|
||||||
override fun findMostReservedThemes(
|
override fun findMostReservedThemes(
|
||||||
@RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") count: Int
|
@RequestParam(defaultValue = "10") @Parameter(description = "최대로 조회할 테마 갯수") count: Int
|
||||||
): ResponseEntity<CommonApiResponse<ThemesResponse>> {
|
): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>> {
|
||||||
val response: ThemesResponse = themeService.findMostReservedThemes(count)
|
val response: ThemeRetrieveListResponse = themeService.findMostReservedThemes(count)
|
||||||
|
|
||||||
return ResponseEntity.ok(CommonApiResponse(response))
|
return ResponseEntity.ok(CommonApiResponse(response))
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/themes")
|
@PostMapping("/themes")
|
||||||
override fun createTheme(
|
override fun createTheme(
|
||||||
@RequestBody @Valid request: ThemeRequest
|
@RequestBody @Valid request: ThemeCreateRequest
|
||||||
): ResponseEntity<CommonApiResponse<ThemeResponse>> {
|
): ResponseEntity<CommonApiResponse<ThemeRetrieveResponse>> {
|
||||||
val themeResponse: ThemeResponse = themeService.createTheme(request)
|
val themeResponse: ThemeRetrieveResponse = themeService.createTheme(request)
|
||||||
|
|
||||||
return ResponseEntity.created(URI.create("/themes/${themeResponse.id}"))
|
return ResponseEntity.created(URI.create("/themes/${themeResponse.id}"))
|
||||||
.body(CommonApiResponse(themeResponse))
|
.body(CommonApiResponse(themeResponse))
|
||||||
|
|||||||
@ -1,57 +1,48 @@
|
|||||||
package roomescape.theme.web
|
package roomescape.theme.web
|
||||||
|
|
||||||
import io.swagger.v3.oas.annotations.media.Schema
|
|
||||||
import jakarta.validation.constraints.NotBlank
|
import jakarta.validation.constraints.NotBlank
|
||||||
import jakarta.validation.constraints.Size
|
import jakarta.validation.constraints.Size
|
||||||
import org.hibernate.validator.constraints.URL
|
import org.hibernate.validator.constraints.URL
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||||
|
|
||||||
@Schema(name = "테마 저장 요청", description = "테마 정보를 저장할 때 사용합니다.")
|
data class ThemeCreateRequest(
|
||||||
data class ThemeRequest(
|
|
||||||
@Schema(description = "필수 값이며, 최대 20글자까지 입력 가능합니다.")
|
|
||||||
@NotBlank
|
@NotBlank
|
||||||
@Size(max = 20, message = "테마의 이름은 1~20글자 사이여야 합니다.")
|
@Size(max = 20)
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|
||||||
@Schema(description = "필수 값이며, 최대 100글자까지 입력 가능합니다.")
|
|
||||||
@NotBlank
|
@NotBlank
|
||||||
@Size(max = 100, message = "테마의 설명은 1~100글자 사이여야 합니다.")
|
@Size(max = 100)
|
||||||
val description: String,
|
val description: String,
|
||||||
|
|
||||||
@Schema(description = "필수 값이며, 썸네일 이미지 URL 을 입력해주세요.")
|
|
||||||
@NotBlank
|
|
||||||
@URL
|
@URL
|
||||||
|
@NotBlank
|
||||||
val thumbnail: String
|
val thumbnail: String
|
||||||
)
|
)
|
||||||
|
|
||||||
@Schema(name = "테마 정보", description = "테마 추가 및 조회 응답에 사용됩니다.")
|
fun ThemeCreateRequest.toEntity(): ThemeEntity = ThemeEntity(
|
||||||
data class ThemeResponse(
|
name = this.name,
|
||||||
@Schema(description = "테마 번호. 테마를 식별할 때 사용합니다.")
|
description = this.description,
|
||||||
|
thumbnail = this.thumbnail
|
||||||
|
)
|
||||||
|
|
||||||
|
data class ThemeRetrieveResponse(
|
||||||
val id: Long,
|
val id: Long,
|
||||||
|
|
||||||
@Schema(description = "테마 이름. 중복을 허용하지 않습니다.")
|
|
||||||
val name: String,
|
val name: String,
|
||||||
|
|
||||||
@Schema(description = "테마 설명")
|
|
||||||
val description: String,
|
val description: String,
|
||||||
|
|
||||||
@Schema(description = "테마 썸네일 이미지 URL")
|
|
||||||
val thumbnail: String
|
val thumbnail: String
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ThemeEntity.toResponse(): ThemeResponse = ThemeResponse(
|
fun ThemeEntity.toResponse(): ThemeRetrieveResponse = ThemeRetrieveResponse(
|
||||||
id = this.id!!,
|
id = this.id!!,
|
||||||
name = this.name,
|
name = this.name,
|
||||||
description = this.description,
|
description = this.description,
|
||||||
thumbnail = this.thumbnail
|
thumbnail = this.thumbnail
|
||||||
)
|
)
|
||||||
|
|
||||||
@Schema(name = "테마 목록 조회 응답", description = "모든 테마 목록 조회 응답시 사용됩니다.")
|
data class ThemeRetrieveListResponse(
|
||||||
data class ThemesResponse(
|
val themes: List<ThemeRetrieveResponse>
|
||||||
@Schema(description = "모든 테마 목록")
|
|
||||||
val themes: List<ThemeResponse>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun List<ThemeEntity>.toResponse(): ThemesResponse = ThemesResponse(
|
fun List<ThemeEntity>.toResponse(): ThemeRetrieveListResponse = ThemeRetrieveListResponse(
|
||||||
themes = this.map { it.toResponse() }
|
themes = this.map { it.toResponse() }
|
||||||
)
|
)
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import roomescape.common.exception.ErrorType
|
|||||||
import roomescape.common.exception.RoomescapeException
|
import roomescape.common.exception.RoomescapeException
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||||
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
||||||
import roomescape.theme.web.ThemeRequest
|
import roomescape.theme.web.ThemeCreateRequest
|
||||||
import roomescape.util.ThemeFixture
|
import roomescape.util.ThemeFixture
|
||||||
|
|
||||||
class ThemeServiceTest : FunSpec({
|
class ThemeServiceTest : FunSpec({
|
||||||
@ -68,7 +68,7 @@ class ThemeServiceTest : FunSpec({
|
|||||||
} returns true
|
} returns true
|
||||||
|
|
||||||
val exception = shouldThrow<RoomescapeException> {
|
val exception = shouldThrow<RoomescapeException> {
|
||||||
themeService.createTheme(ThemeRequest(
|
themeService.createTheme(ThemeCreateRequest(
|
||||||
name = name,
|
name = name,
|
||||||
description = "Description",
|
description = "Description",
|
||||||
thumbnail = "http://example.com/thumbnail.jpg"
|
thumbnail = "http://example.com/thumbnail.jpg"
|
||||||
|
|||||||
@ -58,7 +58,7 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
|
|||||||
ThemeFixture.create(id = 3, name = "theme3")
|
ThemeFixture.create(id = 3, name = "theme3")
|
||||||
)
|
)
|
||||||
|
|
||||||
val response: ThemesResponse = runGetTest(
|
val response: ThemeRetrieveListResponse = runGetTest(
|
||||||
mockMvc = mockMvc,
|
mockMvc = mockMvc,
|
||||||
endpoint = endpoint,
|
endpoint = endpoint,
|
||||||
) {
|
) {
|
||||||
@ -66,7 +66,7 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
|
|||||||
content {
|
content {
|
||||||
contentType(MediaType.APPLICATION_JSON)
|
contentType(MediaType.APPLICATION_JSON)
|
||||||
}
|
}
|
||||||
}.andReturn().readValue(ThemesResponse::class.java)
|
}.andReturn().readValue(ThemeRetrieveListResponse::class.java)
|
||||||
|
|
||||||
assertSoftly(response.themes) {
|
assertSoftly(response.themes) {
|
||||||
it.size shouldBe 3
|
it.size shouldBe 3
|
||||||
@ -78,7 +78,7 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
|
|||||||
|
|
||||||
Given("테마를 추가할 때") {
|
Given("테마를 추가할 때") {
|
||||||
val endpoint = "/themes"
|
val endpoint = "/themes"
|
||||||
val request = ThemeRequest(
|
val request = ThemeCreateRequest(
|
||||||
name = "theme1",
|
name = "theme1",
|
||||||
description = "description1",
|
description = "description1",
|
||||||
thumbnail = "http://example.com/thumbnail1.jpg"
|
thumbnail = "http://example.com/thumbnail1.jpg"
|
||||||
@ -138,13 +138,13 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
|
|||||||
loginAsAdmin()
|
loginAsAdmin()
|
||||||
}
|
}
|
||||||
|
|
||||||
val request = ThemeRequest(
|
val request = ThemeCreateRequest(
|
||||||
name = "theme1",
|
name = "theme1",
|
||||||
description = "description1",
|
description = "description1",
|
||||||
thumbnail = "http://example.com/thumbnail1.jpg"
|
thumbnail = "http://example.com/thumbnail1.jpg"
|
||||||
)
|
)
|
||||||
|
|
||||||
fun runTest(request: ThemeRequest) {
|
fun runTest(request: ThemeCreateRequest) {
|
||||||
runPostTest(
|
runPostTest(
|
||||||
mockMvc = mockMvc,
|
mockMvc = mockMvc,
|
||||||
endpoint = endpoint,
|
endpoint = endpoint,
|
||||||
@ -197,7 +197,7 @@ class ThemeControllerTest(mockMvc: MockMvc) : RoomescapeApiTest() {
|
|||||||
|
|
||||||
every {
|
every {
|
||||||
themeService.createTheme(request)
|
themeService.createTheme(request)
|
||||||
} returns ThemeResponse(
|
} returns ThemeRetrieveResponse(
|
||||||
id = theme.id!!,
|
id = theme.id!!,
|
||||||
name = theme.name,
|
name = theme.name,
|
||||||
description = theme.description,
|
description = theme.description,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user