From 980be9bedb1b0a4ee25ced95fbb49f84df5fffcd Mon Sep 17 00:00:00 2001 From: Jaewon Min <67869514+ekzm8523@users.noreply.github.com> Date: Sun, 15 Oct 2023 22:49:18 +0900 Subject: [PATCH 1/4] =?UTF-8?q?test=20:=20=EB=AC=B8=EC=A0=9C=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80=20(#1?= =?UTF-8?q?24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test : 문제 통합테스트 추가 --- .gitignore | 1 + .../service/problem/LongProblemServiceImpl.kt | 9 ++ .../apiserver/controller/IntegrationTest.kt | 46 +++++-- .../admin/AdminControllerIntegrationTest.kt | 20 +-- .../AdminProblemControllerIntegrationTest.kt | 24 +--- ...dminProblemSetControllerIntegrationTest.kt | 30 +--- .../v1/problem/LongProblemIntegrationTest.kt | 100 ++++++++++++++ .../problem/MultipleProblemIntegrationTest.kt | 94 +++++++++++++ .../v1/problem/ShortProblemIntegrationTest.kt | 129 ++++++++++++++++++ .../service/problem/LongProblemServiceTest.kt | 1 + .../problem/MultipleProblemServiceTest.kt | 2 +- 11 files changed, 380 insertions(+), 76 deletions(-) create mode 100644 src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemIntegrationTest.kt create mode 100644 src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/MultipleProblemIntegrationTest.kt create mode 100644 src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/ShortProblemIntegrationTest.kt diff --git a/.gitignore b/.gitignore index 9a58980b..46391560 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ src/main/resources/static/ ## test ## spy.log +src/test/resources/*.sql diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceImpl.kt index 4b78d771..cb831cc4 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceImpl.kt @@ -159,6 +159,15 @@ class LongProblemServiceImpl( val tags = problem.problemTags.map { it.tag.name } + + val gradingHistory = GradingHistory( + problem = problem, + user = user, + userAnswer = answer, + score = 0.0, + ) + gradingHistoryRepository.save(gradingHistory) + val gradingHistories = problem.gradingHistory val totalSubmissionCount = gradingHistories.size val userSubmissionCount = gradingHistories.count { it.user.id == user.id } diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/IntegrationTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/IntegrationTest.kt index 08cfad10..6ae8439c 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/IntegrationTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/IntegrationTest.kt @@ -7,6 +7,7 @@ import io.csbroker.apiserver.auth.ProviderType import io.csbroker.apiserver.common.enums.Role import io.csbroker.apiserver.model.User import org.intellij.lang.annotations.Language +import org.junit.jupiter.api.BeforeEach import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc import org.springframework.boot.test.context.SpringBootTest @@ -39,6 +40,35 @@ class IntegrationTest { @Autowired private lateinit var authTokenProvider: AuthTokenProvider + protected lateinit var defaultUser: User + protected lateinit var adminUser: User + private val userEmail = "test@test.com" + private val adminEmail = "admin@test.com" + + @BeforeEach + fun setUp() { + defaultUser = getOrCreateUser(userEmail) + adminUser = getOrCreateUser(adminEmail) + } + + fun getOrCreateUser(email: String): User = + try { + findOne("SELECT u FROM User u where u.email = '$email'") + } catch (e: Exception) { + saveUser(email, email == adminEmail) + } + + fun saveUser(email: String, isAdmin: Boolean): User = + save( + User( + email = email, + username = email.split("@")[0], + password = "test", + role = if (isAdmin) Role.ROLE_ADMIN else Role.ROLE_USER, + providerType = ProviderType.LOCAL, + ), + ) + fun request( method: HttpMethod, url: String, @@ -117,21 +147,7 @@ class IntegrationTest { } private fun createToken(isAdmin: Boolean): String { - val email = if (isAdmin) "test-admin@csbroker.io" else "test@csbroker.io" - val users = findAll("SELECT u FROM User u where u.email = '$email'") - val user = if (users.isNotEmpty()) { - users.first() - } else { - save( - User( - email = email, - username = "test", - password = "test", - role = if (isAdmin) Role.ROLE_ADMIN else Role.ROLE_USER, - providerType = ProviderType.LOCAL, - ), - ) - } + val user = getOrCreateUser(if (isAdmin) adminEmail else userEmail) return authTokenProvider.createAuthToken( user.email, diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/AdminControllerIntegrationTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/AdminControllerIntegrationTest.kt index 66693269..5ed1d805 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/AdminControllerIntegrationTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/AdminControllerIntegrationTest.kt @@ -35,23 +35,13 @@ class AdminControllerIntegrationTest : IntegrationTest() { @Test fun `단일 알림 생성`() { - // given - val user = save( - User( - email = "test-noti@test.com", - username = "test-noti", - password = "test-noti", - providerType = ProviderType.LOCAL, - ), - ) - // when val response = request( method = HttpMethod.POST, url = "/api/admin/notification", body = NotificationRequestDto( content = "test", - userId = user.id!!, + userId = defaultUser.id!!, link = "https://test.com", ), isAdmin = true, @@ -67,7 +57,7 @@ class AdminControllerIntegrationTest : IntegrationTest() { ) notification.content shouldBe "test" notification.link shouldBe "https://test.com" - notification.user.id shouldBe user.id + notification.user.id shouldBe defaultUser.id } } @@ -76,9 +66,9 @@ class AdminControllerIntegrationTest : IntegrationTest() { // given val user = save( User( - email = "test-noti2@test.com", - username = "test-noti2", - password = "test-noti2", + email = "manyNotiCreator@test.com", + username = "manyNotiCreator", + password = "test", providerType = ProviderType.LOCAL, ), ) diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminProblemControllerIntegrationTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminProblemControllerIntegrationTest.kt index ac84403b..5574467d 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminProblemControllerIntegrationTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminProblemControllerIntegrationTest.kt @@ -1,11 +1,9 @@ package io.csbroker.apiserver.controller.v1.admin.problem -import io.csbroker.apiserver.auth.ProviderType import io.csbroker.apiserver.controller.IntegrationTest import io.csbroker.apiserver.dto.problem.ProblemDeleteRequestDto import io.csbroker.apiserver.model.LongProblem import io.csbroker.apiserver.model.Problem -import io.csbroker.apiserver.model.User import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import org.springframework.http.HttpMethod @@ -15,19 +13,11 @@ class AdminProblemControllerIntegrationTest : IntegrationTest() { @Test fun `문제 단일 삭제`() { // given - val user = save( - User( - email = "test-problem-creator@test.com", - username = "test-problem1", - password = "test-problem1", - providerType = ProviderType.LOCAL, - ), - ) val problem = save( LongProblem( title = "문제 제목", description = "문제 설명", - creator = user, + creator = adminUser, ), ) @@ -49,26 +39,18 @@ class AdminProblemControllerIntegrationTest : IntegrationTest() { @Test fun `문제 여러개 삭제`() { // given - val user = save( - User( - email = "test-problem-creator2@test.com", - username = "test-problem2", - password = "test-problem2", - providerType = ProviderType.LOCAL, - ), - ) val problem1 = save( LongProblem( title = "문제 제목", description = "문제 설명", - creator = user, + creator = adminUser, ), ) val problem2 = save( LongProblem( title = "문제 제목", description = "문제 설명", - creator = user, + creator = adminUser, ), ) diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminProblemSetControllerIntegrationTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminProblemSetControllerIntegrationTest.kt index d05280ed..cb27f6a2 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminProblemSetControllerIntegrationTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminProblemSetControllerIntegrationTest.kt @@ -1,8 +1,6 @@ package io.csbroker.apiserver.controller.v1.admin.problem import com.jayway.jsonpath.JsonPath -import io.csbroker.apiserver.auth.ProviderType -import io.csbroker.apiserver.common.enums.Role import io.csbroker.apiserver.controller.IntegrationTest import io.csbroker.apiserver.dto.problem.problemset.ProblemSetUpsertRequestDto import io.csbroker.apiserver.model.LongProblem @@ -10,7 +8,6 @@ import io.csbroker.apiserver.model.MultipleChoiceProblem import io.csbroker.apiserver.model.ProblemSet import io.csbroker.apiserver.model.ProblemSetMapping import io.csbroker.apiserver.model.ShortProblem -import io.csbroker.apiserver.model.User import io.kotest.matchers.shouldBe import org.junit.jupiter.api.Test import org.springframework.http.HttpMethod @@ -20,26 +17,18 @@ class AdminProblemSetControllerIntegrationTest : IntegrationTest() { @Test fun `문제 세트 생성`() { // given - val user = save( - User( - email = "problemCreator@csbroker.io", - username = "problemCreator", - role = Role.ROLE_ADMIN, - providerType = ProviderType.LOCAL, - ), - ) val longProblem = save( LongProblem( title = "long problem", description = "long problem description", - creator = user, + creator = adminUser, ), ) val shortProblem = save( ShortProblem( title = "short problem", description = "short problem description", - creator = user, + creator = adminUser, score = 10.0, answer = "answer", ), @@ -69,26 +58,19 @@ class AdminProblemSetControllerIntegrationTest : IntegrationTest() { @Test fun `문제 세트 수정`() { // given - val user = save( - User( - email = "problemCreator2@csbroker.io", - username = "problemCreator2", - role = Role.ROLE_ADMIN, - providerType = ProviderType.LOCAL, - ), - ) + val longProblem = save( LongProblem( title = "long problem", description = "long problem description", - creator = user, + creator = adminUser, ), ) val shortProblem = save( ShortProblem( title = "short problem", description = "short problem description", - creator = user, + creator = adminUser, score = 10.0, answer = "answer", ), @@ -97,7 +79,7 @@ class AdminProblemSetControllerIntegrationTest : IntegrationTest() { MultipleChoiceProblem( title = "multiple choice problem", description = "multiple choice problem description", - creator = user, + creator = adminUser, score = 10.0, isMultiple = false, ), diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemIntegrationTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemIntegrationTest.kt new file mode 100644 index 00000000..82816cd7 --- /dev/null +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemIntegrationTest.kt @@ -0,0 +1,100 @@ +package io.csbroker.apiserver.controller.v1.problem + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import com.jayway.jsonpath.JsonPath +import io.csbroker.apiserver.controller.IntegrationTest +import io.csbroker.apiserver.controller.v2.problem.response.SubmitLongProblemResponseDto +import io.csbroker.apiserver.dto.problem.longproblem.LongProblemAnswerDto +import io.csbroker.apiserver.model.GradingHistory +import io.csbroker.apiserver.model.LongProblem +import io.csbroker.apiserver.model.StandardAnswer +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import org.springframework.http.HttpMethod +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +class LongProblemIntegrationTest : IntegrationTest() { + + private val baseUrl = "/api/v1/problems/long" + private val objectMapper = jacksonObjectMapper() + + @Test + fun `서술형 문제 조회`() { + // given & when + + val problem = getProblem() + val response = request( + method = HttpMethod.GET, + url = "$baseUrl/${problem.id}", + ) + + // then + response.andExpect(status().isOk) + .andDo { + val title = JsonPath.read(it.response.contentAsString, "$.data.title") as String + val description = JsonPath.read(it.response.contentAsString, "$.data.description") as String + title shouldBe problem.title + description shouldBe problem.description + } + } + + @Test + fun `서술형 문제 제출`() { + val problem = getProblem() + + // given & when + val preSubmissionCount = findAll( + "SELECT gh From GradingHistory gh where gh.problem.id = :id", + mapOf("id" to problem.id!!), + ).size + + val preUserSubmissionCount = findAll( + "SELECT gh From GradingHistory gh where gh.problem.id = :problemId AND gh.user.id = :userId", + mapOf("problemId" to problem.id!!, "userId" to defaultUser.id!!), + ).size + + val standardAnswers = findAll( + "SELECT sa From StandardAnswer sa where sa.longProblem.id = :id", + mapOf("id" to problem.id!!), + ) + + val userAnswer = "answer" + val response = request( + method = HttpMethod.POST, + url = "$baseUrl/${problem.id}/submit", + body = LongProblemAnswerDto(userAnswer), + ) + + // userAnswer 생성 확인 + + response.andExpect(status().isOk) + .andDo { + val jsonNode = objectMapper.readTree(it.response.contentAsString) + val dataNode = jsonNode.get("data") + val dataAsString = dataNode.toString() + val responseDto = objectMapper.readValue(dataAsString) + responseDto.title shouldBe problem.title + responseDto.description shouldBe problem.description + responseDto.totalSubmissionCount shouldBe preSubmissionCount + 1 // 제출 수가 증가하는지 확인 + responseDto.userSubmissionCount shouldBe preUserSubmissionCount + 1 // 제출 수가 증가하는지 확인 + responseDto.userAnswer shouldBe userAnswer + standardAnswers.map { sa -> sa.content } shouldContain responseDto.standardAnswer // 모법답안중 하나가 반환되는지 확인 + } + } + + fun getProblem(): LongProblem { + val problem = save( + LongProblem( + title = "title", + description = "description", + creator = defaultUser, + ), + ) + save(StandardAnswer(content = "standard answer1", longProblem = problem)) + save(StandardAnswer(content = "standard answer2", longProblem = problem)) + + return problem + } +} diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/MultipleProblemIntegrationTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/MultipleProblemIntegrationTest.kt new file mode 100644 index 00000000..d41f9050 --- /dev/null +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/MultipleProblemIntegrationTest.kt @@ -0,0 +1,94 @@ +package io.csbroker.apiserver.controller.v1.problem + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import com.jayway.jsonpath.JsonPath +import io.csbroker.apiserver.controller.IntegrationTest +import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemAnswerDto +import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemGradingHistoryDto +import io.csbroker.apiserver.model.Choice +import io.csbroker.apiserver.model.MultipleChoiceProblem +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import org.springframework.http.HttpMethod +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +class MultipleProblemIntegrationTest : IntegrationTest() { + + private val baseUrl = "/api/v1/problems/multiple" + private val objectMapper = jacksonObjectMapper() + + @Test + fun `객관식 문제 조회`() { + // given + val problem = save( + MultipleChoiceProblem( + title = "title", + description = "description", + creator = defaultUser, + score = 10.0, + isMultiple = false, + ), + ) + + val response = request( + method = HttpMethod.GET, + url = "$baseUrl/${problem.id}", + ) + + response.andExpect(status().isOk) + .andDo { + val title = JsonPath.read(it.response.contentAsString, "$.data.title") as String + val description = JsonPath.read(it.response.contentAsString, "$.data.description") as String + title shouldBe problem.title + description shouldBe problem.description + } + } + + @Test + fun `객관식 문제 제출`() { + // given + val problem = save( + MultipleChoiceProblem( + title = "title", + description = "description", + creator = defaultUser, + score = 10.0, + isMultiple = false, + ), + ) + + val choices = listOf( + Choice(content = "content1", isAnswer = true, multipleChoiceProblem = problem), + Choice(content = "content2", isAnswer = false, multipleChoiceProblem = problem), + Choice(content = "content3", isAnswer = false, multipleChoiceProblem = problem), + Choice(content = "content4", isAnswer = false, multipleChoiceProblem = problem), + Choice(content = "content5", isAnswer = false, multipleChoiceProblem = problem), + ).map { save(it) } + val answer = choices.find { it.isAnswer } + val requestDto = MultipleChoiceProblemAnswerDto(answerIds = listOf(answer?.id!!)) + + val response = request( + method = HttpMethod.POST, + url = "$baseUrl/${problem.id}/grade", + body = requestDto, + ) + + response.andExpect(status().isOk) + .andDo { + val jsonNode = objectMapper.readTree(it.response.contentAsString) + val dataNode = jsonNode.get("data").toString() + val responseDto = objectMapper.readValue(dataNode) + responseDto.problemId shouldBe problem.id + responseDto.title shouldBe problem.title + responseDto.description shouldBe problem.description + responseDto.correctSubmission shouldBe 1L + responseDto.correctUserCnt shouldBe 1L + responseDto.totalSubmission shouldBe 1L + responseDto.choices.map { choice -> choice.id }.containsAll(choices.map { choice -> choice.id }) + responseDto.userAnswerIds.size shouldBe 1 + responseDto.isAnswer shouldBe true + responseDto.score shouldBe problem.score + } + } +} diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/ShortProblemIntegrationTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/ShortProblemIntegrationTest.kt new file mode 100644 index 00000000..bba0ded5 --- /dev/null +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/ShortProblemIntegrationTest.kt @@ -0,0 +1,129 @@ +package io.csbroker.apiserver.controller.v1.problem + +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.fasterxml.jackson.module.kotlin.readValue +import com.jayway.jsonpath.JsonPath +import io.csbroker.apiserver.controller.IntegrationTest +import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemAnswerDto +import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemGradingHistoryDto +import io.csbroker.apiserver.model.ShortProblem +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.Test +import org.springframework.http.HttpMethod +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +class ShortProblemIntegrationTest : IntegrationTest() { + private val baseUrl = "/api/v1/problems/short" + private val objectMapper = jacksonObjectMapper() + + @Test + fun `단답형 문제 조회`() { + // given + val problem = save( + ShortProblem( + title = "title", + description = "description", + creator = defaultUser, + score = 10.0, + answer = "answer", + ), + ) + + val response = request( + method = HttpMethod.GET, + url = "$baseUrl/${problem.id}", + ) + + response.andExpect(status().isOk) + .andDo { + val title = JsonPath.read(it.response.contentAsString, "$.data.title") as String + val description = JsonPath.read(it.response.contentAsString, "$.data.description") as String + val problemId = JsonPath.read(it.response.contentAsString, "$.data.id") as Int + title shouldBe problem.title + description shouldBe problem.description + problemId shouldBe problem.id + } + } + + @Test + fun `단답형 문제 제출 (정답)`() { + // given + val problem = save( + ShortProblem( + title = "title", + description = "description", + creator = defaultUser, + score = 10.0, + answer = "answer", + ), + ) + + val answer = "answer" + val response = request( + method = HttpMethod.POST, + url = "$baseUrl/${problem.id}/grade", + body = ShortProblemAnswerDto(answer = answer), + ) + + response.andExpect(status().isOk) + .andDo { + val data = objectMapper.readTree(it.response.contentAsString) + .get("data") + .toString() + val responseDto = objectMapper.readValue(data) + print(data) + responseDto.problemId shouldBe problem.id + responseDto.title shouldBe problem.title + responseDto.description shouldBe problem.description + responseDto.correctSubmission shouldBe 1L + responseDto.correctUserCnt shouldBe 1L + responseDto.totalSubmission shouldBe 1L + responseDto.userAnswer shouldBe answer + responseDto.answerLength shouldBe problem.answer.length + responseDto.isAnswer shouldBe true + responseDto.score shouldBe problem.score + responseDto.correctAnswer shouldBe problem.answer + } + } + + @Test + fun `단답형 문제 제출 (오답)`() { + // given + val problem = save( + ShortProblem( + title = "title", + description = "description", + creator = defaultUser, + score = 10.0, + answer = "answer", + ), + ) + + val answer = "notAnswer" + val response = request( + method = HttpMethod.POST, + url = "$baseUrl/${problem.id}/grade", + body = ShortProblemAnswerDto(answer = answer), + ) + + response.andExpect(status().isOk) + .andDo { + val data = objectMapper.readTree(it.response.contentAsString) + .get("data") + .toString() + val responseDto = objectMapper.readValue(data) + print(data) + responseDto.problemId shouldBe problem.id + responseDto.title shouldBe problem.title + responseDto.description shouldBe problem.description + responseDto.correctSubmission shouldBe 0L + responseDto.correctUserCnt shouldBe 0L + responseDto.totalSubmission shouldBe 1L + responseDto.userAnswer shouldBe answer + responseDto.answerLength shouldBe problem.answer.length + responseDto.isAnswer shouldBe false + responseDto.score shouldBe 0.0 + responseDto.correctAnswer shouldBe problem.answer + } + } +} diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceTest.kt index 8af6fdbd..570b32a7 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceTest.kt @@ -127,6 +127,7 @@ class LongProblemServiceTest { every { longProblemRepository.findByIdOrNull(problemId) } returns longProblem every { userAnswerRepository.save(any()) } returns userAnswer every { standardAnswerRepository.findAllByLongProblem(longProblem) } returns listOf(standardAnswer) + every { gradingHistoryRepository.save(any()) } returns mockk() // when val result = longProblemService.submitProblem(submitRequest) diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceTest.kt index d56ac4ae..5e3402ea 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceTest.kt @@ -86,7 +86,7 @@ class MultipleProblemServiceTest { val problemId = 1L val answerIds = listOf(1L, 2L) val gradingRequest = MultipleProblemGradingRequestDto(email, problemId, answerIds) - val problem = mockk() + every { userRepository.findByEmail(email) } returns user every { multipleChoiceProblemRepository.findByIdOrNull(problemId) } returns null From c2ed18e3d8e56a0b195c6a12339828a780b721c2 Mon Sep 17 00:00:00 2001 From: Jaewon Min <67869514+ekzm8523@users.noreply.github.com> Date: Mon, 16 Oct 2023 00:03:09 +0900 Subject: [PATCH 2/4] Refactor/login user argument resolver (#129) Refactor/login user argument resolver (#129) --- .../auth/LoginUserArgumentResolver.kt | 16 ++-- .../problem/AdminLongProblemController.kt | 6 +- .../problem/AdminMultipleProblemController.kt | 6 +- .../problem/AdminShortProblemController.kt | 6 +- .../problem/AdminUserAnswerController.kt | 6 +- .../controller/v1/common/AuthController.kt | 2 +- .../controller/v1/common/CommonController.kt | 6 +- .../v1/common/NotificationController.kt | 12 +-- .../controller/v1/post/CommentController.kt | 2 +- .../controller/v1/post/PostController.kt | 2 +- .../v1/problem/LongProblemController.kt | 14 ++-- .../v1/problem/MultipleProblemController.kt | 4 +- .../v1/problem/ProblemController.kt | 4 +- .../v1/problem/ShortProblemController.kt | 4 +- .../controller/v1/user/UserController.kt | 8 +- .../v2/problem/ProblemControllerV2.kt | 4 +- .../problem/challenge/CreateChallengeDto.kt | 4 +- .../dto/problem/grade/GradingRequestDto.kt | 6 +- .../service/common/NotificationService.kt | 12 +-- .../service/common/NotificationServiceImpl.kt | 38 +++------- .../problem/CommonProblemServiceImpl.kt | 5 +- .../problem/MultipleProblemServiceImpl.kt | 6 +- .../problem/ShortProblemServiceImpl.kt | 6 +- .../service/problem/UserAnswerServiceImpl.kt | 1 + .../problem/admin/AdminLongProblemService.kt | 5 +- .../admin/AdminLongProblemServiceImpl.kt | 10 +-- .../admin/AdminMultipleProblemService.kt | 5 +- .../admin/AdminMultipleProblemServiceImpl.kt | 10 +-- .../problem/admin/AdminShortProblemService.kt | 5 +- .../admin/AdminShortProblemServiceImpl.kt | 10 +-- .../apiserver/service/user/UserService.kt | 6 +- .../apiserver/service/user/UserServiceImpl.kt | 35 ++++----- .../apiserver/controller/RestDocsTest.kt | 17 ++++- .../problem/AdminLongProblemControllerTest.kt | 2 +- .../AdminMultipleProblemControllerTest.kt | 2 +- .../AdminShortProblemControllerTest.kt | 2 +- .../v1/problem/LongProblemIntegrationTest.kt | 6 +- .../apiserver/service/UserServiceTest.kt | 34 +-------- .../service/common/NotificationServiceTest.kt | 75 +------------------ .../problem/CommonProblemServiceTest.kt | 15 +--- .../problem/MultipleProblemServiceTest.kt | 24 +----- .../problem/ShortProblemServiceTest.kt | 30 +------- .../admin/AdminLongProblemServiceTest.kt | 26 +------ .../admin/AdminMultipleProblemServiceTest.kt | 30 +------- .../admin/AdminShortProblemServiceTest.kt | 21 +----- 45 files changed, 167 insertions(+), 383 deletions(-) diff --git a/src/main/kotlin/io/csbroker/apiserver/auth/LoginUserArgumentResolver.kt b/src/main/kotlin/io/csbroker/apiserver/auth/LoginUserArgumentResolver.kt index cf0c3b3e..8a0af638 100644 --- a/src/main/kotlin/io/csbroker/apiserver/auth/LoginUserArgumentResolver.kt +++ b/src/main/kotlin/io/csbroker/apiserver/auth/LoginUserArgumentResolver.kt @@ -2,9 +2,10 @@ package io.csbroker.apiserver.auth import io.csbroker.apiserver.common.enums.ErrorCode import io.csbroker.apiserver.common.exception.UnAuthorizedException +import io.csbroker.apiserver.model.User +import io.csbroker.apiserver.repository.user.UserRepository import org.springframework.core.MethodParameter import org.springframework.security.core.context.SecurityContextHolder -import org.springframework.security.core.userdetails.User import org.springframework.stereotype.Component import org.springframework.web.bind.support.WebDataBinderFactory import org.springframework.web.context.request.NativeWebRequest @@ -12,7 +13,9 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver import org.springframework.web.method.support.ModelAndViewContainer @Component -class LoginUserArgumentResolver : HandlerMethodArgumentResolver { +class LoginUserArgumentResolver( + private val userRepository: UserRepository, +) : HandlerMethodArgumentResolver { override fun supportsParameter(parameter: MethodParameter): Boolean { parameter.getParameterAnnotation(LoginUser::class.java) ?: return false return parameter.parameterType.isAssignableFrom(User::class.java) @@ -24,13 +27,14 @@ class LoginUserArgumentResolver : HandlerMethodArgumentResolver { webRequest: NativeWebRequest, binderFactory: WebDataBinderFactory?, ): Any { - val principal = SecurityContextHolder.getContext().authentication?.principal + val authentication = SecurityContextHolder.getContext().authentication - // 유저 검증이 되지 않으면, anonymousUser 라는 String으로 넘어옴. 그것을 방지하기 위한 검증. - if (principal is String) { + if (authentication == null || authentication.principal.equals("anonymousUser")) { throw UnAuthorizedException(ErrorCode.UNAUTHORIZED, "로그인이 필요합니다.") } + val email = authentication.name - return principal ?: throw UnAuthorizedException(ErrorCode.UNAUTHORIZED, "로그인이 필요합니다.") + return userRepository.findByEmail(email) + ?: throw UnAuthorizedException(ErrorCode.NOT_FOUND_ENTITY, "알 수 없는 유저의 요청입니다.") } } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminLongProblemController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminLongProblemController.kt index 333970cd..f1689b2d 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminLongProblemController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminLongProblemController.kt @@ -7,9 +7,9 @@ import io.csbroker.apiserver.dto.problem.AdminProblemSearchDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemResponseDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemSearchResponseDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemUpsertRequestDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.problem.admin.AdminLongProblemService import org.springframework.data.domain.Pageable -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -37,7 +37,7 @@ class AdminLongProblemController( @RequestBody createRequestDto: LongProblemUpsertRequestDto, @LoginUser loginUser: User, ): ApiResponse { - val createProblemId = longProblemService.createProblem(createRequestDto, loginUser.username) + val createProblemId = longProblemService.createProblem(createRequestDto, loginUser) return ApiResponse.success(UpsertSuccessResponseDto(createProblemId)) } @@ -47,7 +47,7 @@ class AdminLongProblemController( @RequestBody updateRequestDto: LongProblemUpsertRequestDto, @LoginUser loginUser: User, ): ApiResponse { - val updateProblemId = longProblemService.updateProblem(id, updateRequestDto, loginUser.username) + val updateProblemId = longProblemService.updateProblem(id, updateRequestDto) return ApiResponse.success(UpsertSuccessResponseDto(updateProblemId)) } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminMultipleProblemController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminMultipleProblemController.kt index 2bb3b283..81b6c4c7 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminMultipleProblemController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminMultipleProblemController.kt @@ -7,9 +7,9 @@ import io.csbroker.apiserver.dto.problem.AdminProblemSearchDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemResponseDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemSearchResponseDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemUpsertRequestDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.problem.admin.AdminMultipleProblemService import org.springframework.data.domain.Pageable -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -37,7 +37,7 @@ class AdminMultipleProblemController( @RequestBody createRequestDto: MultipleChoiceProblemUpsertRequestDto, @LoginUser loginUser: User, ): ApiResponse { - val createProblemId = multipleProblemService.createProblem(createRequestDto, loginUser.username) + val createProblemId = multipleProblemService.createProblem(createRequestDto, loginUser) return ApiResponse.success(UpsertSuccessResponseDto(createProblemId)) } @@ -47,7 +47,7 @@ class AdminMultipleProblemController( @RequestBody updateRequestDto: MultipleChoiceProblemUpsertRequestDto, @LoginUser loginUser: User, ): ApiResponse { - val updateProblemId = multipleProblemService.updateProblem(id, updateRequestDto, loginUser.username) + val updateProblemId = multipleProblemService.updateProblem(id, updateRequestDto) return ApiResponse.success(UpsertSuccessResponseDto(updateProblemId)) } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminShortProblemController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminShortProblemController.kt index 7e6f64e1..6acca2d2 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminShortProblemController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminShortProblemController.kt @@ -7,9 +7,9 @@ import io.csbroker.apiserver.dto.problem.AdminProblemSearchDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemResponseDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemSearchResponseDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemUpsertRequestDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.problem.admin.AdminShortProblemService import org.springframework.data.domain.Pageable -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -37,7 +37,7 @@ class AdminShortProblemController( @RequestBody createRequestDto: ShortProblemUpsertRequestDto, @LoginUser loginUser: User, ): ApiResponse { - val createProblemId = shortProblemService.createProblem(createRequestDto, loginUser.username) + val createProblemId = shortProblemService.createProblem(createRequestDto, loginUser) return ApiResponse.success(UpsertSuccessResponseDto(createProblemId)) } @@ -47,7 +47,7 @@ class AdminShortProblemController( @RequestBody updateRequestDto: ShortProblemUpsertRequestDto, @LoginUser loginUser: User, ): ApiResponse { - val updateProblemId = shortProblemService.updateProblem(id, updateRequestDto, loginUser.username) + val updateProblemId = shortProblemService.updateProblem(id, updateRequestDto) return ApiResponse.success(UpsertSuccessResponseDto(updateProblemId)) } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminUserAnswerController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminUserAnswerController.kt index 324847da..75768b21 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminUserAnswerController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminUserAnswerController.kt @@ -11,9 +11,9 @@ import io.csbroker.apiserver.dto.useranswer.UserAnswerLabelRequestDto import io.csbroker.apiserver.dto.useranswer.UserAnswerResponseDto import io.csbroker.apiserver.dto.useranswer.UserAnswerSearchResponseDto import io.csbroker.apiserver.dto.useranswer.UserAnswerUpsertDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.problem.UserAnswerService import org.springframework.data.domain.Pageable -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -62,14 +62,14 @@ class AdminUserAnswerController( val answerId = when (type) { "label" -> userAnswerService.labelUserAnswer( - loginUser.username, + loginUser.email, id, userAnswerLabelRequestDto.selectedGradingStandardIds, ) "validate" -> userAnswerService.validateUserAnswer( - loginUser.username, + loginUser.email, id, userAnswerLabelRequestDto.selectedGradingStandardIds, ) diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/AuthController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/AuthController.kt index 704b1635..0ff3b5ef 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/AuthController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/AuthController.kt @@ -12,11 +12,11 @@ import io.csbroker.apiserver.dto.common.UpsertSuccessResponseDto import io.csbroker.apiserver.dto.user.UserInfoResponseDto import io.csbroker.apiserver.dto.user.UserLoginRequestDto import io.csbroker.apiserver.dto.user.UserSignUpDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.repository.common.REFRESH_TOKEN import io.csbroker.apiserver.service.auth.AuthService import io.csbroker.apiserver.service.common.MailService import kotlinx.coroutines.runBlocking -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.PutMapping diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/CommonController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/CommonController.kt index 354906da..ed99dfe8 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/CommonController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/CommonController.kt @@ -5,12 +5,12 @@ import io.csbroker.apiserver.dto.StatsDto import io.csbroker.apiserver.dto.common.ApiResponse import io.csbroker.apiserver.dto.common.ChatCompletionRequestDto import io.csbroker.apiserver.dto.common.RankListDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.common.ChatService import io.csbroker.apiserver.service.common.CommonService import io.csbroker.apiserver.service.common.S3Service import io.csbroker.apiserver.service.user.UserService import kotlinx.coroutines.runBlocking -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody @@ -37,7 +37,7 @@ class CommonController( fun uploadImg(@RequestPart("image") multipartFile: MultipartFile, @LoginUser loginUser: User): ApiResponse { return runBlocking { val imgUrl = s3Service.uploadProfileImg(multipartFile) - userService.updateUserProfileImg(loginUser.username, imgUrl) + userService.updateUserProfileImg(loginUser, imgUrl) ApiResponse.success(imgUrl) } } @@ -65,6 +65,6 @@ class CommonController( @LoginUser loginUser: User, @RequestBody chatCompletionRequestDto: ChatCompletionRequestDto, ): ApiResponse { - return ApiResponse.success(chatService.completeChat(loginUser.username, chatCompletionRequestDto.content)) + return ApiResponse.success(chatService.completeChat(loginUser.email, chatCompletionRequestDto.content)) } } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/NotificationController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/NotificationController.kt index 347c1571..6b3abd4f 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/NotificationController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/NotificationController.kt @@ -7,11 +7,11 @@ import io.csbroker.apiserver.dto.notification.NotificationBulkReadDto import io.csbroker.apiserver.dto.notification.NotificationPageResponseDto import io.csbroker.apiserver.dto.notification.NotificationReadResponseDto import io.csbroker.apiserver.dto.notification.UnReadNotificationCountDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.common.NotificationService import org.springframework.data.domain.Pageable import org.springframework.data.domain.Sort import org.springframework.data.web.PageableDefault -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -34,13 +34,13 @@ class NotificationController( direction = Sort.Direction.DESC, ) pageable: Pageable, ): ApiResponse { - val notifications = notificationService.getNotification(loginUser.username, pageable) + val notifications = notificationService.getNotification(loginUser.id!!, pageable) return ApiResponse.success(NotificationPageResponseDto(notifications)) } @GetMapping("/count") fun getUnreadNotificationCount(@LoginUser loginUser: User): ApiResponse { - val count = notificationService.getUnreadNotificationCount(loginUser.username) + val count = notificationService.getUnreadNotificationCount(loginUser.id!!) return ApiResponse.success(UnReadNotificationCountDto(count)) } @@ -49,7 +49,7 @@ class NotificationController( @LoginUser loginUser: User, @RequestBody notificationBulkReadDto: NotificationBulkReadDto, ): ApiResponse { - notificationService.readNotifications(loginUser.username, notificationBulkReadDto.ids) + notificationService.readNotifications(loginUser.id!!, notificationBulkReadDto.ids) return ApiResponse.success(NotificationReadResponseDto()) } @@ -58,7 +58,7 @@ class NotificationController( @LoginUser loginUser: User, @PathVariable("id") id: Long, ): ApiResponse { - notificationService.readNotificationById(loginUser.username, id) + notificationService.readNotificationById(loginUser.id!!, id) return ApiResponse.success(NotificationReadResponseDto()) } @@ -67,7 +67,7 @@ class NotificationController( @LoginUser loginUser: User, @RequestBody notificationBulkDeleteDto: NotificationBulkDeleteDto, ): ApiResponse { - notificationService.deleteNotifications(loginUser.username, notificationBulkDeleteDto.ids) + notificationService.deleteNotifications(loginUser, notificationBulkDeleteDto.ids) return ApiResponse.success(true) } } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt index 0bdb3f8d..8add0408 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt @@ -3,8 +3,8 @@ package io.csbroker.apiserver.controller.v1.post import io.csbroker.apiserver.auth.LoginUser import io.csbroker.apiserver.controller.v1.post.request.CommentCreateRequestDto import io.csbroker.apiserver.dto.common.ApiResponse +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.post.CommentService -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt index d6dc43ae..258cb5fc 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt @@ -5,8 +5,8 @@ import io.csbroker.apiserver.common.util.getEmailFromSecurityContextHolder import io.csbroker.apiserver.controller.v1.post.request.PostCreateRequestDto import io.csbroker.apiserver.controller.v1.post.response.PostResponseDto import io.csbroker.apiserver.dto.common.ApiResponse +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.post.PostService -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemController.kt index 7ad714fd..4ae6f094 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemController.kt @@ -9,9 +9,9 @@ import io.csbroker.apiserver.dto.problem.grade.LongProblemGradingRequestDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemAnswerDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemDetailResponseDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemGradingHistoryDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.post.PostService import io.csbroker.apiserver.service.problem.LongProblemService -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -41,7 +41,7 @@ class LongProblemController( @RequestParam("isGrading", required = false) isGrading: Boolean?, ): ApiResponse { val gradingRequestDto = LongProblemGradingRequestDto( - loginUser.username, + loginUser.email, id, answerDto.answer, isGrading ?: true, @@ -52,13 +52,17 @@ class LongProblemController( @PostMapping("{id}/submit") fun submitLongProblem( - @LoginUser user: User, + @LoginUser loginUser: User, @PathVariable("id") problemId: Long, @RequestBody answerDto: LongProblemAnswerDto, ): ApiResponse { - val submitRequestDto = SubmitLongProblemDto(user.username, problemId, answerDto.answer) + val submitRequestDto = SubmitLongProblemDto(loginUser.email, problemId, answerDto.answer) val submitResponseDto = longProblemService.submitProblem(submitRequestDto) - postService.create(problemId = submitRequestDto.problemId, content = answerDto.answer, email = user.username) + postService.create( + problemId = submitRequestDto.problemId, + content = answerDto.answer, + email = loginUser.email, + ) return ApiResponse.success(submitResponseDto) } } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/MultipleProblemController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/MultipleProblemController.kt index 61430b1a..74c09036 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/MultipleProblemController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/MultipleProblemController.kt @@ -7,8 +7,8 @@ import io.csbroker.apiserver.dto.problem.grade.MultipleProblemGradingRequestDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemAnswerDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemDetailResponseDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemGradingHistoryDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.problem.MultipleProblemService -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -35,7 +35,7 @@ class MultipleProblemController( @PathVariable("id") id: Long, @RequestBody answerDto: MultipleChoiceProblemAnswerDto, ): ApiResponse { - val gradingRequestDto = MultipleProblemGradingRequestDto(loginUser.username, id, answerDto.answerIds) + val gradingRequestDto = MultipleProblemGradingRequestDto(loginUser, id, answerDto.answerIds) val gradeHistory = multipleProblemService.gradingProblem(gradingRequestDto) return ApiResponse.success(gradeHistory) } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/ProblemController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/ProblemController.kt index da1f2cc4..264ebd2e 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/ProblemController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/ProblemController.kt @@ -10,8 +10,8 @@ import io.csbroker.apiserver.dto.problem.ProblemPageResponseDto import io.csbroker.apiserver.dto.problem.ProblemSearchDto import io.csbroker.apiserver.dto.problem.ProblemsResponseDto import io.csbroker.apiserver.dto.problem.grade.AssessmentRequestDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.problem.CommonProblemService -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -60,7 +60,7 @@ class ProblemController( return ApiResponse.success( UpsertSuccessResponseDto( commonProblemService.gradingAssessment( - loginUser.username, + loginUser.email, id, assessmentRequestDto, ), diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/ShortProblemController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/ShortProblemController.kt index d5d8f57f..7361ea4d 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/ShortProblemController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/ShortProblemController.kt @@ -7,8 +7,8 @@ import io.csbroker.apiserver.dto.problem.grade.ShortProblemGradingRequestDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemAnswerDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemDetailResponseDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemGradingHistoryDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.problem.ShortProblemService -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping @@ -34,7 +34,7 @@ class ShortProblemController( @PathVariable("id") id: Long, @RequestBody answerDto: ShortProblemAnswerDto, ): ApiResponse { - val gradingRequestDto = ShortProblemGradingRequestDto(loginUser.username, id, answerDto.answer) + val gradingRequestDto = ShortProblemGradingRequestDto(loginUser, id, answerDto.answer) val gradeHistory = shortProblemService.gradingProblem(gradingRequestDto) return ApiResponse.success(gradeHistory) } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/user/UserController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/user/UserController.kt index 1f2ca40d..53167304 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/user/UserController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/user/UserController.kt @@ -7,9 +7,9 @@ import io.csbroker.apiserver.dto.common.DeleteResponseDto import io.csbroker.apiserver.dto.user.UserResponseDto import io.csbroker.apiserver.dto.user.UserStatsDto import io.csbroker.apiserver.dto.user.UserUpdateRequestDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.user.UserService import org.springframework.security.access.prepost.PreAuthorize -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.DeleteMapping import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.PathVariable @@ -36,7 +36,7 @@ class UserController( @PreAuthorize("hasRole('ADMIN')") fun getUsers(): ApiResponse> { val result = userService.findUsers() - .map(io.csbroker.apiserver.model.User::toUserResponseDto) + .map(User::toUserResponseDto) return ApiResponse.success(result) } @@ -47,7 +47,7 @@ class UserController( @PathVariable("id") id: UUID, @RequestBody userUpdateRequestDto: UserUpdateRequestDto, ): ApiResponse { - val user = userService.modifyUser(id, loginUser.username, userUpdateRequestDto) + val user = userService.modifyUser(id, loginUser, userUpdateRequestDto) return ApiResponse.success(user.toUserResponseDto()) } @@ -63,7 +63,7 @@ class UserController( @LoginUser loginUser: User, @PathVariable("id") id: UUID, ): ApiResponse { - val result = userService.deleteUser(loginUser.username, id) + val result = userService.deleteUser(loginUser, id) return ApiResponse.success(DeleteResponseDto(id, result)) } } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/ProblemControllerV2.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/ProblemControllerV2.kt index b356a938..a298344b 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/ProblemControllerV2.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/ProblemControllerV2.kt @@ -4,8 +4,8 @@ import io.csbroker.apiserver.auth.LoginUser import io.csbroker.apiserver.controller.v2.problem.request.ChallengeCreateRequest import io.csbroker.apiserver.dto.common.ApiResponse import io.csbroker.apiserver.dto.problem.challenge.CreateChallengeDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.service.problem.CommonProblemService -import org.springframework.security.core.userdetails.User import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody @@ -26,7 +26,7 @@ class ProblemControllerV2( @RequestBody challengeCreateRequest: ChallengeCreateRequest, ): ApiResponse { - commonProblemService.createChallenge(CreateChallengeDto(loginUser.username, id, challengeCreateRequest.content)) + commonProblemService.createChallenge(CreateChallengeDto(loginUser, id, challengeCreateRequest.content)) return ApiResponse.success(true) } diff --git a/src/main/kotlin/io/csbroker/apiserver/dto/problem/challenge/CreateChallengeDto.kt b/src/main/kotlin/io/csbroker/apiserver/dto/problem/challenge/CreateChallengeDto.kt index a8b4aa85..fdfed6a8 100644 --- a/src/main/kotlin/io/csbroker/apiserver/dto/problem/challenge/CreateChallengeDto.kt +++ b/src/main/kotlin/io/csbroker/apiserver/dto/problem/challenge/CreateChallengeDto.kt @@ -1,7 +1,9 @@ package io.csbroker.apiserver.dto.problem.challenge +import io.csbroker.apiserver.model.User + data class CreateChallengeDto( - val email: String, + val user: User, val problemId: Long, val content: String, ) diff --git a/src/main/kotlin/io/csbroker/apiserver/dto/problem/grade/GradingRequestDto.kt b/src/main/kotlin/io/csbroker/apiserver/dto/problem/grade/GradingRequestDto.kt index 44f42964..6045bd29 100644 --- a/src/main/kotlin/io/csbroker/apiserver/dto/problem/grade/GradingRequestDto.kt +++ b/src/main/kotlin/io/csbroker/apiserver/dto/problem/grade/GradingRequestDto.kt @@ -1,15 +1,17 @@ package io.csbroker.apiserver.dto.problem.grade +import io.csbroker.apiserver.model.User + interface GradingRequestDto data class MultipleProblemGradingRequestDto( - val email: String, + val user: User, val problemId: Long, val answerIds: List, ) : GradingRequestDto data class ShortProblemGradingRequestDto( - val email: String, + val user: User, val problemId: Long, val answer: String, ) : GradingRequestDto diff --git a/src/main/kotlin/io/csbroker/apiserver/service/common/NotificationService.kt b/src/main/kotlin/io/csbroker/apiserver/service/common/NotificationService.kt index 68a5af52..2017e3d3 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/common/NotificationService.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/common/NotificationService.kt @@ -2,15 +2,17 @@ package io.csbroker.apiserver.service.common import io.csbroker.apiserver.dto.notification.NotificationRequestDto import io.csbroker.apiserver.model.Notification +import io.csbroker.apiserver.model.User import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable +import java.util.UUID interface NotificationService { fun createNotification(notificationRequestDto: NotificationRequestDto): Long fun createBulkNotification(notificationRequestListDto: List): Int - fun getNotification(email: String, page: Pageable): Page - fun readNotifications(email: String, notificationIds: List) - fun readNotificationById(email: String, id: Long) - fun getUnreadNotificationCount(email: String): Long - fun deleteNotifications(email: String, ids: List) + fun getNotification(userId: UUID, page: Pageable): Page + fun readNotifications(userId: UUID, notificationIds: List) + fun readNotificationById(userId: UUID, id: Long) + fun getUnreadNotificationCount(userId: UUID): Long + fun deleteNotifications(user: User, ids: List) } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/common/NotificationServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/common/NotificationServiceImpl.kt index b42c605a..34dada39 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/common/NotificationServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/common/NotificationServiceImpl.kt @@ -3,12 +3,14 @@ package io.csbroker.apiserver.service.common import io.csbroker.apiserver.common.exception.EntityNotFoundException import io.csbroker.apiserver.dto.notification.NotificationRequestDto import io.csbroker.apiserver.model.Notification +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.repository.common.NotificationRepository import io.csbroker.apiserver.repository.user.UserRepository import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional +import java.util.UUID @Transactional(readOnly = true) @Service @@ -40,19 +42,13 @@ class NotificationServiceImpl( ).size } - override fun getNotification(email: String, page: Pageable): Page { - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 에 해당하는 유저를 찾을 수 없습니다.") - - return notificationRepository.findByUserId(findUser.id!!, page) + override fun getNotification(userId: UUID, page: Pageable): Page { + return notificationRepository.findByUserId(userId, page) } @Transactional - override fun readNotifications(email: String, notificationIds: List) { - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 에 해당하는 유저를 찾을 수 없습니다.") - - val updatedCount = notificationRepository.setIsReadByIdIn(findUser.id!!, notificationIds) + override fun readNotifications(userId: UUID, notificationIds: List) { + val updatedCount = notificationRepository.setIsReadByIdIn(userId, notificationIds) if (updatedCount != notificationIds.size) { throw EntityNotFoundException("해당하는 알림을 찾을 수 없습니다.") @@ -60,30 +56,20 @@ class NotificationServiceImpl( } @Transactional - override fun readNotificationById(email: String, id: Long) { - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 에 해당하는 유저를 찾을 수 없습니다.") - - val updatedCount = notificationRepository.setIsReadById(findUser.id!!, id) - + override fun readNotificationById(userId: UUID, id: Long) { + val updatedCount = notificationRepository.setIsReadById(userId, id) if (updatedCount == 0) { throw EntityNotFoundException("해당하는 알림을 찾을 수 없습니다.") } } - override fun getUnreadNotificationCount(email: String): Long { - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 에 해당하는 유저를 찾을 수 없습니다.") - - return notificationRepository.countUnReadByUserId(findUser.id!!) + override fun getUnreadNotificationCount(userId: UUID): Long { + return notificationRepository.countUnReadByUserId(userId) } @Transactional - override fun deleteNotifications(email: String, ids: List) { - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 에 해당하는 유저를 찾을 수 없습니다.") - - val deletedCnt = notificationRepository.deleteAllByUserAndIdIn(findUser, ids) + override fun deleteNotifications(user: User, ids: List) { + val deletedCnt = notificationRepository.deleteAllByUserAndIdIn(user, ids) if (deletedCnt != ids.size) { throw EntityNotFoundException("해당하는 알림을 찾을 수 없습니다.") diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceImpl.kt index 09e4c746..fa6c160e 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceImpl.kt @@ -92,10 +92,7 @@ class CommonProblemServiceImpl( @Transactional override fun createChallenge(createChallengeDto: CreateChallengeDto) { - val (email, problemId, content) = createChallengeDto - - val user = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") + val (user, problemId, content) = createChallengeDto val problem = problemRepository.findByIdOrNull(problemId) ?: throw EntityNotFoundException("${problemId}번 문제는 존재하지 않는 문제입니다.") diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceImpl.kt index 3692ee23..5be4a50e 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceImpl.kt @@ -29,9 +29,7 @@ class MultipleProblemServiceImpl( gradingRequest: MultipleProblemGradingRequestDto, ): MultipleChoiceProblemGradingHistoryDto { // get entities - val (email, problemId, answerIds) = gradingRequest - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") + val (user, problemId, answerIds) = gradingRequest val findProblem = multipleChoiceProblemRepository.findByIdOrNull(problemId) ?: throw EntityNotFoundException("${problemId}번 문제는 존재하지 않는 서술형 문제입니다.") @@ -50,7 +48,7 @@ class MultipleProblemServiceImpl( val gradingHistory = gradingHistoryRepository.save( GradingHistory( problem = findProblem, - user = findUser, + user = user, userAnswer = answerIds.joinToString(","), score = score, ), diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/ShortProblemServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/ShortProblemServiceImpl.kt index 6d8cd66c..c0281e2b 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/ShortProblemServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/ShortProblemServiceImpl.kt @@ -28,9 +28,7 @@ class ShortProblemServiceImpl( @Transactional override fun gradingProblem(gradingRequest: ShortProblemGradingRequestDto): ShortProblemGradingHistoryDto { // get entities - val (email, problemId, answer) = gradingRequest - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") + val (user, problemId, answer) = gradingRequest val findProblem = shortProblemRepository.findByIdOrNull(problemId) ?: throw EntityNotFoundException("${problemId}번 문제는 존재하지 않는 서술형 문제입니다.") @@ -43,7 +41,7 @@ class ShortProblemServiceImpl( val gradingHistory = gradingHistoryRepository.save( GradingHistory( problem = findProblem, - user = findUser, + user = user, userAnswer = answer, score = score, ), diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/UserAnswerServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/UserAnswerServiceImpl.kt index e21cccd3..9d232a35 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/UserAnswerServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/UserAnswerServiceImpl.kt @@ -75,6 +75,7 @@ class UserAnswerServiceImpl( if (userAnswer.assignedUser == null) { throw EntityNotFoundException("${userAnswerId}번에 담당자가 존재하지 않습니다.") } + if (userAnswer.assignedUser!!.email != email) { throw EntityNotFoundException("${userAnswerId}번에 할당된 유저가 아닙니다.") } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemService.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemService.kt index 35c8b584..a8067fad 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemService.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemService.kt @@ -4,10 +4,11 @@ import io.csbroker.apiserver.dto.problem.AdminProblemSearchDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemResponseDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemSearchResponseDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemUpsertRequestDto +import io.csbroker.apiserver.model.User interface AdminLongProblemService { fun findProblems(problemSearchDto: AdminProblemSearchDto): LongProblemSearchResponseDto fun findProblemById(id: Long): LongProblemResponseDto - fun createProblem(createRequestDto: LongProblemUpsertRequestDto, email: String): Long - fun updateProblem(id: Long, updateRequestDto: LongProblemUpsertRequestDto, email: String): Long + fun createProblem(createRequestDto: LongProblemUpsertRequestDto, user: User): Long + fun updateProblem(id: Long, updateRequestDto: LongProblemUpsertRequestDto): Long } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceImpl.kt index 205b7e8b..7c663f34 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceImpl.kt @@ -6,6 +6,7 @@ import io.csbroker.apiserver.dto.problem.longproblem.LongProblemResponseDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemSearchResponseDto import io.csbroker.apiserver.dto.problem.longproblem.LongProblemUpsertRequestDto import io.csbroker.apiserver.model.StandardAnswer +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.repository.problem.GradingStandardRepository import io.csbroker.apiserver.repository.problem.LongProblemRepository import io.csbroker.apiserver.repository.problem.ProblemRepository @@ -44,10 +45,8 @@ class AdminLongProblemServiceImpl( } @Transactional - override fun createProblem(createRequestDto: LongProblemUpsertRequestDto, email: String): Long { - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") - val longProblem = createRequestDto.toLongProblem(findUser) + override fun createProblem(createRequestDto: LongProblemUpsertRequestDto, user: User): Long { + val longProblem = createRequestDto.toLongProblem(user) val gradingStandardList = createRequestDto.getGradingStandardList(longProblem) longProblem.addGradingStandards(gradingStandardList) @@ -67,10 +66,9 @@ class AdminLongProblemServiceImpl( } @Transactional - override fun updateProblem(id: Long, updateRequestDto: LongProblemUpsertRequestDto, email: String): Long { + override fun updateProblem(id: Long, updateRequestDto: LongProblemUpsertRequestDto): Long { val longProblem = longProblemRepository.findByIdOrNull(id) ?: throw EntityNotFoundException("${id}번 문제는 존재하지 않는 서술형 문제입니다.") - val gradingStandardList = updateRequestDto.getGradingStandardList(longProblem) if (longProblem.standardAnswers.map { it.content }.toSet() != updateRequestDto.standardAnswers.toSet()) { diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemService.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemService.kt index dfe11f76..00064f95 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemService.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemService.kt @@ -4,10 +4,11 @@ import io.csbroker.apiserver.dto.problem.AdminProblemSearchDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemResponseDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemSearchResponseDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemUpsertRequestDto +import io.csbroker.apiserver.model.User interface AdminMultipleProblemService { fun findProblems(problemSearchDto: AdminProblemSearchDto): MultipleChoiceProblemSearchResponseDto fun findProblemById(id: Long): MultipleChoiceProblemResponseDto - fun createProblem(createRequestDto: MultipleChoiceProblemUpsertRequestDto, email: String): Long - fun updateProblem(id: Long, updateRequestDto: MultipleChoiceProblemUpsertRequestDto, email: String): Long + fun createProblem(createRequestDto: MultipleChoiceProblemUpsertRequestDto, user: User): Long + fun updateProblem(id: Long, updateRequestDto: MultipleChoiceProblemUpsertRequestDto): Long } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemServiceImpl.kt index f5864464..077651fb 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemServiceImpl.kt @@ -7,6 +7,7 @@ import io.csbroker.apiserver.dto.problem.AdminProblemSearchDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemResponseDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemSearchResponseDto import io.csbroker.apiserver.dto.problem.multiplechoiceproblem.MultipleChoiceProblemUpsertRequestDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.repository.problem.ChoiceRepository import io.csbroker.apiserver.repository.problem.MultipleChoiceProblemRepository import io.csbroker.apiserver.repository.problem.ProblemRepository @@ -43,11 +44,8 @@ class AdminMultipleProblemServiceImpl( } @Transactional - override fun createProblem(createRequestDto: MultipleChoiceProblemUpsertRequestDto, email: String): Long { - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") - - val multipleChoiceProblem = createRequestDto.toMultipleChoiceProblem(findUser) + override fun createProblem(createRequestDto: MultipleChoiceProblemUpsertRequestDto, user: User): Long { + val multipleChoiceProblem = createRequestDto.toMultipleChoiceProblem(user) val choiceDataList = createRequestDto.getChoiceList(multipleChoiceProblem) tagUpserter.setTags(multipleChoiceProblem, createRequestDto.tags) @@ -62,7 +60,7 @@ class AdminMultipleProblemServiceImpl( } @Transactional - override fun updateProblem(id: Long, updateRequestDto: MultipleChoiceProblemUpsertRequestDto, email: String): Long { + override fun updateProblem(id: Long, updateRequestDto: MultipleChoiceProblemUpsertRequestDto): Long { val findProblem = multipleChoiceProblemRepository.findByIdOrNull(id) ?: throw EntityNotFoundException("${id}번 문제는 존재하지 않는 객관식 문제입니다.") diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemService.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemService.kt index 59704c6b..5f8ebf57 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemService.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemService.kt @@ -4,10 +4,11 @@ import io.csbroker.apiserver.dto.problem.AdminProblemSearchDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemResponseDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemSearchResponseDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemUpsertRequestDto +import io.csbroker.apiserver.model.User interface AdminShortProblemService { fun findProblems(problemSearchDto: AdminProblemSearchDto): ShortProblemSearchResponseDto fun findProblemById(id: Long): ShortProblemResponseDto - fun createProblem(createRequestDto: ShortProblemUpsertRequestDto, email: String): Long - fun updateProblem(id: Long, updateRequestDto: ShortProblemUpsertRequestDto, email: String): Long + fun createProblem(createRequestDto: ShortProblemUpsertRequestDto, user: User): Long + fun updateProblem(id: Long, updateRequestDto: ShortProblemUpsertRequestDto): Long } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemServiceImpl.kt index 1a7811eb..ca01c1d2 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemServiceImpl.kt @@ -5,6 +5,7 @@ import io.csbroker.apiserver.dto.problem.AdminProblemSearchDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemResponseDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemSearchResponseDto import io.csbroker.apiserver.dto.problem.shortproblem.ShortProblemUpsertRequestDto +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.repository.problem.ProblemRepository import io.csbroker.apiserver.repository.problem.ShortProblemRepository import io.csbroker.apiserver.repository.user.UserRepository @@ -39,18 +40,15 @@ class AdminShortProblemServiceImpl( } @Transactional - override fun createProblem(createRequestDto: ShortProblemUpsertRequestDto, email: String): Long { - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") - val shortProblem = createRequestDto.toShortProblem(findUser) - + override fun createProblem(createRequestDto: ShortProblemUpsertRequestDto, user: User): Long { + val shortProblem = createRequestDto.toShortProblem(user) tagUpserter.setTags(shortProblem, createRequestDto.tags) return problemRepository.save(shortProblem).id } @Transactional - override fun updateProblem(id: Long, updateRequestDto: ShortProblemUpsertRequestDto, email: String): Long { + override fun updateProblem(id: Long, updateRequestDto: ShortProblemUpsertRequestDto): Long { val findProblem = shortProblemRepository.findByIdOrNull(id) ?: throw EntityNotFoundException("${id}번 문제는 존재하지 않는 단답형 문제입니다.") diff --git a/src/main/kotlin/io/csbroker/apiserver/service/user/UserService.kt b/src/main/kotlin/io/csbroker/apiserver/service/user/UserService.kt index 2594f574..39458fcd 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/user/UserService.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/user/UserService.kt @@ -10,14 +10,14 @@ interface UserService { fun findUserById(uuid: UUID): User? - fun modifyUser(uuid: UUID, email: String, userUpdateRequestDto: UserUpdateRequestDto): User + fun modifyUser(uuid: UUID, user: User, userUpdateRequestDto: UserUpdateRequestDto): User fun findUsers(): List fun findAdminUsers(): List fun getStats(id: UUID): UserStatsDto - fun deleteUser(email: String, id: UUID): Boolean - fun updateUserProfileImg(email: String, imgUrl: String) + fun deleteUser(user: User, id: UUID): Boolean + fun updateUserProfileImg(user: User, imgUrl: String) fun calculateRank() } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/user/UserServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/user/UserServiceImpl.kt index 5ae0f178..a45141d1 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/user/UserServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/user/UserServiceImpl.kt @@ -35,24 +35,21 @@ class UserServiceImpl( } @Transactional - override fun modifyUser(uuid: UUID, email: String, userUpdateRequestDto: UserUpdateRequestDto): User { - val findUser = userRepository.findByIdOrNull(uuid) - ?: throw EntityNotFoundException("${uuid}를 가진 유저를 찾을 수 없습니다.") - - if (findUser.email != email) { - throw EntityNotFoundException("${email}을 가진 유저를 찾을 수 없습니다.") + override fun modifyUser(uuid: UUID, user: User, userUpdateRequestDto: UserUpdateRequestDto): User { + if (user.id != uuid) { + throw UnAuthorizedException(ErrorCode.FORBIDDEN, "해당 유저를 수정할 권한이 없습니다.") } userUpdateRequestDto.password?.let { - if (findUser.providerType != ProviderType.LOCAL) { + if (user.providerType != ProviderType.LOCAL) { throw ConditionConflictException(ErrorCode.CONDITION_NOT_FULFILLED, "간편 가입 유저는 비밀번호를 변경할 수 없습니다.") } - encodePassword(findUser, userUpdateRequestDto) + encodePassword(user, userUpdateRequestDto) } - findUser.updateInfo(userUpdateRequestDto) + user.updateInfo(userUpdateRequestDto) - return findUser + return user } private fun encodePassword(user: User, userUpdateRequestDto: UserUpdateRequestDto) { @@ -122,25 +119,21 @@ class UserServiceImpl( ) } - override fun deleteUser(email: String, id: UUID): Boolean { + override fun deleteUser(user: User, id: UUID): Boolean { val findUserById = findUserById(id) ?: throw EntityNotFoundException("${id}를 가진 유저를 찾을 수 없습니다.") - if (findUserById.email != email) { + if (user.role == Role.ROLE_ADMIN || user.id == findUserById.id) { + findUserById.isDeleted = true + return true + } else { throw UnAuthorizedException(ErrorCode.FORBIDDEN, "해당 유저를 삭제할 권한이 없습니다.") } - - findUserById.isDeleted = true - - return true } @Transactional - override fun updateUserProfileImg(email: String, imgUrl: String) { - val findUser = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("${email}를 가진 유저를 찾을 수 없습니다.") - - findUser.profileImageUrl = imgUrl + override fun updateUserProfileImg(user: User, imgUrl: String) { + user.profileImageUrl = imgUrl } @Transactional diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/RestDocsTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/RestDocsTest.kt index 10120b41..d063cbfa 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/RestDocsTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/RestDocsTest.kt @@ -4,7 +4,11 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature import com.fasterxml.jackson.module.kotlin.registerKotlinModule import io.csbroker.apiserver.auth.LoginUserArgumentResolver +import io.csbroker.apiserver.auth.ProviderType import io.csbroker.apiserver.common.enums.Role +import io.csbroker.apiserver.repository.user.UserRepository +import io.mockk.every +import io.mockk.mockk import io.restassured.config.ObjectMapperConfig import io.restassured.module.mockmvc.RestAssuredMockMvc import io.restassured.module.mockmvc.config.RestAssuredMockMvcConfig @@ -25,10 +29,21 @@ import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.setup.MockMvcBuilders import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder import org.springframework.web.method.support.HandlerMethodArgumentResolver +import java.util.UUID @ExtendWith(RestDocumentationExtension::class) abstract class RestDocsTest { private lateinit var restDocumentation: RestDocumentationContextProvider + private val userRepository: UserRepository = mockk { + every { findByEmail("admin@csbroker.io") } returns io.csbroker.apiserver.model.User( + id = UUID.randomUUID(), + email = "admin@csbroker.io", + password = "1234", + role = Role.ROLE_ADMIN, + providerType = ProviderType.LOCAL, + username = "admin", + ) + } @BeforeEach fun setUp(restDocumentation: RestDocumentationContextProvider) { @@ -40,7 +55,7 @@ abstract class RestDocsTest { controllerAdvices: Any = emptyArray(), argumentResolvers: Array = arrayOf( PageableHandlerMethodArgumentResolver(), - LoginUserArgumentResolver(), + LoginUserArgumentResolver(userRepository), ), httpMessageConverters: Array> = arrayOf( MappingJackson2HttpMessageConverter(objectMapper()), diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminLongProblemControllerTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminLongProblemControllerTest.kt index a335e868..26f4d898 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminLongProblemControllerTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminLongProblemControllerTest.kt @@ -88,7 +88,7 @@ class AdminLongProblemControllerTest : RestDocsTest() { @Test fun `Update Long Problem 200`() { // given - every { problemService.updateProblem(any(), any(), any()) } returns 1L + every { problemService.updateProblem(any(), any()) } returns 1L val longProblemUpsertRequestDto = createLongProblemUpsertRequestDto() // when diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminMultipleProblemControllerTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminMultipleProblemControllerTest.kt index 40462d32..ef0c60c3 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminMultipleProblemControllerTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminMultipleProblemControllerTest.kt @@ -117,7 +117,7 @@ class AdminMultipleProblemControllerTest : RestDocsTest() { ), 5.0, ) - every { problemService.updateProblem(any(), any(), any()) } returns 1L + every { problemService.updateProblem(any(), any()) } returns 1L // when val result = mockMvc.body(multipleChoiceProblemUpsertRequestDto) diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminShortProblemControllerTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminShortProblemControllerTest.kt index 9aded7d9..156fdcae 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminShortProblemControllerTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminShortProblemControllerTest.kt @@ -95,7 +95,7 @@ class AdminShortProblemControllerTest : RestDocsTest() { true, true, ) - every { problemService.updateProblem(any(), any(), any()) } returns 1L + every { problemService.updateProblem(any(), any()) } returns 1L // when val result = mockMvc.body(shortProblemUpsertRequestDto) diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemIntegrationTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemIntegrationTest.kt index 82816cd7..90bb93e4 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemIntegrationTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemIntegrationTest.kt @@ -47,17 +47,17 @@ class LongProblemIntegrationTest : IntegrationTest() { // given & when val preSubmissionCount = findAll( "SELECT gh From GradingHistory gh where gh.problem.id = :id", - mapOf("id" to problem.id!!), + mapOf("id" to problem.id), ).size val preUserSubmissionCount = findAll( "SELECT gh From GradingHistory gh where gh.problem.id = :problemId AND gh.user.id = :userId", - mapOf("problemId" to problem.id!!, "userId" to defaultUser.id!!), + mapOf("problemId" to problem.id, "userId" to defaultUser.id!!), ).size val standardAnswers = findAll( "SELECT sa From StandardAnswer sa where sa.longProblem.id = :id", - mapOf("id" to problem.id!!), + mapOf("id" to problem.id), ) val userAnswer = "answer" diff --git a/src/test/kotlin/io/csbroker/apiserver/service/UserServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/UserServiceTest.kt index 183b1d80..b37c0b0a 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/UserServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/UserServiceTest.kt @@ -1,9 +1,7 @@ package io.csbroker.apiserver.service import io.csbroker.apiserver.auth.ProviderType -import io.csbroker.apiserver.common.enums.ErrorCode import io.csbroker.apiserver.common.enums.Role -import io.csbroker.apiserver.common.exception.EntityNotFoundException import io.csbroker.apiserver.dto.user.UserUpdateRequestDto import io.csbroker.apiserver.model.User import io.csbroker.apiserver.repository.common.RedisRepository @@ -17,7 +15,6 @@ import io.mockk.verify import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows import org.springframework.security.crypto.password.PasswordEncoder import java.util.UUID @@ -123,31 +120,6 @@ class UserServiceTest { assertThat(adminUsers.size).isEqualTo(1) } - @Test - fun `유저 수정 ID 조회 불가 실패 테스트`() { - // given - val id = UUID.randomUUID() - every { userRepository.findByIdOrNull(any()) } returns null - - // when - val exception = assertThrows { - userService.modifyUser( - id, - "test@test.com", - UserUpdateRequestDto( - "test-url.com", - "test", - "test1234!", - "test1234", - ), - ) - } - - // then - verify(exactly = 1) { userRepository.findByIdOrNull(any()) } - assertThat(ErrorCode.NOT_FOUND_ENTITY).isEqualTo(exception.errorCode) - } - @Test fun `유저 수정 성공 without password 테스트`() { // given @@ -159,12 +131,11 @@ class UserServiceTest { // when val modifyUser = userService.modifyUser( id, - "test@test.com", + user, userUpdateRequestDto, ) // then - verify(exactly = 1) { userRepository.findByIdOrNull(any()) } assertThat("test-url.com").isEqualTo(modifyUser.profileImageUrl) assertThat("test").isEqualTo(modifyUser.username) assertThat(user.password).isEqualTo(modifyUser.password) @@ -183,12 +154,11 @@ class UserServiceTest { // when val modifyUser = userService.modifyUser( id, - "test@test.com", + user, userUpdateRequestDto, ) // then - verify(exactly = 1) { userRepository.findByIdOrNull(any()) } verify(exactly = 1) { passwordEncoder.encode(any()) } verify(exactly = 1) { passwordEncoder.matches(any(), any()) } assertThat("test-url.com").isEqualTo(modifyUser.profileImageUrl) diff --git a/src/test/kotlin/io/csbroker/apiserver/service/common/NotificationServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/common/NotificationServiceTest.kt index 5ebbd0c9..4a854a69 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/common/NotificationServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/common/NotificationServiceTest.kt @@ -12,7 +12,6 @@ import io.mockk.verify import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -import org.springframework.data.domain.PageRequest import java.util.UUID class NotificationServiceTest { @@ -46,104 +45,36 @@ class NotificationServiceTest { verify { userRepository.findByIdOrNull(req.userId) } } - @Test - fun `getNotification - 존재하지 않는 유저가 알림을 조회할 시 예외가 발생합니다`() { - // given - val email = "notExist@email.com" - val page = PageRequest.of(0, 10) - every { userRepository.findByEmail(email) } returns null - - // when & then - assertThrows { service.getNotification(email, page) } - verify { userRepository.findByEmail(email) } - } - - @Test - fun `readNotifications - 존재하지 않는 유저가 알림을 읽을 시 예외가 발생합니다`() { - // given - val email = "notExist@email.com" - val notificationIds = listOf(1L, 2L) - every { userRepository.findByEmail(email) } returns null - - // when & then - assertThrows { service.readNotifications(email, notificationIds) } - verify { userRepository.findByEmail(email) } - } - @Test fun `readNotifications - 존재하지 않는 알림을 읽을 시 예외가 발생합니다`() { // given - val email = user.email val notificationIds = listOf(1L, 2L) - every { userRepository.findByEmail(email) } returns user every { notificationRepository.setIsReadByIdIn(user.id!!, notificationIds) } returns 0 // when & then - assertThrows { service.readNotifications(email, notificationIds) } - verify { userRepository.findByEmail(email) } + assertThrows { service.readNotifications(user.id!!, notificationIds) } verify { notificationRepository.setIsReadByIdIn(user.id!!, notificationIds) } } - @Test - fun `readNotificationById - 존재하지 않는 유저가 알림을 읽을 시 예외가 발생합니다`() { - // given - val email = "notExist@email.com" - val notificationId = 1L - every { userRepository.findByEmail(email) } returns null - - // when & then - assertThrows { service.readNotificationById(email, notificationId) } - verify { userRepository.findByEmail(email) } - } - @Test fun `readNotificationById - 존재하지 않는 알림을 읽을 시 예외가 발생합니다`() { // given - val email = user.email val notificationId = 1L - every { userRepository.findByEmail(email) } returns user every { notificationRepository.setIsReadById(user.id!!, notificationId) } returns 0 // when & then - assertThrows { service.readNotificationById(email, notificationId) } - verify { userRepository.findByEmail(email) } + assertThrows { service.readNotificationById(user.id!!, notificationId) } verify { notificationRepository.setIsReadById(user.id!!, notificationId) } } - @Test - fun `getUnreadNotificationCount - 존재하지 않는 유저가 요청시 예외가 발생합니다`() { - // given - val email = "notExist@email.com" - every { userRepository.findByEmail(email) } returns null - - // when & then - assertThrows { service.getUnreadNotificationCount(email) } - verify { userRepository.findByEmail(email) } - } - - @Test - fun `deleteNotifications - 존재하지 않는 유저가 요청시 예외가 발생합니다`() { - // given - val email = "notExist@email.com" - val notificationIds = listOf(1L, 2L) - every { userRepository.findByEmail(email) } returns null - - // when & then - assertThrows { service.deleteNotifications(email, notificationIds) } - verify { userRepository.findByEmail(email) } - } - @Test fun `deleteNotifications - 존재하지 않는 알림에 대한 삭제 요청시 예외가 발생합니다`() { // given - val email = user.email val notificationIds = listOf(1L, 2L) - every { userRepository.findByEmail(email) } returns user every { notificationRepository.deleteAllByUserAndIdIn(user, notificationIds) } returns notificationIds.size - 1 // when & then - assertThrows { service.deleteNotifications(email, notificationIds) } - verify { userRepository.findByEmail(email) } + assertThrows { service.deleteNotifications(user, notificationIds) } verify { notificationRepository.deleteAllByUserAndIdIn(user, notificationIds) } } } diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceTest.kt index 8e7c5930..437416e3 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceTest.kt @@ -50,19 +50,6 @@ class CommonProblemServiceTest { ) } - @Test - fun `createChallenge - 존재하지 않는 유저가 이의제기를 할 시 예외가 발생합니다 `() { - // Arrange - every { userRepository.findByEmail(any()) } returns null - - // Act & Assert - assertThrows { - commonProblemService.createChallenge( - CreateChallengeDto(email = "test@test.com", problemId = 1L, content = "Test"), - ) - } - } - @Test fun `createChallenge should throw EntityNotFoundException when problem not found`() { // Arrange @@ -74,7 +61,7 @@ class CommonProblemServiceTest { // Act & Assert assertThrows { commonProblemService.createChallenge( - CreateChallengeDto(email = "test@test.com", problemId = 1L, content = "Test"), + CreateChallengeDto(user, problemId = 1L, content = "Test"), ) } } diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceTest.kt index 5e3402ea..4121b1f9 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/MultipleProblemServiceTest.kt @@ -65,34 +65,16 @@ class MultipleProblemServiceTest { assertEquals(responseDto, mockProblem.toDetailResponseDto(user.email)) } - @Test - fun `gradingProblem - 없는 유저가 문제를 제출시 예외가 발생합니다`() { - // given - val email = "test@test.com" - val problemId = 1L - val answerIds = listOf(1L, 2L) - val gradingRequest = MultipleProblemGradingRequestDto(email, problemId, answerIds) - every { userRepository.findByEmail(email) } returns null - - // when & then - assertThrows { service.gradingProblem(gradingRequest) } - verify { userRepository.findByEmail(email) } - } - @Test fun `gradingProblem - 존재하지 않는 문제에 대한 답안을 제출시 예외가 발생합니다`() { // given - val email = "test@test.com" val problemId = 1L val answerIds = listOf(1L, 2L) - val gradingRequest = MultipleProblemGradingRequestDto(email, problemId, answerIds) - - every { userRepository.findByEmail(email) } returns user + val gradingRequest = MultipleProblemGradingRequestDto(user, problemId, answerIds) every { multipleChoiceProblemRepository.findByIdOrNull(problemId) } returns null // when & then assertThrows { service.gradingProblem(gradingRequest) } - verify { userRepository.findByEmail(email) } verify { multipleChoiceProblemRepository.findByIdOrNull(problemId) } } @@ -104,7 +86,7 @@ class MultipleProblemServiceTest { val problemId = problem.id val answerId = problem.choicesList.first { it.isAnswer }.id val answerIds = listOf(answerId) - val gradingRequest = MultipleProblemGradingRequestDto(email, problemId, answerIds) + val gradingRequest = MultipleProblemGradingRequestDto(user, problemId, answerIds) val gradingHistory = GradingHistory( gradingHistoryId = 1L, problem = problem, @@ -129,7 +111,7 @@ class MultipleProblemServiceTest { val problemId = problem.id val answerId = problem.choicesList.first { !it.isAnswer }.id val answerIds = listOf(answerId) - val gradingRequest = MultipleProblemGradingRequestDto(email, problemId, answerIds) + val gradingRequest = MultipleProblemGradingRequestDto(user, problemId, answerIds) val gradingHistory = GradingHistory( gradingHistoryId = 1L, problem = problem, diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/ShortProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/ShortProblemServiceTest.kt index 88cb9d68..2edffdca 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/ShortProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/ShortProblemServiceTest.kt @@ -52,24 +52,6 @@ class ShortProblemServiceTest { verify { shortProblemRepository.findByIdOrNull(any()) } } - @Test - fun `gradingProblem - 없는 유저가 답안을 제출할 시 예외가 발생합니다`() { - // given - val email = user.email - val problemId = 1L - val answer = "answer" - val gradingRequest = ShortProblemGradingRequestDto( - email, - problemId, - answer, - ) - every { userRepository.findByEmail(email) } returns null - - // when & then - assertThrows { service.gradingProblem(gradingRequest) } - verify { userRepository.findByEmail(email) } - } - @Test fun `gradingProblem - 없는 문제에 답안을 제출할 시 예외가 발생합니다`() { // given @@ -77,16 +59,14 @@ class ShortProblemServiceTest { val problemId = 1L val answer = "answer" val gradingRequest = ShortProblemGradingRequestDto( - email, + user, problemId, answer, ) - every { userRepository.findByEmail(email) } returns user every { shortProblemRepository.findByIdOrNull(problemId) } returns null // when & then assertThrows { service.gradingProblem(gradingRequest) } - verify { userRepository.findByEmail(email) } verify { shortProblemRepository.findByIdOrNull(problemId) } } @@ -95,7 +75,7 @@ class ShortProblemServiceTest { // given val problem = createProblem() val gradingRequest = ShortProblemGradingRequestDto( - user.email, + user, problem.id, problem.answer, ) @@ -106,7 +86,6 @@ class ShortProblemServiceTest { userAnswer = problem.answer, score = problem.score, ) - every { userRepository.findByEmail(user.email) } returns user every { shortProblemRepository.findByIdOrNull(problem.id) } returns problem every { gradingHistoryRepository.save(any()) } returns gradingHistory @@ -115,7 +94,6 @@ class ShortProblemServiceTest { // then assertEquals(problem.score, result.score) - verify { userRepository.findByEmail(user.email) } verify { shortProblemRepository.findByIdOrNull(problem.id) } verify { gradingHistoryRepository.save(any()) } } @@ -126,7 +104,7 @@ class ShortProblemServiceTest { val problem = createProblem() val wrongAnswer = "wrongAnswer" val gradingRequest = ShortProblemGradingRequestDto( - user.email, + user, problem.id, wrongAnswer, ) @@ -137,7 +115,6 @@ class ShortProblemServiceTest { userAnswer = wrongAnswer, score = 10.0, ) - every { userRepository.findByEmail(user.email) } returns user every { shortProblemRepository.findByIdOrNull(problem.id) } returns problem every { gradingHistoryRepository.save(any()) } returns gradingHistory // when @@ -145,7 +122,6 @@ class ShortProblemServiceTest { // then assertEquals(0.0, result.score) - verify { userRepository.findByEmail(user.email) } verify { shortProblemRepository.findByIdOrNull(problem.id) } verify { gradingHistoryRepository.save(any()) } } diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceTest.kt index 94c29715..eb193b13 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceTest.kt @@ -94,36 +94,12 @@ class AdminLongProblemServiceTest { every { standardAnswerRepository.saveAll(emptyList()) } returns emptyList() // when - val result = adminLongProblemService.createProblem(createRequestDto, user.email) + val result = adminLongProblemService.createProblem(createRequestDto, user) // then assertEquals(longProblem.id, result) } - @Test - fun `존재하는 유저만이 문제를 생성할 수 있습니다`() { - // given - val createRequestDto = getLongProblemUpsertRequestDto() - every { userRepository.findByEmail(any()) } returns null - val notExistUserEmail = "email@email.com" - // when & then - assertThrows { - adminLongProblemService.createProblem(createRequestDto, notExistUserEmail) - } - } - - @Test - fun `존재하는 유저만이 문제 갱신을 할 수 있습니다`() { - // given - val id = 1L - every { longProblemRepository.findByIdOrNull(id) } returns null - - // when, then - assertThrows { - adminLongProblemService.updateProblem(id, getLongProblemUpsertRequestDto(), "test@test.com") - } - } - private fun getLongProblemUpsertRequestDto(): LongProblemUpsertRequestDto { return LongProblemUpsertRequestDto( title = "Test problem", diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemServiceTest.kt index 48f1c339..3f43d8d8 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminMultipleProblemServiceTest.kt @@ -139,24 +139,6 @@ class AdminMultipleProblemServiceTest { assertEquals(result.title, problem.title) } - @Test - fun `createProblem - 존재하지 않는 유저가 문제 생성할 시 예외 발생`() { - // given - val createRequestDto = MultipleChoiceProblemUpsertRequestDto( - title = "title", - description = "description", - tags = mutableListOf(), - choices = mutableListOf(), - score = 10.0, - ) - val email = "not@exist.email" - every { userRepository.findByEmail(email) } returns null - - // when & then - assertThrows { adminMultipleProblemService.createProblem(createRequestDto, email) } - verify { userRepository.findByEmail(email) } - } - @Test fun `createProblem - 정답은 항상 1개 이상 존재해야 한다`() { // given @@ -167,15 +149,13 @@ class AdminMultipleProblemServiceTest { choices = mutableListOf(), score = 10.0, ) - every { userRepository.findByEmail(user.email) } returns user every { tagUpserter.setTags(any(), any()) } just runs // when & then assertThrows { - adminMultipleProblemService.createProblem(createRequestDto, user.email) + adminMultipleProblemService.createProblem(createRequestDto, user) } - verify { userRepository.findByEmail(user.email) } verify { tagUpserter.setTags(any(), any()) } } @@ -189,13 +169,11 @@ class AdminMultipleProblemServiceTest { choices = mutableListOf(ChoiceData(content = "content", isAnswer = true)), score = 10.0, ) - every { userRepository.findByEmail(user.email) } returns user every { tagUpserter.setTags(any(), any()) } just runs every { problemRepository.save(any()) } returns problem // when & then - val result = adminMultipleProblemService.createProblem(createRequestDto, user.email) - verify { userRepository.findByEmail(user.email) } + val result = adminMultipleProblemService.createProblem(createRequestDto, user) verify { tagUpserter.setTags(any(), any()) } verify { problemRepository.save(any()) } assertEquals(problem.id, result) @@ -215,7 +193,7 @@ class AdminMultipleProblemServiceTest { // when & then assertThrows { - adminMultipleProblemService.updateProblem(1L, requestDto, user.email) + adminMultipleProblemService.updateProblem(1L, requestDto) } verify { multipleChoiceProblemRepository.findByIdOrNull(any()) } } @@ -234,7 +212,7 @@ class AdminMultipleProblemServiceTest { // when & then assertThrows { - adminMultipleProblemService.updateProblem(1L, requestDto, user.email) + adminMultipleProblemService.updateProblem(1L, requestDto) } verify { multipleChoiceProblemRepository.findByIdOrNull(problem.id) } diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemServiceTest.kt index e639180f..f6bdfc13 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminShortProblemServiceTest.kt @@ -127,35 +127,20 @@ class AdminShortProblemServiceTest { answer = "answer", score = 10.0, ) - val email = "test@test.com" - every { userRepository.findByEmail(email) } returns user every { problemRepository.save(any()) } returns problem every { tagUpserter.setTags(any(), any()) } just runs every { problemRepository.save(any()) } returns problem // when - val result = adminShortProblemService.createProblem(requestDto, email) + val result = adminShortProblemService.createProblem(requestDto, user) // then - verify { userRepository.findByEmail(email) } verify { problemRepository.save(any()) } verify { tagUpserter.setTags(any(), any()) } verify { problemRepository.save(any()) } assertEquals(problem.id, result) } - @Test - fun `createProblem - 존재하지 않는 유저가 문제를 생성하면 예외가 발생합니다`() { - // given - every { userRepository.findByEmail(any()) } returns null - - // when & then - assertThrows { - adminShortProblemService.createProblem(mockk(), "email") - } - verify { userRepository.findByEmail(any()) } - } - @Test fun `updateProblem - success`() { // given @@ -171,7 +156,7 @@ class AdminShortProblemServiceTest { every { tagUpserter.updateTags(any(), any()) } just runs // when - val result = adminShortProblemService.updateProblem(1L, requestDto, email) + val result = adminShortProblemService.updateProblem(1L, requestDto) // then verify { shortProblemRepository.findByIdOrNull(any()) } @@ -186,7 +171,7 @@ class AdminShortProblemServiceTest { // when & then assertThrows { - adminShortProblemService.updateProblem(1L, mockk(), "email") + adminShortProblemService.updateProblem(1L, mockk()) } verify { shortProblemRepository.findByIdOrNull(any()) } } From 934fa5b8792f14e8b0fd6b9b888a5a006c090704 Mon Sep 17 00:00:00 2001 From: Jaewon Min <67869514+ekzm8523@users.noreply.github.com> Date: Sun, 29 Oct 2023 01:34:19 +0900 Subject: [PATCH 3/4] =?UTF-8?q?refactor:=20ArgumentResolver=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20(#132)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: remove unnecessary code * remove unused comments --------- Co-authored-by: kshired --- .../controller/v1/common/AuthController.kt | 3 +-- .../controller/v1/post/CommentController.kt | 4 ++-- .../controller/v1/post/PostController.kt | 6 ++--- .../v1/problem/LongProblemController.kt | 8 ++----- .../v2/problem/ProblemControllerV2.kt | 4 ++-- .../problem/request/SubmitLongProblemDto.kt | 4 +++- .../apiserver/dto/user/UserInfoResponseDto.kt | 9 ++++++++ .../apiserver/service/post/CommentService.kt | 6 +++-- .../service/post/CommentServiceImpl.kt | 7 +++--- .../apiserver/service/post/PostService.kt | 7 +++--- .../apiserver/service/post/PostServiceImpl.kt | 12 ++++------ .../service/problem/CommonProblemService.kt | 5 ++-- .../problem/CommonProblemServiceImpl.kt | 11 +++------ .../service/problem/LongProblemServiceImpl.kt | 4 +--- .../v1/common/AuthControllerTest.kt | 11 --------- .../problem/CommonProblemServiceTest.kt | 5 ---- .../service/problem/LongProblemServiceTest.kt | 23 +++---------------- .../admin/AdminLongProblemServiceTest.kt | 1 - 18 files changed, 47 insertions(+), 83 deletions(-) diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/AuthController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/AuthController.kt index 0ff3b5ef..183508fd 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/AuthController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/common/AuthController.kt @@ -42,8 +42,7 @@ class AuthController( @GetMapping("/info") fun getUserInfo(@LoginUser loginUser: User): ApiResponse { - val userInfo = authService.getUserInfo(loginUser.username) - return ApiResponse.success(UserInfoResponseDto(userInfo)) + return ApiResponse.success(UserInfoResponseDto(loginUser)) } @PostMapping("/login") diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt index 8add0408..ef454722 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt @@ -20,7 +20,7 @@ class CommentController( @LoginUser user: User, @PathVariable id: Long, ): ApiResponse { - commentService.deleteById(id, user.username) + commentService.deleteById(id, user) return ApiResponse.success() } @@ -32,7 +32,7 @@ class CommentController( val postId = commentService.create( commentCreateRequestDto.postId, commentCreateRequestDto.content, - loginUser.username, + loginUser, ) return ApiResponse.success(postId) } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt index 258cb5fc..58724e52 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt @@ -26,7 +26,7 @@ class PostController( val postId = postService.create( postCreateRequestDto.problemId, postCreateRequestDto.content, - loginUser.username, + loginUser, ) return ApiResponse.success(postId) } @@ -36,7 +36,7 @@ class PostController( @LoginUser loginUser: User, @PathVariable("id") id: Long, ): ApiResponse { - postService.deleteById(id, loginUser.username) + postService.deleteById(id, loginUser) return ApiResponse.success() } @@ -53,7 +53,7 @@ class PostController( @LoginUser loginUser: User, @PathVariable("id") id: Long, ): ApiResponse { - postService.like(id, loginUser.username) + postService.like(id, loginUser) return ApiResponse.success() } } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemController.kt index 4ae6f094..87abbc67 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/problem/LongProblemController.kt @@ -56,13 +56,9 @@ class LongProblemController( @PathVariable("id") problemId: Long, @RequestBody answerDto: LongProblemAnswerDto, ): ApiResponse { - val submitRequestDto = SubmitLongProblemDto(loginUser.email, problemId, answerDto.answer) + val submitRequestDto = SubmitLongProblemDto(loginUser, problemId, answerDto.answer) val submitResponseDto = longProblemService.submitProblem(submitRequestDto) - postService.create( - problemId = submitRequestDto.problemId, - content = answerDto.answer, - email = loginUser.email, - ) + postService.create(submitRequestDto.problemId, answerDto.answer, loginUser) // Todo : LongProblemService 내부로 보내기 return ApiResponse.success(submitResponseDto) } } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/ProblemControllerV2.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/ProblemControllerV2.kt index a298344b..238ac216 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/ProblemControllerV2.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/ProblemControllerV2.kt @@ -35,7 +35,7 @@ class ProblemControllerV2( @LoginUser loginUser: User, @PathVariable("id") id: Long, ): ApiResponse { - commonProblemService.likeProblem(loginUser.username, id) + commonProblemService.likeProblem(loginUser, id) return ApiResponse.success() } @@ -44,7 +44,7 @@ class ProblemControllerV2( @LoginUser loginUser: User, @PathVariable("id") id: Long, ): ApiResponse { - commonProblemService.bookmarkProblem(loginUser.username, id) + commonProblemService.bookmarkProblem(loginUser, id) return ApiResponse.success() } } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/request/SubmitLongProblemDto.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/request/SubmitLongProblemDto.kt index df5dffab..4ffa6d9a 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/request/SubmitLongProblemDto.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v2/problem/request/SubmitLongProblemDto.kt @@ -1,7 +1,9 @@ package io.csbroker.apiserver.controller.v2.problem.request +import io.csbroker.apiserver.model.User + data class SubmitLongProblemDto( - val email: String, + val user: User, val problemId: Long, val answer: String, ) diff --git a/src/main/kotlin/io/csbroker/apiserver/dto/user/UserInfoResponseDto.kt b/src/main/kotlin/io/csbroker/apiserver/dto/user/UserInfoResponseDto.kt index 8402f6ee..4ed45ca5 100644 --- a/src/main/kotlin/io/csbroker/apiserver/dto/user/UserInfoResponseDto.kt +++ b/src/main/kotlin/io/csbroker/apiserver/dto/user/UserInfoResponseDto.kt @@ -2,6 +2,7 @@ package io.csbroker.apiserver.dto.user import com.fasterxml.jackson.annotation.JsonInclude import io.csbroker.apiserver.common.enums.Role +import io.csbroker.apiserver.model.User import java.util.UUID @JsonInclude(JsonInclude.Include.NON_NULL) @@ -19,4 +20,12 @@ data class UserInfoResponseDto( userInfoDto.role, userInfoDto.accessToken, ) + + constructor(user: User, accessToken: String? = null) : this( + user.id!!, + user.username, + user.email, + user.role, + accessToken, + ) } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/post/CommentService.kt b/src/main/kotlin/io/csbroker/apiserver/service/post/CommentService.kt index 124baa6d..a81cf348 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/post/CommentService.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/post/CommentService.kt @@ -1,6 +1,8 @@ package io.csbroker.apiserver.service.post +import io.csbroker.apiserver.model.User + interface CommentService { - fun create(postId: Long, content: String, email: String): Long - fun deleteById(id: Long, email: String) + fun create(postId: Long, content: String, user: User): Long + fun deleteById(id: Long, user: User) } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/post/CommentServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/post/CommentServiceImpl.kt index e9f4dcb2..54c53954 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/post/CommentServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/post/CommentServiceImpl.kt @@ -3,6 +3,7 @@ package io.csbroker.apiserver.service.post import io.csbroker.apiserver.common.enums.ErrorCode import io.csbroker.apiserver.common.exception.UnAuthorizedException import io.csbroker.apiserver.model.Comment +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.repository.post.CommentRepository import io.csbroker.apiserver.repository.post.PostRepository import io.csbroker.apiserver.repository.user.UserRepository @@ -19,8 +20,7 @@ class CommentServiceImpl( private val userRepository: UserRepository, ) : CommentService { @Transactional - override fun create(postId: Long, content: String, email: String): Long { - val user = userRepository.findByEmail(email) ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") + override fun create(postId: Long, content: String, user: User): Long { val post = postRepository.findByIdOrNull(postId) ?: throw EntityNotFoundException( "${postId}번 답변은 존재하지 않는 답변입니다", ) @@ -29,8 +29,7 @@ class CommentServiceImpl( } @Transactional - override fun deleteById(id: Long, email: String) { - val user = userRepository.findByEmail(email) ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") + override fun deleteById(id: Long, user: User) { val comment = commentRepository.findByIdOrNull(id) ?: throw EntityNotFoundException("${id}번 답변은 존재하지 않는 답변입니다") if (comment.user != user) { diff --git a/src/main/kotlin/io/csbroker/apiserver/service/post/PostService.kt b/src/main/kotlin/io/csbroker/apiserver/service/post/PostService.kt index 93d73942..90147256 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/post/PostService.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/post/PostService.kt @@ -1,10 +1,11 @@ package io.csbroker.apiserver.service.post import io.csbroker.apiserver.controller.v1.post.response.PostResponseDto +import io.csbroker.apiserver.model.User interface PostService { fun findByProblemId(problemId: Long, email: String?): List - fun create(problemId: Long, content: String, email: String): Long - fun like(id: Long, email: String) - fun deleteById(id: Long, email: String) + fun create(problemId: Long, content: String, user: User): Long + fun like(id: Long, user: User) + fun deleteById(id: Long, user: User) } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/post/PostServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/post/PostServiceImpl.kt index a23111f7..7170b399 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/post/PostServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/post/PostServiceImpl.kt @@ -7,11 +7,11 @@ import io.csbroker.apiserver.controller.v1.post.response.CommentResponseDto import io.csbroker.apiserver.controller.v1.post.response.PostResponseDto import io.csbroker.apiserver.model.Post import io.csbroker.apiserver.model.PostLike +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.repository.post.CommentRepository import io.csbroker.apiserver.repository.post.PostLikeRepository import io.csbroker.apiserver.repository.post.PostRepository import io.csbroker.apiserver.repository.problem.ProblemRepository -import io.csbroker.apiserver.repository.user.UserRepository import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -22,7 +22,6 @@ class PostServiceImpl( private val problemRepository: ProblemRepository, private val postRepository: PostRepository, private val postLikeRepository: PostLikeRepository, - private val userRepository: UserRepository, private val commentRepository: CommentRepository, ) : PostService { override fun findByProblemId(problemId: Long, email: String?): List { @@ -47,8 +46,7 @@ class PostServiceImpl( } @Transactional - override fun create(problemId: Long, content: String, email: String): Long { - val user = userRepository.findByEmail(email) ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") + override fun create(problemId: Long, content: String, user: User): Long { val problem = problemRepository.findByIdOrNull(problemId) ?: throw EntityNotFoundException( "${problemId}번 문제는 존재하지 않는 문제입니다", ) @@ -57,9 +55,8 @@ class PostServiceImpl( } @Transactional - override fun like(id: Long, email: String) { + override fun like(id: Long, user: User) { val post = postRepository.findByIdOrNull(id) ?: throw EntityNotFoundException("${id}번 답변은 존재하지 않는 답변입니다") - val user = userRepository.findByEmail(email) ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") val postLike = postLikeRepository.findByPostAndUser(post, user) if (postLike == null) { postLikeRepository.save(PostLike(post = post, user = user)) @@ -69,8 +66,7 @@ class PostServiceImpl( } @Transactional - override fun deleteById(id: Long, email: String) { - val user = userRepository.findByEmail(email) ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") + override fun deleteById(id: Long, user: User) { val post = postRepository.findByIdOrNull(id) ?: throw EntityNotFoundException("${id}번 답변은 존재하지 않는 답변입니다") if (post.user != user) { diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemService.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemService.kt index d864ffd7..8c8960b4 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemService.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemService.kt @@ -5,6 +5,7 @@ import io.csbroker.apiserver.dto.problem.ProblemSearchDto import io.csbroker.apiserver.dto.problem.ProblemsResponseDto import io.csbroker.apiserver.dto.problem.challenge.CreateChallengeDto import io.csbroker.apiserver.dto.problem.grade.AssessmentRequestDto +import io.csbroker.apiserver.model.User interface CommonProblemService { fun findProblems(problemSearchDto: ProblemSearchDto): ProblemPageResponseDto @@ -13,6 +14,6 @@ interface CommonProblemService { fun removeProblemsById(ids: List) fun gradingAssessment(email: String, gradingHistoryId: Long, assessmentRequestDto: AssessmentRequestDto): Long fun createChallenge(createChallengeDto: CreateChallengeDto) - fun likeProblem(email: String, problemId: Long) - fun bookmarkProblem(email: String, problemId: Long) + fun likeProblem(user: User, problemId: Long) + fun bookmarkProblem(user: User, problemId: Long) } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceImpl.kt index fa6c160e..d3aef03e 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceImpl.kt @@ -12,13 +12,13 @@ import io.csbroker.apiserver.dto.problem.grade.AssessmentRequestDto import io.csbroker.apiserver.model.Challenge import io.csbroker.apiserver.model.ProblemBookmark import io.csbroker.apiserver.model.ProblemLike +import io.csbroker.apiserver.model.User import io.csbroker.apiserver.repository.problem.ChallengeRepository import io.csbroker.apiserver.repository.problem.GradingHistoryRepository import io.csbroker.apiserver.repository.problem.GradingResultAssessmentRepository import io.csbroker.apiserver.repository.problem.ProblemBookmarkRepository import io.csbroker.apiserver.repository.problem.ProblemLikeRepository import io.csbroker.apiserver.repository.problem.ProblemRepository -import io.csbroker.apiserver.repository.user.UserRepository import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -29,7 +29,6 @@ class CommonProblemServiceImpl( private val problemRepository: ProblemRepository, private val gradingHistoryRepository: GradingHistoryRepository, private val gradingResultAssessmentRepository: GradingResultAssessmentRepository, - private val userRepository: UserRepository, private val challengeRepository: ChallengeRepository, private val problemLikeRepository: ProblemLikeRepository, private val problemBookmarkRepository: ProblemBookmarkRepository, @@ -107,11 +106,9 @@ class CommonProblemServiceImpl( } @Transactional - override fun likeProblem(email: String, problemId: Long) { + override fun likeProblem(user: User, problemId: Long) { val problem = problemRepository.findByIdOrNull(problemId) ?: throw EntityNotFoundException("${problemId}번 문제는 존재하지 않는 문제입니다.") - val user = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") val problemLike = problemLikeRepository.findByUserAndProblem(user, problem) if (problemLike == null) { problemLikeRepository.save(ProblemLike(user = user, problem = problem)) @@ -121,11 +118,9 @@ class CommonProblemServiceImpl( } @Transactional - override fun bookmarkProblem(email: String, problemId: Long) { + override fun bookmarkProblem(user: User, problemId: Long) { val problem = problemRepository.findByIdOrNull(problemId) ?: throw EntityNotFoundException("${problemId}번 문제는 존재하지 않는 문제입니다.") - val user = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") val problemBookmark = problemBookmarkRepository.findByUserAndProblem(user, problem) if (problemBookmark == null) { problemBookmarkRepository.save(ProblemBookmark(user = user, problem = problem)) diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceImpl.kt index cb831cc4..b7801471 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceImpl.kt @@ -142,9 +142,7 @@ class LongProblemServiceImpl( @Transactional override fun submitProblem(submitRequest: SubmitLongProblemDto): SubmitLongProblemResponseDto { - val (email, problemId, answer) = submitRequest - val user = userRepository.findByEmail(email) - ?: throw EntityNotFoundException("$email 을 가진 유저는 존재하지 않습니다.") + val (user, problemId, answer) = submitRequest val problem = longProblemRepository.findByIdOrNull(problemId) ?: throw EntityNotFoundException("${problemId}번 문제는 존재하지 않는 서술형 문제입니다.") diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/common/AuthControllerTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/common/AuthControllerTest.kt index 06cd16fe..0f17414e 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/v1/common/AuthControllerTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/common/AuthControllerTest.kt @@ -176,16 +176,6 @@ class AuthControllerTest : RestDocsTest() { @Test fun `Get userInfo v1 200 OK`() { - // given - every { authService.getUserInfo(any()) } returns UserInfoDto( - id = UUID.randomUUID(), - username = "seongil-kim", - email = "seongil.kim@gmail.com", - role = Role.ROLE_USER, - accessToken = "accessToken", - refreshToken = "refreshToken", - ) - // when val result = mockMvc.request(Method.GET, "$AUTH_ENDPOINT/info") @@ -205,7 +195,6 @@ class AuthControllerTest : RestDocsTest() { fieldWithPath("data.username").type(JsonFieldType.STRING).description("닉네임"), fieldWithPath("data.email").type(JsonFieldType.STRING).description("이메일"), fieldWithPath("data.role").type(JsonFieldType.STRING).description("권한"), - fieldWithPath("data.accessToken").type(JsonFieldType.STRING).description("Access 토큰 (JWT)"), ), ), ) diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceTest.kt index 437416e3..39abf8ad 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/CommonProblemServiceTest.kt @@ -10,7 +10,6 @@ import io.csbroker.apiserver.repository.problem.GradingResultAssessmentRepositor import io.csbroker.apiserver.repository.problem.ProblemBookmarkRepository import io.csbroker.apiserver.repository.problem.ProblemLikeRepository import io.csbroker.apiserver.repository.problem.ProblemRepository -import io.csbroker.apiserver.repository.user.UserRepository import io.mockk.every import io.mockk.mockk import org.junit.jupiter.api.BeforeEach @@ -24,7 +23,6 @@ class CommonProblemServiceTest { private val problemRepository = mockk() private val gradingHistoryRepository = mockk() private val gradingResultAssessmentRepository = mockk() - private val userRepository = mockk() private val challengeRepository = mockk() private val problemLikeRepository = mockk() private val problemBookmarkRepository = mockk() @@ -43,7 +41,6 @@ class CommonProblemServiceTest { problemRepository, gradingHistoryRepository, gradingResultAssessmentRepository, - userRepository, challengeRepository, problemLikeRepository, problemBookmarkRepository, @@ -54,8 +51,6 @@ class CommonProblemServiceTest { fun `createChallenge should throw EntityNotFoundException when problem not found`() { // Arrange // Define necessary mock for user here - every { userRepository.findByEmail(any()) } returns user - every { problemRepository.findByIdOrNull(any()) } returns null // Act & Assert diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceTest.kt index 570b32a7..408f98c9 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/LongProblemServiceTest.kt @@ -53,27 +53,14 @@ class LongProblemServiceTest { ) } - @Test - fun `submitProblem - 존재하지 않는 유저가 제출시 예외가 발생합니다`() { - // given - val submitRequest = SubmitLongProblemDto(email = "test@test.com", problemId = 1L, answer = "test answer") - every { userRepository.findByEmail(any()) } returns null - - // when & then - assertThrows { longProblemService.submitProblem(submitRequest) } - verify { userRepository.findByEmail(any()) } - } - @Test fun `submitProblem - 존재하지 않는 문제에 대한 답안을 제출할 시 예외가 발생합니다`() { // given - val submitRequest = SubmitLongProblemDto(email = "test@test.com", problemId = 1L, answer = "test answer") - every { userRepository.findByEmail("test@test.com") } returns user + val submitRequest = SubmitLongProblemDto(user = user, problemId = 1L, answer = "test answer") every { longProblemRepository.findByIdOrNull(1L) } returns null // when & then assertThrows { longProblemService.submitProblem(submitRequest) } - verify { userRepository.findByEmail("test@test.com") } verify { longProblemRepository.findByIdOrNull(1L) } } @@ -83,7 +70,7 @@ class LongProblemServiceTest { val email = "test@test.com" val problemId = 1L val answer = "test answer" - val submitRequest = SubmitLongProblemDto(email, problemId, answer) + val submitRequest = SubmitLongProblemDto(user, problemId, answer) val title = "test problem" val description = "test description" val longProblem = LongProblem( @@ -92,7 +79,6 @@ class LongProblemServiceTest { description = description, ) val userAnswer = UserAnswer(answer = answer, problem = longProblem) - every { userRepository.findByEmail(email) } returns user every { longProblemRepository.findByIdOrNull(problemId) } returns longProblem every { userAnswerRepository.save(any()) } returns userAnswer every { standardAnswerRepository.findAllByLongProblem(longProblem) } returns emptyList() @@ -100,7 +86,6 @@ class LongProblemServiceTest { // when && then assertThrows { longProblemService.submitProblem(submitRequest) } - verify { userRepository.findByEmail(email) } verify { longProblemRepository.findByIdOrNull(problemId) } verify { userAnswerRepository.save(any()) } verify { standardAnswerRepository.findAllByLongProblem(longProblem) } @@ -112,7 +97,7 @@ class LongProblemServiceTest { val email = "test@test.com" val problemId = 1L val answer = "test answer" - val submitRequest = SubmitLongProblemDto(email, problemId, answer) + val submitRequest = SubmitLongProblemDto(user, problemId, answer) val title = "test problem" val description = "test description" val content = "std content" @@ -123,7 +108,6 @@ class LongProblemServiceTest { ) val standardAnswer = StandardAnswer(content = "std content", longProblem = longProblem) val userAnswer = UserAnswer(answer = answer, problem = longProblem) - every { userRepository.findByEmail(email) } returns user every { longProblemRepository.findByIdOrNull(problemId) } returns longProblem every { userAnswerRepository.save(any()) } returns userAnswer every { standardAnswerRepository.findAllByLongProblem(longProblem) } returns listOf(standardAnswer) @@ -138,7 +122,6 @@ class LongProblemServiceTest { assertEquals(answer, result.userAnswer) assertEquals(content, result.standardAnswer) - verify { userRepository.findByEmail(email) } verify { longProblemRepository.findByIdOrNull(problemId) } verify { userAnswerRepository.save(any()) } verify { standardAnswerRepository.findAllByLongProblem(longProblem) } diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceTest.kt index eb193b13..63f144b7 100644 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/service/problem/admin/AdminLongProblemServiceTest.kt @@ -88,7 +88,6 @@ class AdminLongProblemServiceTest { val createRequestDto = getLongProblemUpsertRequestDto() val longProblem = createRequestDto.toLongProblem(user) longProblem.id = 1L - every { userRepository.findByEmail(any()) } returns user every { problemRepository.save(any()) } returns longProblem every { tagUpserter.setTags(any(), any()) } just runs every { standardAnswerRepository.saveAll(emptyList()) } returns emptyList() From 61bb4aef22a8daefef5fe48505245b618e451eb4 Mon Sep 17 00:00:00 2001 From: Jaewon Min <67869514+ekzm8523@users.noreply.github.com> Date: Sun, 12 Nov 2023 19:51:51 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat=20:=20=EB=8C=93=EA=B8=80=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#133)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Like 테이블 추가 * feat: Comment Like 추가 * Like 테이블 추가 * PostLike -> Like로 변경 * Comment Like 추가 * 약간의 리팩토링.. * test: test fix * refactor: kshired 리뷰 반영 * fix: QueryCreationException 해결 --- .../apiserver/common/enums/LikeType.kt | 5 + .../controller/v1/post/CommentController.kt | 9 ++ .../controller/v1/post/PostController.kt | 4 +- .../v1/post/response/CommentResponseDto.kt | 22 ++++ .../v1/post/response/PostResponseDto.kt | 20 +--- .../io/csbroker/apiserver/model/Like.kt | 35 ++++++ .../io/csbroker/apiserver/model/Post.kt | 5 + .../repository/post/LikeRepository.kt | 26 +++++ .../repository/post/PostRepository.kt | 12 ++- .../apiserver/service/post/CommentService.kt | 1 + .../service/post/CommentServiceImpl.kt | 15 ++- .../apiserver/service/post/PostService.kt | 3 +- .../apiserver/service/post/PostServiceImpl.kt | 100 +++++++++++++----- .../controller/v1/post/PostControllerTest.kt | 8 +- 14 files changed, 213 insertions(+), 52 deletions(-) create mode 100644 src/main/kotlin/io/csbroker/apiserver/common/enums/LikeType.kt create mode 100644 src/main/kotlin/io/csbroker/apiserver/controller/v1/post/response/CommentResponseDto.kt create mode 100644 src/main/kotlin/io/csbroker/apiserver/model/Like.kt create mode 100644 src/main/kotlin/io/csbroker/apiserver/repository/post/LikeRepository.kt diff --git a/src/main/kotlin/io/csbroker/apiserver/common/enums/LikeType.kt b/src/main/kotlin/io/csbroker/apiserver/common/enums/LikeType.kt new file mode 100644 index 00000000..f6ad8f92 --- /dev/null +++ b/src/main/kotlin/io/csbroker/apiserver/common/enums/LikeType.kt @@ -0,0 +1,5 @@ +package io.csbroker.apiserver.common.enums + +enum class LikeType { + POST, COMMENT +} diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt index ef454722..456835bb 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/CommentController.kt @@ -36,4 +36,13 @@ class CommentController( ) return ApiResponse.success(postId) } + + @PostMapping("/api/v1/comments/{id}/like") + fun likeComment( + @LoginUser loginUser: User, + @PathVariable("id") id: Long, + ): ApiResponse { + commentService.like(id, loginUser) + return ApiResponse.success() + } } diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt index 58724e52..f8b226fa 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/PostController.kt @@ -44,8 +44,8 @@ class PostController( fun findAllByProblemId( @PathVariable("problemId") id: Long, ): ApiResponse> { - val email = getEmailFromSecurityContextHolder() - return ApiResponse.success(postService.findByProblemId(id, email)) + val nullableEmail = getEmailFromSecurityContextHolder() + return ApiResponse.success(postService.findByProblemId(id, nullableEmail)) } @PostMapping("/api/v1/posts/{id}/like") diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/response/CommentResponseDto.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/response/CommentResponseDto.kt new file mode 100644 index 00000000..d0a67c19 --- /dev/null +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/response/CommentResponseDto.kt @@ -0,0 +1,22 @@ +package io.csbroker.apiserver.controller.v1.post.response + +import io.csbroker.apiserver.model.Comment +import java.time.LocalDateTime + +data class CommentResponseDto( + val id: Long, + val content: String, + val username: String, + val likeCount: Long, + val isLiked: Boolean, + val createdAt: LocalDateTime, +) { + constructor(comment: Comment, likeCount: Long, isLiked: Boolean) : this( + id = comment.id, + content = comment.content, + username = comment.user.username, + likeCount = likeCount, + isLiked = isLiked, + createdAt = comment.createdAt!!, + ) +} diff --git a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/response/PostResponseDto.kt b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/response/PostResponseDto.kt index 4cd7eb39..bbf86e93 100644 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/response/PostResponseDto.kt +++ b/src/main/kotlin/io/csbroker/apiserver/controller/v1/post/response/PostResponseDto.kt @@ -1,18 +1,16 @@ package io.csbroker.apiserver.controller.v1.post.response -import io.csbroker.apiserver.model.Comment import io.csbroker.apiserver.model.Post -import java.time.LocalDateTime data class PostResponseDto( val id: Long, val content: String, val username: String, - val likeCount: Long, + val likeCount: Int, val isLiked: Boolean, val comments: List, ) { - constructor(post: Post, likeCount: Long, isLiked: Boolean, comments: List) : this( + constructor(post: Post, likeCount: Int, isLiked: Boolean, comments: List) : this( id = post.id, content = post.content, username = post.user.username, @@ -21,17 +19,3 @@ data class PostResponseDto( comments = comments, ) } - -data class CommentResponseDto( - val id: Long, - val content: String, - val username: String, - val createdAt: LocalDateTime, -) { - constructor(comment: Comment) : this( - id = comment.id, - content = comment.content, - username = comment.user.username, - createdAt = comment.createdAt!!, - ) -} diff --git a/src/main/kotlin/io/csbroker/apiserver/model/Like.kt b/src/main/kotlin/io/csbroker/apiserver/model/Like.kt new file mode 100644 index 00000000..f9296e01 --- /dev/null +++ b/src/main/kotlin/io/csbroker/apiserver/model/Like.kt @@ -0,0 +1,35 @@ +package io.csbroker.apiserver.model + +import io.csbroker.apiserver.common.enums.LikeType +import javax.persistence.Column +import javax.persistence.Entity +import javax.persistence.EnumType +import javax.persistence.Enumerated +import javax.persistence.FetchType +import javax.persistence.GeneratedValue +import javax.persistence.GenerationType +import javax.persistence.Id +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne +import javax.persistence.Table + +@Entity +@Table(name = "like") +class Like( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "like_id") + val id: Long = 0, + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") + val user: User, + + @Enumerated(EnumType.STRING) + @Column(name = "like_type") + val type: LikeType, + + @Column(name = "target_id") + val targetId: Long, + +) : BaseEntity() diff --git a/src/main/kotlin/io/csbroker/apiserver/model/Post.kt b/src/main/kotlin/io/csbroker/apiserver/model/Post.kt index d2aabe92..1fbf7e42 100644 --- a/src/main/kotlin/io/csbroker/apiserver/model/Post.kt +++ b/src/main/kotlin/io/csbroker/apiserver/model/Post.kt @@ -8,6 +8,7 @@ import javax.persistence.GenerationType import javax.persistence.Id import javax.persistence.JoinColumn import javax.persistence.ManyToOne +import javax.persistence.OneToMany import javax.persistence.Table @Entity @@ -28,4 +29,8 @@ class Post( @Column(name = "content", columnDefinition = "VARCHAR(300)") val content: String, + + @OneToMany(mappedBy = "post", fetch = FetchType.LAZY) + val comments: List = listOf(), + ) : BaseEntity() diff --git a/src/main/kotlin/io/csbroker/apiserver/repository/post/LikeRepository.kt b/src/main/kotlin/io/csbroker/apiserver/repository/post/LikeRepository.kt new file mode 100644 index 00000000..3498f76b --- /dev/null +++ b/src/main/kotlin/io/csbroker/apiserver/repository/post/LikeRepository.kt @@ -0,0 +1,26 @@ +package io.csbroker.apiserver.repository.post + +import io.csbroker.apiserver.common.enums.LikeType +import io.csbroker.apiserver.model.Like +import io.csbroker.apiserver.model.User +import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query + +interface LikeRepository : JpaRepository { + + @Query( + """ + select l from Like l + where l.type = :type and l.targetId in :targetIds + """, + ) + fun findAllByTargetIdIn(type: LikeType, targetIds: List): List + + @Query( + """ + select l from Like l + where l.type = :type and l.targetId = :targetId and l.user = :user + """, + ) + fun findByTargetIdAndUser(type: LikeType, targetId: Long, user: User): Like? +} diff --git a/src/main/kotlin/io/csbroker/apiserver/repository/post/PostRepository.kt b/src/main/kotlin/io/csbroker/apiserver/repository/post/PostRepository.kt index 10ca28b7..be193eb1 100644 --- a/src/main/kotlin/io/csbroker/apiserver/repository/post/PostRepository.kt +++ b/src/main/kotlin/io/csbroker/apiserver/repository/post/PostRepository.kt @@ -9,10 +9,20 @@ interface PostRepository : JpaRepository { @Query( """ select p from Post p - join fetch p.problem join fetch p.user + join fetch p.comments where p.problem = :problem """, ) fun findAllByProblem(problem: Problem): List + + @Query( + """ + select p from Post p + join fetch p.user + join fetch p.comments + where p.id = :id + """, + ) + fun findByIdOrNull(id: Long): Post? } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/post/CommentService.kt b/src/main/kotlin/io/csbroker/apiserver/service/post/CommentService.kt index a81cf348..e145d641 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/post/CommentService.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/post/CommentService.kt @@ -5,4 +5,5 @@ import io.csbroker.apiserver.model.User interface CommentService { fun create(postId: Long, content: String, user: User): Long fun deleteById(id: Long, user: User) + fun like(id: Long, user: User) } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/post/CommentServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/post/CommentServiceImpl.kt index 54c53954..263a92ce 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/post/CommentServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/post/CommentServiceImpl.kt @@ -1,12 +1,14 @@ package io.csbroker.apiserver.service.post import io.csbroker.apiserver.common.enums.ErrorCode +import io.csbroker.apiserver.common.enums.LikeType import io.csbroker.apiserver.common.exception.UnAuthorizedException import io.csbroker.apiserver.model.Comment +import io.csbroker.apiserver.model.Like import io.csbroker.apiserver.model.User import io.csbroker.apiserver.repository.post.CommentRepository +import io.csbroker.apiserver.repository.post.LikeRepository import io.csbroker.apiserver.repository.post.PostRepository -import io.csbroker.apiserver.repository.user.UserRepository import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -17,7 +19,7 @@ import javax.persistence.EntityNotFoundException class CommentServiceImpl( private val commentRepository: CommentRepository, private val postRepository: PostRepository, - private val userRepository: UserRepository, + private val likeRepository: LikeRepository, ) : CommentService { @Transactional override fun create(postId: Long, content: String, user: User): Long { @@ -37,4 +39,13 @@ class CommentServiceImpl( } commentRepository.delete(comment) } + + override fun like(id: Long, user: User) { + val comment = commentRepository.findByIdOrNull(id) + ?: throw EntityNotFoundException("${id}번 답변은 존재하지 않는 답변입니다") + + likeRepository.findByTargetIdAndUser(LikeType.COMMENT, comment.id, user) + ?.let { likeRepository.delete(it) } + ?: likeRepository.save(Like(user = user, type = LikeType.COMMENT, targetId = comment.id)) + } } diff --git a/src/main/kotlin/io/csbroker/apiserver/service/post/PostService.kt b/src/main/kotlin/io/csbroker/apiserver/service/post/PostService.kt index 90147256..0694703b 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/post/PostService.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/post/PostService.kt @@ -4,7 +4,8 @@ import io.csbroker.apiserver.controller.v1.post.response.PostResponseDto import io.csbroker.apiserver.model.User interface PostService { - fun findByProblemId(problemId: Long, email: String?): List + fun findByProblemId(problemId: Long, emailIfLogin: String?): List + fun findByPostId(postId: Long, emailIfLogin: String?): PostResponseDto fun create(problemId: Long, content: String, user: User): Long fun like(id: Long, user: User) fun deleteById(id: Long, user: User) diff --git a/src/main/kotlin/io/csbroker/apiserver/service/post/PostServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/post/PostServiceImpl.kt index 7170b399..48eef54f 100644 --- a/src/main/kotlin/io/csbroker/apiserver/service/post/PostServiceImpl.kt +++ b/src/main/kotlin/io/csbroker/apiserver/service/post/PostServiceImpl.kt @@ -1,17 +1,19 @@ package io.csbroker.apiserver.service.post import io.csbroker.apiserver.common.enums.ErrorCode +import io.csbroker.apiserver.common.enums.LikeType import io.csbroker.apiserver.common.exception.EntityNotFoundException import io.csbroker.apiserver.common.exception.UnAuthorizedException import io.csbroker.apiserver.controller.v1.post.response.CommentResponseDto import io.csbroker.apiserver.controller.v1.post.response.PostResponseDto +import io.csbroker.apiserver.model.Comment +import io.csbroker.apiserver.model.Like import io.csbroker.apiserver.model.Post -import io.csbroker.apiserver.model.PostLike import io.csbroker.apiserver.model.User -import io.csbroker.apiserver.repository.post.CommentRepository -import io.csbroker.apiserver.repository.post.PostLikeRepository +import io.csbroker.apiserver.repository.post.LikeRepository import io.csbroker.apiserver.repository.post.PostRepository import io.csbroker.apiserver.repository.problem.ProblemRepository +import io.csbroker.apiserver.repository.user.UserRepository import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -21,30 +23,75 @@ import org.springframework.transaction.annotation.Transactional class PostServiceImpl( private val problemRepository: ProblemRepository, private val postRepository: PostRepository, - private val postLikeRepository: PostLikeRepository, - private val commentRepository: CommentRepository, + private val likeRepository: LikeRepository, + private val userRepository: UserRepository, ) : PostService { - override fun findByProblemId(problemId: Long, email: String?): List { - val problem = problemRepository.findByIdOrNull(problemId) ?: throw EntityNotFoundException( - "${problemId}번 문제는 존재하지 않습니다", - ) + override fun findByProblemId(problemId: Long, emailIfLogin: String?): List { + val problem = problemRepository.findByIdOrNull(problemId) + ?: throw EntityNotFoundException("${problemId}번 문제는 존재하지 않습니다") + + val user = emailIfLogin?.let { + userRepository.findByEmail(it) + ?: throw EntityNotFoundException("$emailIfLogin 을 가진 유저는 존재하지 않습니다.") + } val posts = postRepository.findAllByProblem(problem) - val comments = commentRepository.findAllByPostIn(posts) - val postLikes = postLikeRepository.findAllByPostIn(posts) + val comments = posts.flatMap { it.comments } + val postLikeMap = likeRepository.findAllByTargetIdIn(LikeType.POST, posts.map { it.id }) + .groupBy { it.targetId } + val commentLikeMap = likeRepository.findAllByTargetIdIn(LikeType.COMMENT, comments.map { it.id }) + .groupBy { it.targetId } + return posts.map { - PostResponseDto( - it, - likeCount = postLikes.count { postLike -> postLike.post == it }.toLong(), - isLiked = postLikes.any { postLike -> postLike.post == it && postLike.user.email == email }, - comments = comments.filter { comment -> comment.post == it }.map { comment -> - CommentResponseDto( - comment, - ) - }, + combineResponseDto( + post = it, + comments = comments, + postLikes = postLikeMap[it.id] ?: emptyList(), + commentLikeMap = commentLikeMap, + user = user, ) } } + override fun findByPostId(postId: Long, emailIfLogin: String?): PostResponseDto { + val post = postRepository.findByIdOrNull(postId) + ?: throw EntityNotFoundException("id : $postId 게시글은 존재하지 않는 게시글입니다") + val user = emailIfLogin?.let { + userRepository.findByEmail(it) + ?: throw EntityNotFoundException("$emailIfLogin 을 가진 유저는 존재하지 않습니다.") + } + + val postLikes = likeRepository.findAllByTargetIdIn(LikeType.POST, listOf(postId)) + val commentLikeMap = likeRepository.findAllByTargetIdIn(LikeType.COMMENT, post.comments.map { it.id }) + .groupBy { it.targetId } + + return combineResponseDto( + post = post, + comments = post.comments, + postLikes = postLikes, + commentLikeMap = commentLikeMap, + user = user, + ) + } + + private fun combineResponseDto( + post: Post, + comments: List, + postLikes: List, + commentLikeMap: Map>, + user: User?, + ) = PostResponseDto( + post, + likeCount = postLikes.count(), + isLiked = postLikes.any { like -> like.user == user }, + comments = comments.map { comment -> + CommentResponseDto( + comment = comment, + likeCount = commentLikeMap[comment.id]?.count()?.toLong() ?: 0, + isLiked = commentLikeMap[comment.id]?.any { like -> like.user == user } ?: false, + ) + }, + ) + @Transactional override fun create(problemId: Long, content: String, user: User): Long { val problem = problemRepository.findByIdOrNull(problemId) ?: throw EntityNotFoundException( @@ -56,13 +103,12 @@ class PostServiceImpl( @Transactional override fun like(id: Long, user: User) { - val post = postRepository.findByIdOrNull(id) ?: throw EntityNotFoundException("${id}번 답변은 존재하지 않는 답변입니다") - val postLike = postLikeRepository.findByPostAndUser(post, user) - if (postLike == null) { - postLikeRepository.save(PostLike(post = post, user = user)) - } else { - postLikeRepository.delete(postLike) - } + val post = postRepository.findByIdOrNull(id) + ?: throw EntityNotFoundException("${id}번 게시글은 존재하지 않습니다.") + + likeRepository.findByTargetIdAndUser(LikeType.POST, post.id, user) + ?.let { likeRepository.delete(it) } + ?: likeRepository.save(Like(user = user, type = LikeType.POST, targetId = post.id)) } @Transactional diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/post/PostControllerTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/post/PostControllerTest.kt index a11a752b..e5bdb7d4 100644 --- a/src/test/kotlin/io/csbroker/apiserver/controller/v1/post/PostControllerTest.kt +++ b/src/test/kotlin/io/csbroker/apiserver/controller/v1/post/PostControllerTest.kt @@ -101,13 +101,15 @@ class PostControllerTest : RestDocsTest() { 1L, "CONTENT", "USER", - 1L, + 1, true, listOf( CommentResponseDto( 1L, "CONTENT", "USER", + 1, + true, LocalDateTime.now(), ), ), @@ -146,6 +148,10 @@ class PostControllerTest : RestDocsTest() { .type(JsonFieldType.STRING).description("댓글 내용"), PayloadDocumentation.fieldWithPath("data[].comments[].username") .type(JsonFieldType.STRING).description("댓글 작성자"), + PayloadDocumentation.fieldWithPath("data[].comments[].likeCount") + .type(JsonFieldType.NUMBER).description("좋아요 수"), + PayloadDocumentation.fieldWithPath("data[].comments[].isLiked") + .type(JsonFieldType.BOOLEAN).description("좋아요 여부"), PayloadDocumentation.fieldWithPath("data[].comments[].createdAt") .type(JsonFieldType.STRING).description("댓글 생성일시"), ),