generated from pricelees/issue-pr-template
refactor: 테마 반환 DTO 이름 수정 및 테스트 추가
This commit is contained in:
parent
fc3c6e42b0
commit
ed618e1699
@ -24,17 +24,26 @@ class ThemeService(
|
||||
private val themeValidator: ThemeValidator
|
||||
) {
|
||||
@Transactional(readOnly = true)
|
||||
fun findThemesByIds(request: ThemeListRetrieveRequest): ThemeRetrieveListResponse {
|
||||
fun findThemesByIds(request: ThemeListRetrieveRequest): ThemeSummaryListResponse {
|
||||
log.info { "[ThemeService.findThemesByIds] 예약 페이지에서의 테마 목록 조회 시작: themeIds=${request.themeIds}" }
|
||||
val result: MutableList<ThemeEntity> = mutableListOf()
|
||||
|
||||
return request.themeIds
|
||||
.map { findOrThrow(it) }
|
||||
.toRetrieveListResponse()
|
||||
.also { log.info { "[ThemeService.findThemesByIds] ${it.themes.size}개 테마 조회 완료" } }
|
||||
for (id in request.themeIds) {
|
||||
val theme: ThemeEntity? = themeRepository.findByIdOrNull(id)
|
||||
if (theme == null) {
|
||||
log.warn { "[ThemeService.findThemesByIds] id=${id} 인 테마 조회 실패" }
|
||||
continue
|
||||
}
|
||||
result.add(theme)
|
||||
}
|
||||
|
||||
return result.toRetrieveListResponse().also {
|
||||
log.info { "[ThemeService.findThemesByIds] ${it.themes.size} / ${request.themeIds.size} 개 테마 조회 완료" }
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
fun findThemesForReservation(): ThemeRetrieveListResponse {
|
||||
fun findThemesForReservation(): ThemeSummaryListResponse {
|
||||
log.info { "[ThemeService.findThemesForReservation] 예약 페이지에서의 테마 목록 조회 시작" }
|
||||
|
||||
return themeRepository.findOpenedThemes()
|
||||
@ -65,10 +74,10 @@ class ThemeService(
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
fun findById(id: Long): ThemeRetrieveResponseV2 {
|
||||
fun findSummaryById(id: Long): ThemeSummaryResponse {
|
||||
log.info { "[ThemeService.findById] 테마 조회 시작: id=$id" }
|
||||
|
||||
return findOrThrow(id).toRetrieveResponse()
|
||||
return findOrThrow(id).toSummaryResponse()
|
||||
.also { log.info { "[ThemeService.findById] 테마 조회 완료: id=$id" } }
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ import roomescape.theme.web.ThemeCreateRequest
|
||||
import roomescape.theme.web.ThemeCreateResponseV2
|
||||
import roomescape.theme.web.ThemeListRetrieveRequest
|
||||
import roomescape.theme.web.ThemeUpdateRequest
|
||||
import roomescape.theme.web.ThemeRetrieveListResponse
|
||||
import roomescape.theme.web.ThemeSummaryListResponse
|
||||
|
||||
@Tag(name = "5. 관리자 테마 API", description = "관리자 페이지에서 테마를 조회 / 추가 / 삭제할 때 사용합니다.")
|
||||
interface ThemeAPIV2 {
|
||||
@ -53,10 +53,10 @@ interface ThemeAPIV2 {
|
||||
@LoginRequired
|
||||
@Operation(summary = "예약 페이지에서 모든 테마 조회", description = "모든 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
|
||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||
fun findUserThemes(): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>>
|
||||
fun findUserThemes(): ResponseEntity<CommonApiResponse<ThemeSummaryListResponse>>
|
||||
|
||||
@LoginRequired
|
||||
@Operation(summary = "예약 페이지에서 입력한 날짜에 가능한 테마 조회", description = "입력한 날짜에 가능한 테마를 조회합니다.", tags = ["로그인이 필요한 API"])
|
||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||
fun findThemesByIds(request: ThemeListRetrieveRequest): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>>
|
||||
fun findThemesByIds(request: ThemeListRetrieveRequest): ResponseEntity<CommonApiResponse<ThemeSummaryListResponse>>
|
||||
}
|
||||
|
||||
@ -15,14 +15,14 @@ class ThemeController(
|
||||
@PostMapping("/themes/retrieve")
|
||||
override fun findThemesByIds(
|
||||
@RequestBody request: ThemeListRetrieveRequest
|
||||
): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>> {
|
||||
): ResponseEntity<CommonApiResponse<ThemeSummaryListResponse>> {
|
||||
val response = themeService.findThemesByIds(request)
|
||||
|
||||
return ResponseEntity.ok(CommonApiResponse(response))
|
||||
}
|
||||
|
||||
@GetMapping("/v2/themes")
|
||||
override fun findUserThemes(): ResponseEntity<CommonApiResponse<ThemeRetrieveListResponse>> {
|
||||
override fun findUserThemes(): ResponseEntity<CommonApiResponse<ThemeSummaryListResponse>> {
|
||||
val response = themeService.findThemesForReservation()
|
||||
|
||||
return ResponseEntity.ok(CommonApiResponse(response))
|
||||
|
||||
@ -132,7 +132,7 @@ data class ThemeListRetrieveRequest(
|
||||
val themeIds: List<Long>
|
||||
)
|
||||
|
||||
data class ThemeRetrieveResponseV2(
|
||||
data class ThemeSummaryResponse(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
val thumbnailUrl: String,
|
||||
@ -146,7 +146,7 @@ data class ThemeRetrieveResponseV2(
|
||||
val expectedMinutesTo: Short
|
||||
)
|
||||
|
||||
fun ThemeEntity.toRetrieveResponse() = ThemeRetrieveResponseV2(
|
||||
fun ThemeEntity.toSummaryResponse() = ThemeSummaryResponse(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
thumbnailUrl = this.thumbnailUrl,
|
||||
@ -160,10 +160,10 @@ fun ThemeEntity.toRetrieveResponse() = ThemeRetrieveResponseV2(
|
||||
expectedMinutesTo = this.expectedMinutesTo
|
||||
)
|
||||
|
||||
data class ThemeRetrieveListResponse(
|
||||
val themes: List<ThemeRetrieveResponseV2>
|
||||
data class ThemeSummaryListResponse(
|
||||
val themes: List<ThemeSummaryResponse>
|
||||
)
|
||||
|
||||
fun List<ThemeEntity>.toRetrieveListResponse() = ThemeRetrieveListResponse(
|
||||
themes = this.map { it.toRetrieveResponse() }
|
||||
fun List<ThemeEntity>.toRetrieveListResponse() = ThemeSummaryListResponse(
|
||||
themes = this.map { it.toSummaryResponse() }
|
||||
)
|
||||
|
||||
@ -4,15 +4,11 @@ import io.kotest.matchers.date.shouldBeAfter
|
||||
import io.kotest.matchers.nulls.shouldNotBeNull
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.restassured.module.kotlin.extensions.Extract
|
||||
import io.restassured.module.kotlin.extensions.Given
|
||||
import io.restassured.module.kotlin.extensions.When
|
||||
import io.restassured.response.ValidatableResponse
|
||||
import org.hamcrest.CoreMatchers.equalTo
|
||||
import org.hamcrest.CoreMatchers.notNullValue
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.MediaType
|
||||
import roomescape.auth.exception.AuthErrorCode
|
||||
import roomescape.member.infrastructure.persistence.Role
|
||||
import roomescape.theme.business.MIN_DURATION
|
||||
@ -21,9 +17,10 @@ import roomescape.theme.business.MIN_PRICE
|
||||
import roomescape.theme.exception.ThemeErrorCode
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.theme.infrastructure.persistence.ThemeRepository
|
||||
import roomescape.theme.web.ThemeCreateRequest
|
||||
import roomescape.theme.web.ThemeListRetrieveRequest
|
||||
import roomescape.theme.web.ThemeUpdateRequest
|
||||
import roomescape.util.FunSpecSpringbootTest
|
||||
import roomescape.util.INVALID_PK
|
||||
import roomescape.util.ThemeFixture.createRequest
|
||||
import roomescape.util.assertProperties
|
||||
import roomescape.util.runTest
|
||||
@ -105,10 +102,15 @@ class ThemeApiTest(
|
||||
|
||||
context("일반 회원도 접근할 수 있다.") {
|
||||
test("테마 조회: GET /v2/themes") {
|
||||
createDummyTheme(createRequest.copy(name = "test123", isOpen = true))
|
||||
val token = loginUtil.loginAsUser()
|
||||
|
||||
dummyInitializer.createTheme(
|
||||
adminToken = loginUtil.loginAsAdmin(),
|
||||
request = createRequest.copy(name = "test123", isOpen = true)
|
||||
)
|
||||
|
||||
runTest(
|
||||
token = loginUtil.loginAsUser(),
|
||||
token = token,
|
||||
on = {
|
||||
get("/v2/themes")
|
||||
},
|
||||
@ -158,7 +160,10 @@ class ThemeApiTest(
|
||||
|
||||
test("이미 동일한 이름의 테마가 있으면 실패한다.") {
|
||||
val commonName = "test123"
|
||||
createDummyTheme(createRequest.copy(name = commonName))
|
||||
dummyInitializer.createTheme(
|
||||
adminToken = token,
|
||||
request = createRequest.copy(name = commonName)
|
||||
)
|
||||
|
||||
runTest(
|
||||
token = token,
|
||||
@ -329,15 +334,77 @@ class ThemeApiTest(
|
||||
}
|
||||
}
|
||||
|
||||
context("모든 테마를 조회한다.") {
|
||||
context("입력된 모든 ID에 대한 테마를 조회한다.") {
|
||||
val themeIds = mutableListOf<Long>()
|
||||
|
||||
beforeTest {
|
||||
createDummyTheme(createRequest.copy(name = "open", isOpen = true))
|
||||
createDummyTheme(createRequest.copy(name = "close", isOpen = false))
|
||||
for (i in 1..3) {
|
||||
dummyInitializer.createTheme(
|
||||
adminToken = loginUtil.loginAsAdmin(),
|
||||
request = createRequest.copy(name = "test$i")
|
||||
).also {
|
||||
themeIds.add(it.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
afterTest {
|
||||
themeIds.clear()
|
||||
}
|
||||
|
||||
test("정상 응답") {
|
||||
runTest(
|
||||
token = loginUtil.loginAsUser(),
|
||||
using = {
|
||||
body(ThemeListRetrieveRequest(themeIds))
|
||||
},
|
||||
on = {
|
||||
post("/themes/retrieve")
|
||||
},
|
||||
expect = {
|
||||
statusCode(HttpStatus.OK.value())
|
||||
body("data.themes.size()", equalTo(3))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
test("없는 테마가 있으면 생략한다.") {
|
||||
themeIds.add(INVALID_PK)
|
||||
|
||||
runTest(
|
||||
token = loginUtil.loginAsUser(),
|
||||
using = {
|
||||
body(ThemeListRetrieveRequest(themeIds))
|
||||
},
|
||||
on = {
|
||||
post("/themes/retrieve")
|
||||
},
|
||||
expect = {
|
||||
statusCode(HttpStatus.OK.value())
|
||||
body("data.themes.size()", equalTo(3))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
context("모든 테마를 조회한다.") {
|
||||
lateinit var token: String
|
||||
|
||||
beforeTest {
|
||||
token = loginUtil.loginAsAdmin()
|
||||
dummyInitializer.createTheme(
|
||||
adminToken = token,
|
||||
request = createRequest.copy(name = "open", isOpen = true)
|
||||
)
|
||||
dummyInitializer.createTheme(
|
||||
adminToken = token,
|
||||
request = createRequest.copy(name = "close", isOpen = false)
|
||||
)
|
||||
}
|
||||
|
||||
test("관리자 페이지에서는 비공개 테마까지 포함하여 간단한 정보만 조회된다.") {
|
||||
runTest(
|
||||
token = loginUtil.loginAsAdmin(),
|
||||
token = token,
|
||||
on = {
|
||||
get("/admin/themes")
|
||||
},
|
||||
@ -375,10 +442,14 @@ class ThemeApiTest(
|
||||
|
||||
context("관리자 페이지에서 특정 테마의 상세 정보를 조회한다.") {
|
||||
test("정상 응답") {
|
||||
val createdTheme: ThemeEntity = createDummyTheme(createRequest)
|
||||
val token = loginUtil.loginAsAdmin()
|
||||
val createdTheme = dummyInitializer.createTheme(
|
||||
adminToken = token,
|
||||
request = createRequest
|
||||
)
|
||||
|
||||
runTest(
|
||||
token = loginUtil.loginAsAdmin(),
|
||||
token = token,
|
||||
on = {
|
||||
get("/admin/themes/${createdTheme.id}")
|
||||
},
|
||||
@ -413,10 +484,14 @@ class ThemeApiTest(
|
||||
|
||||
context("테마를 삭제한다.") {
|
||||
test("정상 삭제") {
|
||||
val createdTheme = createDummyTheme(createRequest)
|
||||
val token = loginUtil.loginAsAdmin()
|
||||
val createdTheme = dummyInitializer.createTheme(
|
||||
adminToken = token,
|
||||
request = createRequest
|
||||
)
|
||||
|
||||
runTest(
|
||||
token = loginUtil.loginAsAdmin(),
|
||||
token = token,
|
||||
on = {
|
||||
delete("/admin/themes/${createdTheme.id}")
|
||||
},
|
||||
@ -451,7 +526,10 @@ class ThemeApiTest(
|
||||
|
||||
beforeTest {
|
||||
token = loginUtil.loginAsAdmin()
|
||||
createdTheme = createDummyTheme(createRequest.copy(name = "theme-${Random.nextInt()}"))
|
||||
createdTheme = dummyInitializer.createTheme(
|
||||
adminToken = token,
|
||||
request = createRequest.copy(name = "theme-${Random.nextInt()}")
|
||||
)
|
||||
apiPath = "/admin/themes/${createdTheme.id}"
|
||||
}
|
||||
|
||||
@ -669,19 +747,4 @@ class ThemeApiTest(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createDummyTheme(request: ThemeCreateRequest): ThemeEntity {
|
||||
val createdThemeId: Long = Given {
|
||||
contentType(MediaType.APPLICATION_JSON_VALUE)
|
||||
header("Authorization", "Bearer ${loginUtil.loginAsAdmin()}")
|
||||
body(request)
|
||||
} When {
|
||||
post("/admin/themes")
|
||||
} Extract {
|
||||
path("data.id")
|
||||
}
|
||||
|
||||
return themeRepository.findByIdOrNull(createdThemeId)
|
||||
?: throw RuntimeException("unreachable line")
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user