diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc index 34381163..ab84cad3 100644 --- a/src/docs/asciidoc/index.adoc +++ b/src/docs/asciidoc/index.adoc @@ -1600,245 +1600,6 @@ include::{snippets}/admin/problems/deleteMany/request-fields.adoc[] include::{snippets}/admin/problems/deleteMany/response-body.adoc[] include::{snippets}/admin/problems/deleteMany/response-fields.adoc[] - -=== UserAnswer API ( /api/admin/user-answers, /api/admin/user-answer ) - -==== 유저 응답 단일 생성 - -.description -[source] ----- -하나의 유저 응답을 생성하기 위한 API입니다. - -HTTP Method : POST -End-Point : /api/admin/user-answer ----- - -.Sample Request -include::{snippets}/admin/userAnswer/createOne/http-request.adoc[] - -.Sample Response -include::{snippets}/admin/userAnswer/createOne/http-response.adoc[] - -.Request Body -include::{snippets}/admin/userAnswer/createOne/request-body.adoc[] -include::{snippets}/admin/userAnswer/createOne/request-fields.adoc[] - -.Response Body -include::{snippets}/admin/userAnswer/createOne/response-body.adoc[] -include::{snippets}/admin/userAnswer/createOne/response-fields.adoc[] - -==== 유저 응답 생성 - -.description -[source] ----- -여러 개의 유저 응답을 생성하기 위한 API입니다. - -HTTP Method : POST -End-Point : /api/admin/user-answers ----- - -.Sample Request -include::{snippets}/admin/userAnswer/createMany/http-request.adoc[] - -.Sample Response -include::{snippets}/admin/userAnswer/createMany/http-response.adoc[] - -.Request Body -include::{snippets}/admin/userAnswer/createMany/request-body.adoc[] -include::{snippets}/admin/userAnswer/createMany/request-fields.adoc[] - -.Response Body -include::{snippets}/admin/userAnswer/createMany/response-body.adoc[] -include::{snippets}/admin/userAnswer/createMany/response-fields.adoc[] - -==== 유저 응답 단건 조회 - -.description -[source] ----- -유저 응답 단건 조회를 위한 API입니다. - -HTTP Method : GET -End-Point : /api/admin/user-answers/{user_answer_id: Long} ----- - -.Sample Request -include::{snippets}/admin/userAnswer/findOne/http-request.adoc[] - -.Sample Response -include::{snippets}/admin/userAnswer/findOne/http-response.adoc[] - -.Path parameters -include::{snippets}/admin/userAnswer/findOne/path-parameters.adoc[] - -유저 답안 조회를 위한 path parameter의 설명입니다. - -.Response Body -include::{snippets}/admin/userAnswer/findOne/response-body.adoc[] -include::{snippets}/admin/userAnswer/findOne/response-fields.adoc[] - -==== 유저 답안 검색 - -.description -[source] ----- -유저 답안 검색을 위한 API입니다. - -HTTP Method : GET -End-Point : /api/admin/user-answers -Req Params : id, assignedBy, validatedBy, problemTitle, answer, isLabeled, isValidated, size, page ----- - -.Sample Request -include::{snippets}/admin/userAnswer/search/http-request.adoc[] - -.Sample Response -include::{snippets}/admin/userAnswer/search/http-response.adoc[] - -.Request parameters - -유저 답안 검색을 위한 request parameter의 설명입니다. -include::{snippets}/admin/userAnswer/search/request-parameters.adoc[] - - -.Response Body -include::{snippets}/admin/userAnswer/search/response-body.adoc[] -include::{snippets}/admin/userAnswer/search/response-fields.adoc[] - -==== 유저 답안 라벨링 - -.description -[source] ----- -유저 답안 라벨링을 위한 API입니다. - -HTTP Method : POST -End-Point : /api/admin/user-answers/{user_answer_id: Long}/label ----- - -.Sample Request -include::{snippets}/admin/userAnswer/label/http-request.adoc[] - -.Sample Response -include::{snippets}/admin/userAnswer/label/http-response.adoc[] - -.Path parameters -include::{snippets}/admin/userAnswer/label/path-parameters.adoc[] - -유저 답안 라벨링을 위한 path parameter의 설명입니다. - -.Request Body -include::{snippets}/admin/userAnswer/label/request-body.adoc[] -include::{snippets}/admin/userAnswer/label/request-fields.adoc[] - -.Response Body -include::{snippets}/admin/userAnswer/label/response-body.adoc[] -include::{snippets}/admin/userAnswer/label/response-fields.adoc[] - -==== 유저 답안 검수 - -.description -[source] ----- -유저 답안 검수를 위한 API입니다. - -HTTP Method : POST -End-Point : /api/admin/user-answers/{user_answer_id: Long}/validate ----- - -.Sample Request -include::{snippets}/admin/userAnswer/validate/http-request.adoc[] - -.Sample Response -include::{snippets}/admin/userAnswer/validate/http-response.adoc[] - -.Path parameters -include::{snippets}/admin/userAnswer/validate/path-parameters.adoc[] - -유저 답안 검수를 위한 path parameter의 설명입니다. - -.Request Body -include::{snippets}/admin/userAnswer/validate/request-body.adoc[] -include::{snippets}/admin/userAnswer/validate/request-fields.adoc[] - -.Response Body -include::{snippets}/admin/userAnswer/validate/response-body.adoc[] -include::{snippets}/admin/userAnswer/validate/response-fields.adoc[] - -==== 유저 답안 라벨링 유저 할당 - -.description -[source] ----- -유저 답안 라벨링 유저 할당을 위한 API입니다. - -HTTP Method : PUT -End-Point : /api/admin/user-answers/assign/validate ----- - -.Sample Request -include::{snippets}/admin/userAnswer/assign/label/http-request.adoc[] - -.Sample Response -include::{snippets}/admin/userAnswer/assign/label/http-response.adoc[] - -.Request Body -include::{snippets}/admin/userAnswer/assign/label/request-body.adoc[] -include::{snippets}/admin/userAnswer/assign/label/request-fields.adoc[] - -.Response Body -include::{snippets}/admin/userAnswer/assign/label/response-body.adoc[] -include::{snippets}/admin/userAnswer/assign/label/response-fields.adoc[] - - -==== 유저 답안 검수자 할당 - -.description -[source] ----- -유저 답안 검수자 할당을 위한 API입니다. - -HTTP Method : PUT -End-Point : /api/admin/user-answers/assign/validate ----- - -.Sample Request -include::{snippets}/admin/userAnswer/assign/validate/http-request.adoc[] - -.Sample Response -include::{snippets}/admin/userAnswer/assign/validate/http-response.adoc[] - -.Request Body -include::{snippets}/admin/userAnswer/assign/validate/request-body.adoc[] -include::{snippets}/admin/userAnswer/assign/validate/request-fields.adoc[] - -.Response Body -include::{snippets}/admin/userAnswer/assign/validate/response-body.adoc[] -include::{snippets}/admin/userAnswer/assign/validate/response-fields.adoc[] - -==== 유저 응답 삭제 - -.description -[source] ----- -하나의 유저 응답을 삭제하기 위한 API입니다. - -HTTP Method : DELETE -End-Point : /api/admin/user-answers/{user_answer_id: Long} ----- - -.Sample Request -include::{snippets}/admin/userAnswer/deleteOne/http-request.adoc[] - -.Sample Response -include::{snippets}/admin/userAnswer/deleteOne/http-response.adoc[] - -.Response Body -include::{snippets}/admin/userAnswer/deleteOne/response-body.adoc[] -include::{snippets}/admin/userAnswer/deleteOne/response-fields.adoc[] - === User API ( /api/admin/users ) ==== ADMIN 유저 조회 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 deleted file mode 100644 index 75768b21..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminUserAnswerController.kt +++ /dev/null @@ -1,148 +0,0 @@ -package io.csbroker.apiserver.controller.v1.admin.problem - -import io.csbroker.apiserver.auth.LoginUser -import io.csbroker.apiserver.common.enums.ErrorCode -import io.csbroker.apiserver.common.exception.ConditionConflictException -import io.csbroker.apiserver.dto.common.ApiResponse -import io.csbroker.apiserver.dto.common.UpsertSuccessResponseDto -import io.csbroker.apiserver.dto.useranswer.AssignUserAnswerDto -import io.csbroker.apiserver.dto.useranswer.UserAnswerBatchInsertDto -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.web.bind.annotation.DeleteMapping -import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable -import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.PutMapping -import org.springframework.web.bind.annotation.RequestBody -import org.springframework.web.bind.annotation.RequestMapping -import org.springframework.web.bind.annotation.RequestParam -import org.springframework.web.bind.annotation.RestController - -@RestController -@RequestMapping("/api/admin") -class AdminUserAnswerController( - private val userAnswerService: UserAnswerService, -) { - @PostMapping("/user-answers") - fun createUserAnswers( - @RequestBody userAnswers: UserAnswerBatchInsertDto, - ): ApiResponse { - val size = userAnswerService.createUserAnswers(userAnswers.userAnswers) - return ApiResponse.success(UpsertSuccessResponseDto(size = size)) - } - - @PostMapping("/user-answer") - fun createUserAnswer( - @RequestBody userAnswer: UserAnswerUpsertDto, - ): ApiResponse { - val createUserAnswerId = userAnswerService.createUserAnswer(userAnswer) - return ApiResponse.success(UpsertSuccessResponseDto(id = createUserAnswerId)) - } - - @GetMapping("/user-answers/{id}") - fun getUserAnswerById( - @PathVariable("id") id: Long, - ): ApiResponse { - return ApiResponse.success(userAnswerService.findUserAnswerById(id)) - } - - @PostMapping("/user-answers/{id}/{type:label|validate}") - fun checkUserAnswer( - @PathVariable("id") id: Long, - @RequestBody userAnswerLabelRequestDto: UserAnswerLabelRequestDto, - @PathVariable("type") type: String, - @LoginUser loginUser: User, - ): ApiResponse { - val answerId = when (type) { - "label" -> - userAnswerService.labelUserAnswer( - loginUser.email, - id, - userAnswerLabelRequestDto.selectedGradingStandardIds, - ) - - "validate" -> - userAnswerService.validateUserAnswer( - loginUser.email, - id, - userAnswerLabelRequestDto.selectedGradingStandardIds, - ) - - else -> throw ConditionConflictException( - ErrorCode.CONDITION_NOT_FULFILLED, - "존재하지 않는 uri ( $type ) 입니다.", - ) - } - return ApiResponse.success(UpsertSuccessResponseDto(id = answerId)) - } - - @GetMapping("/user-answers") - fun findUserAnswersByQuery( - @RequestParam("id", required = false) id: Long?, - @RequestParam("assignedBy", required = false) assignedBy: String?, - @RequestParam("validatedBy", required = false) validatedBy: String?, - @RequestParam("problemTitle", required = false) problemTitle: String?, - @RequestParam("answer", required = false) answer: String?, - @RequestParam("isLabeled", required = false) isLabeled: Boolean?, - @RequestParam("isValidated", required = false) isValidated: Boolean?, - pageable: Pageable, - ): ApiResponse { - return ApiResponse.success( - userAnswerService.findUserAnswersByQuery( - id, - assignedBy, - validatedBy, - problemTitle, - answer, - isLabeled, - isValidated, - pageable, - ), - ) - } - - @DeleteMapping("/user-answers/{id}") - fun deleteUserAnswerById( - @PathVariable("id") id: Long, - ): ApiResponse { - userAnswerService.removeUserAnswerById(id) - return ApiResponse.success(true) - } - - @PutMapping("/user-answers/assign/{type:label|validate}") - fun assignUserAnswer( - @PathVariable("type") type: String, - @RequestBody assignUserAnswerDto: AssignUserAnswerDto, - ): ApiResponse { - if (assignUserAnswerDto.userAnswerIds.isEmpty()) { - throw ConditionConflictException(ErrorCode.CONDITION_NOT_FULFILLED, "할당 할 user answer가 없습니다.") - } - - when (type) { - "label" -> - userAnswerService.assignLabelUserAnswer( - assignUserAnswerDto.userAnswerIds, - assignUserAnswerDto.assigneeId, - ) - - "validate" -> - userAnswerService.assignValidationUserAnswer( - assignUserAnswerDto.userAnswerIds, - assignUserAnswerDto.assigneeId, - ) - - else -> throw ConditionConflictException( - ErrorCode.CONDITION_NOT_FULFILLED, - "존재하지 않는 uri ( $type ) 입니다.", - ) - } - - return ApiResponse.success(UpsertSuccessResponseDto(size = assignUserAnswerDto.userAnswerIds.size)) - } -} diff --git a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/AssignUserAnswerDto.kt b/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/AssignUserAnswerDto.kt deleted file mode 100644 index 6facdd95..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/AssignUserAnswerDto.kt +++ /dev/null @@ -1,8 +0,0 @@ -package io.csbroker.apiserver.dto.useranswer - -import java.util.UUID - -data class AssignUserAnswerDto( - val userAnswerIds: List = listOf(), - val assigneeId: UUID, -) diff --git a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerBatchInsertDto.kt b/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerBatchInsertDto.kt deleted file mode 100644 index 74ce8077..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerBatchInsertDto.kt +++ /dev/null @@ -1,6 +0,0 @@ -package io.csbroker.apiserver.dto.useranswer - -data class UserAnswerBatchInsertDto( - val size: Int, - val userAnswers: List, -) diff --git a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerLabelRequestDto.kt b/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerLabelRequestDto.kt deleted file mode 100644 index 72eb4e0d..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerLabelRequestDto.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.csbroker.apiserver.dto.useranswer - -data class UserAnswerLabelRequestDto( - val selectedGradingStandardIds: List, -) diff --git a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerResponseDto.kt b/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerResponseDto.kt deleted file mode 100644 index f591093b..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerResponseDto.kt +++ /dev/null @@ -1,47 +0,0 @@ -package io.csbroker.apiserver.dto.useranswer - -import io.csbroker.apiserver.common.enums.GradingStandardType -import io.csbroker.apiserver.dto.user.GradingStandardResponseDto -import io.csbroker.apiserver.model.UserAnswer - -data class UserAnswerResponseDto( - val id: Long, - val problemId: Long, - val problemTitle: String, - val problemDescription: String, - val answer: String, - val isLabeled: Boolean, - val isValidated: Boolean, - val keywordsGradingStandards: List, - val contentGradingStandards: List, - val selectedGradingStandards: List, -) { - companion object { - fun fromUserAnswer(userAnswer: UserAnswer): UserAnswerResponseDto { - val keywords = userAnswer.problem - .gradingStandards.filter { it.type == GradingStandardType.KEYWORD } - .map { GradingStandardResponseDto.fromGradingStandard(it) } - - val contents = userAnswer.problem - .gradingStandards.filter { it.type == GradingStandardType.CONTENT } - .map { GradingStandardResponseDto.fromGradingStandard(it) } - - val selectedGradingStandards = userAnswer.userAnswerGradingStandards.map { - it.gradingStandard.id - } - - return UserAnswerResponseDto( - userAnswer.id, - userAnswer.problem.id, - userAnswer.problem.title, - userAnswer.problem.description, - userAnswer.answer, - userAnswer.isLabeled, - userAnswer.isValidated, - keywords, - contents, - selectedGradingStandards, - ) - } - } -} diff --git a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerSearchResponseDto.kt b/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerSearchResponseDto.kt deleted file mode 100644 index 6dd7107e..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerSearchResponseDto.kt +++ /dev/null @@ -1,19 +0,0 @@ -package io.csbroker.apiserver.dto.useranswer - -import java.time.LocalDateTime - -data class UserAnswerSearchResponseDto( - val userAnswers: List, - val totalPages: Int, - val totalElements: Long, -) { - data class UserAnswerDataDto( - val id: Long, - val problemTitle: String, - val assignedUsername: String?, - val validatingUsername: String?, - val updatedAt: LocalDateTime, - val isLabeled: Boolean, - val isValidated: Boolean, - ) -} diff --git a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerUpsertDto.kt b/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerUpsertDto.kt deleted file mode 100644 index 486b8b3d..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/dto/useranswer/UserAnswerUpsertDto.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.csbroker.apiserver.dto.useranswer - -import java.util.UUID - -data class UserAnswerUpsertDto( - val assignedUserId: UUID?, - val validatingUserId: UUID?, - val answer: String, - val problemId: Long, -) diff --git a/src/main/kotlin/io/csbroker/apiserver/model/UserAnswer.kt b/src/main/kotlin/io/csbroker/apiserver/model/UserAnswer.kt index 66627b53..2badd598 100644 --- a/src/main/kotlin/io/csbroker/apiserver/model/UserAnswer.kt +++ b/src/main/kotlin/io/csbroker/apiserver/model/UserAnswer.kt @@ -1,7 +1,6 @@ package io.csbroker.apiserver.model import io.csbroker.apiserver.common.enums.GradingStandardType -import io.csbroker.apiserver.dto.useranswer.UserAnswerSearchResponseDto.UserAnswerDataDto import jakarta.persistence.CascadeType import jakarta.persistence.Column import jakarta.persistence.Entity @@ -65,16 +64,4 @@ class UserAnswer( it.score } } - - fun toUserAnswerDataDto(): UserAnswerDataDto { - return UserAnswerDataDto( - id, - problem.title, - assignedUser?.username, - validatingUser?.username, - updatedAt!!, - isLabeled, - isValidated, - ) - } } diff --git a/src/main/kotlin/io/csbroker/apiserver/repository/problem/UserAnswerRepository.kt b/src/main/kotlin/io/csbroker/apiserver/repository/problem/UserAnswerRepository.kt index e41ddd9c..103d948e 100644 --- a/src/main/kotlin/io/csbroker/apiserver/repository/problem/UserAnswerRepository.kt +++ b/src/main/kotlin/io/csbroker/apiserver/repository/problem/UserAnswerRepository.kt @@ -2,20 +2,5 @@ package io.csbroker.apiserver.repository.problem import io.csbroker.apiserver.model.UserAnswer import org.springframework.data.jpa.repository.JpaRepository -import org.springframework.data.jpa.repository.Modifying -import org.springframework.data.jpa.repository.Query -import org.springframework.data.repository.query.Param -import java.util.UUID -interface UserAnswerRepository : JpaRepository, UserAnswerRepositoryCustom { - @Query("select count(ua.id) from UserAnswer ua where ua.id in :ids") - fun cntUserAnswer(@Param("ids") ids: List): Int - - @Modifying - @Query("update UserAnswer ua set ua.assignedUser.id = :user_id where ua.id in :ids") - fun updateLabelerId(@Param("ids") ids: List, @Param("user_id") userId: UUID) - - @Modifying - @Query("update UserAnswer ua set ua.validatingUser.id = :user_id where ua.id in :ids") - fun updateValidatorId(@Param("ids") ids: List, @Param("user_id") userId: UUID) -} +interface UserAnswerRepository : JpaRepository diff --git a/src/main/kotlin/io/csbroker/apiserver/repository/problem/UserAnswerRepositoryCustom.kt b/src/main/kotlin/io/csbroker/apiserver/repository/problem/UserAnswerRepositoryCustom.kt deleted file mode 100644 index 7f0441b7..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/repository/problem/UserAnswerRepositoryCustom.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.csbroker.apiserver.repository.problem - -import io.csbroker.apiserver.dto.useranswer.UserAnswerUpsertDto -import io.csbroker.apiserver.model.UserAnswer -import org.springframework.data.domain.Page -import org.springframework.data.domain.Pageable - -interface UserAnswerRepositoryCustom { - fun batchInsert(userAnswers: List) - fun findUserAnswersByQuery( - id: Long?, - assignedBy: String?, - validatedBy: String?, - problemTitle: String?, - answer: String?, - isLabeled: Boolean?, - isValidated: Boolean?, - pageable: Pageable, - ): Page -} diff --git a/src/main/kotlin/io/csbroker/apiserver/repository/problem/UserAnswerRepositoryCustomImpl.kt b/src/main/kotlin/io/csbroker/apiserver/repository/problem/UserAnswerRepositoryCustomImpl.kt deleted file mode 100644 index 3403d441..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/repository/problem/UserAnswerRepositoryCustomImpl.kt +++ /dev/null @@ -1,130 +0,0 @@ -package io.csbroker.apiserver.repository.problem - -import com.querydsl.core.types.dsl.BooleanExpression -import com.querydsl.jpa.impl.JPAQueryFactory -import io.csbroker.apiserver.common.util.asByte -import io.csbroker.apiserver.dto.useranswer.UserAnswerUpsertDto -import io.csbroker.apiserver.model.QLongProblem.longProblem -import io.csbroker.apiserver.model.QUser.user -import io.csbroker.apiserver.model.QUserAnswer.userAnswer -import io.csbroker.apiserver.model.UserAnswer -import org.springframework.data.domain.Page -import org.springframework.data.domain.PageImpl -import org.springframework.data.domain.Pageable -import org.springframework.jdbc.core.BatchPreparedStatementSetter -import org.springframework.jdbc.core.JdbcTemplate -import java.sql.PreparedStatement -import java.sql.Timestamp -import java.time.LocalDateTime - -class UserAnswerRepositoryCustomImpl( - private val jdbcTemplate: JdbcTemplate, - private val queryFactory: JPAQueryFactory, -) : UserAnswerRepositoryCustom { - override fun batchInsert(userAnswers: List) { - val sql = """ - INSERT INTO user_answer - (answer, is_labeled, is_validated, assigned_user_id, problem_id, validator_id, created_at, updated_at) - VALUES - (?, ?, ?, ?, ?, ?, ?, ?) - """.trimIndent() - - jdbcTemplate.batchUpdate( - sql, - object : BatchPreparedStatementSetter { - override fun setValues(ps: PreparedStatement, i: Int) { - val userAnswer = userAnswers[i] - ps.setString(1, userAnswer.answer) - ps.setBoolean(2, false) - ps.setBoolean(3, false) - ps.setBytes(4, userAnswer.assignedUserId?.asByte()) - ps.setLong(5, userAnswer.problemId) - ps.setBytes(6, userAnswer.assignedUserId?.asByte()) - ps.setTimestamp(7, Timestamp.valueOf(LocalDateTime.now())) - ps.setTimestamp(8, Timestamp.valueOf(LocalDateTime.now())) - } - - override fun getBatchSize(): Int { - return userAnswers.size - } - }, - ) - } - - override fun findUserAnswersByQuery( - id: Long?, - assignedBy: String?, - validatedBy: String?, - problemTitle: String?, - answer: String?, - isLabeled: Boolean?, - isValidated: Boolean?, - pageable: Pageable, - ): Page { - val result = queryFactory.selectFrom(userAnswer) - .distinct() - .leftJoin(userAnswer.assignedUser, user).fetchJoin() - .leftJoin(userAnswer.validatingUser, user).fetchJoin() - .leftJoin(userAnswer.problem, longProblem).fetchJoin() - .where( - findById(id), - isAssignedBy(assignedBy), - isValidatedBy(validatedBy), - likeProblemTitle(problemTitle), - likeAnswer(answer), - userAnswerLabeled(isLabeled), - userAnswerValidated(isValidated), - ) - .orderBy(userAnswer.updatedAt.desc()) - .offset(pageable.offset) - .limit(pageable.pageSize.toLong()) - .fetch() - - val totalCnt = queryFactory.selectFrom(userAnswer) - .distinct() - .leftJoin(userAnswer.assignedUser, user) - .leftJoin(userAnswer.validatingUser, user) - .leftJoin(userAnswer.problem, longProblem) - .groupBy(userAnswer.id) - .where( - findById(id), - isAssignedBy(assignedBy), - isValidatedBy(validatedBy), - likeProblemTitle(problemTitle), - likeAnswer(answer), - userAnswerLabeled(isLabeled), - userAnswerValidated(isValidated), - ) - .fetch().size.toLong() - - return PageImpl(result, pageable, totalCnt) - } - - private fun findById(id: Long?): BooleanExpression? { - return if (id == null) null else userAnswer.id.eq(id) - } - - private fun isAssignedBy(assignedBy: String?): BooleanExpression? { - return if (assignedBy == null) null else userAnswer.assignedUser.username.eq(assignedBy) - } - - private fun isValidatedBy(validatedBy: String?): BooleanExpression? { - return if (validatedBy == null) null else userAnswer.validatingUser.username.eq(validatedBy) - } - - private fun likeProblemTitle(problemTitle: String?): BooleanExpression? { - return if (problemTitle == null) null else longProblem.title.contains(problemTitle) - } - - private fun likeAnswer(answer: String?): BooleanExpression? { - return if (answer == null) null else userAnswer.answer.contains(answer) - } - - private fun userAnswerLabeled(isLabeled: Boolean?): BooleanExpression? { - return if (isLabeled == null) null else userAnswer.isLabeled.eq(isLabeled) - } - - private fun userAnswerValidated(isValidated: Boolean?): BooleanExpression? { - return if (isValidated == null) null else userAnswer.isValidated.eq(isValidated) - } -} diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/UserAnswerService.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/UserAnswerService.kt deleted file mode 100644 index 47bbe080..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/UserAnswerService.kt +++ /dev/null @@ -1,39 +0,0 @@ -package io.csbroker.apiserver.service.problem - -import io.csbroker.apiserver.dto.useranswer.UserAnswerResponseDto -import io.csbroker.apiserver.dto.useranswer.UserAnswerSearchResponseDto -import io.csbroker.apiserver.dto.useranswer.UserAnswerUpsertDto -import org.springframework.data.domain.Pageable -import java.util.UUID - -interface UserAnswerService { - fun createUserAnswers(userAnswers: List): Int - fun createUserAnswer(userAnswer: UserAnswerUpsertDto): Long - fun findUserAnswerById(id: Long): UserAnswerResponseDto - fun labelUserAnswer(email: String, userAnswerId: Long, selectedGradingStandardIds: List): Long - fun validateUserAnswer(email: String, userAnswerId: Long, selectedGradingStandardIds: List): Long - fun findUserAnswersByQuery( - id: Long?, - assignedBy: String?, - validatedBy: String?, - problemTitle: String?, - answer: String?, - isLabeled: Boolean?, - isValidated: Boolean?, - pageable: Pageable, - ): UserAnswerSearchResponseDto - - fun assignLabelUserAnswer( - userAnswerIds: List, - userId: UUID, - ) - - fun assignValidationUserAnswer( - userAnswerIds: List, - userId: UUID, - ) - - fun removeUserAnswerById( - userAnswerId: Long, - ) -} diff --git a/src/main/kotlin/io/csbroker/apiserver/service/problem/UserAnswerServiceImpl.kt b/src/main/kotlin/io/csbroker/apiserver/service/problem/UserAnswerServiceImpl.kt deleted file mode 100644 index 9d232a35..00000000 --- a/src/main/kotlin/io/csbroker/apiserver/service/problem/UserAnswerServiceImpl.kt +++ /dev/null @@ -1,192 +0,0 @@ -package io.csbroker.apiserver.service.problem - -import io.csbroker.apiserver.common.enums.ErrorCode -import io.csbroker.apiserver.common.enums.Role -import io.csbroker.apiserver.common.exception.ConditionConflictException -import io.csbroker.apiserver.common.exception.EntityNotFoundException -import io.csbroker.apiserver.common.exception.UnAuthorizedException -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.UserAnswer -import io.csbroker.apiserver.repository.problem.GradingStandardRepository -import io.csbroker.apiserver.repository.problem.LongProblemRepository -import io.csbroker.apiserver.repository.problem.UserAnswerGradingStandardRepository -import io.csbroker.apiserver.repository.problem.UserAnswerRepository -import io.csbroker.apiserver.repository.user.UserRepository -import org.springframework.data.domain.Pageable -import org.springframework.data.repository.findByIdOrNull -import org.springframework.stereotype.Service -import org.springframework.transaction.annotation.Transactional -import java.util.UUID - -@Service -class UserAnswerServiceImpl( - private val userAnswerRepository: UserAnswerRepository, - private val userRepository: UserRepository, - private val longProblemRepository: LongProblemRepository, - private val gradingStandardRepository: GradingStandardRepository, - private val userAnswerGradingStandardRepository: UserAnswerGradingStandardRepository, -) : UserAnswerService { - @Transactional - override fun createUserAnswers(userAnswers: List): Int { - userAnswerRepository.batchInsert(userAnswers) - return userAnswers.size - } - - @Transactional - override fun createUserAnswer(userAnswer: UserAnswerUpsertDto): Long { - val problem = longProblemRepository.findByIdOrNull(userAnswer.problemId) - ?: throw EntityNotFoundException("${userAnswer.problemId}번 문제를 찾을 수 없습니다.") - - val assignedUser = userAnswer.assignedUserId?.let { - userRepository.findByIdOrNull(it) - ?: throw EntityNotFoundException("${it}번 유저를 찾을 수 없습니다.") - } - - val validatingUser = userAnswer.validatingUserId?.let { - userRepository.findByIdOrNull(it) - ?: throw EntityNotFoundException("${it}번 유저를 찾을 수 없습니다.") - } - - val createUserAnswer = UserAnswer( - answer = userAnswer.answer, - problem = problem, - assignedUser = assignedUser, - validatingUser = validatingUser, - ) - - return userAnswerRepository.save(createUserAnswer).id - } - - override fun findUserAnswerById(id: Long): UserAnswerResponseDto { - val userAnswer = userAnswerRepository.findByIdOrNull(id) - ?: throw EntityNotFoundException("${id}번 유저 응답을 찾을 수 없습니다.") - - return UserAnswerResponseDto.fromUserAnswer(userAnswer) - } - - @Transactional - override fun labelUserAnswer(email: String, userAnswerId: Long, selectedGradingStandardIds: List): Long { - val userAnswer = userAnswerRepository.findByIdOrNull(userAnswerId) - ?: throw EntityNotFoundException( - "${userAnswerId}번 유저 응답을 찾을 수 없습니다.", - ) - if (userAnswer.assignedUser == null) { - throw EntityNotFoundException("${userAnswerId}번에 담당자가 존재하지 않습니다.") - } - - if (userAnswer.assignedUser!!.email != email) { - throw EntityNotFoundException("${userAnswerId}번에 할당된 유저가 아닙니다.") - } - - setGradingStandards(selectedGradingStandardIds, userAnswerId) - - userAnswer.isLabeled = true - - return userAnswerId - } - - @Transactional - override fun validateUserAnswer(email: String, userAnswerId: Long, selectedGradingStandardIds: List): Long { - val userAnswer = userAnswerRepository.findByIdOrNull(userAnswerId) - ?: throw EntityNotFoundException( - "${userAnswerId}번 유저 응답을 찾을 수 없습니다.", - ) - - if (!userAnswer.isLabeled) { - throw ConditionConflictException( - ErrorCode.CONDITION_NOT_FULFILLED, - "${userAnswerId}번 유저 응답은 라벨링 되지 않았기때문에, 검수할 수 없습니다.", - ) - } - - if (userAnswer.validatingUser == null || userAnswer.validatingUser?.email != email) { - throw ConditionConflictException( - ErrorCode.CONDITION_NOT_FULFILLED, - "${userAnswerId}번에 검수자로 할당된 유저가 아닙니다.", - ) - } - - setGradingStandards(selectedGradingStandardIds, userAnswerId) - - userAnswer.isValidated = true - - return userAnswerId - } - - private fun setGradingStandards( - selectedGradingStandardIds: List, - userAnswerId: Long, - ) { - val foundGradingStandardsCount = gradingStandardRepository - .countByIdIn(selectedGradingStandardIds) - - if (foundGradingStandardsCount != selectedGradingStandardIds.size) { - throw EntityNotFoundException("존재하지 않는 채점 기준으로 라벨링을 시도하였습니다.") - } - - userAnswerGradingStandardRepository.deleteAllByUserAnswerId(userAnswerId) - - userAnswerGradingStandardRepository.batchInsert(userAnswerId, selectedGradingStandardIds) - } - - override fun findUserAnswersByQuery( - id: Long?, - assignedBy: String?, - validatedBy: String?, - problemTitle: String?, - answer: String?, - isLabeled: Boolean?, - isValidated: Boolean?, - pageable: Pageable, - ): UserAnswerSearchResponseDto { - val pagedUserAnswers = userAnswerRepository.findUserAnswersByQuery( - id, - assignedBy, - validatedBy, - problemTitle, - answer, - isLabeled, - isValidated, - pageable, - ) - - return UserAnswerSearchResponseDto( - pagedUserAnswers.map { it.toUserAnswerDataDto() }.toList(), - pagedUserAnswers.totalPages, - pagedUserAnswers.totalElements, - ) - } - - @Transactional - override fun assignLabelUserAnswer(userAnswerIds: List, userId: UUID) { - validateAssignCondition(userAnswerIds, userId) - userAnswerRepository.updateLabelerId(userAnswerIds, userId) - } - - @Transactional - override fun assignValidationUserAnswer(userAnswerIds: List, userId: UUID) { - validateAssignCondition(userAnswerIds, userId) - userAnswerRepository.updateValidatorId(userAnswerIds, userId) - } - - @Transactional - override fun removeUserAnswerById(userAnswerId: Long) { - userAnswerRepository.deleteById(userAnswerId) - } - - private fun validateAssignCondition(userAnswerIds: List, userId: UUID) { - val cnt = userAnswerRepository.cntUserAnswer(userAnswerIds) - if (cnt != userAnswerIds.size) { - throw EntityNotFoundException("존재하지 않는 user answer를 업데이트 할 수 없습니다.") - } - - val findUser = userRepository.findByIdOrNull(userId) - ?: throw EntityNotFoundException("${userId}를 가진 유저를 찾을 수 없습니다.") - - if (findUser.role != Role.ROLE_ADMIN) { - throw UnAuthorizedException(ErrorCode.FORBIDDEN, "권한이 없는 유저를 할당하려 하였습니다.") - } - } -} diff --git a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminUserAnswerProblemControllerTest.kt b/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminUserAnswerProblemControllerTest.kt deleted file mode 100644 index 71a26a9e..00000000 --- a/src/test/kotlin/io/csbroker/apiserver/controller/v1/admin/problem/AdminUserAnswerProblemControllerTest.kt +++ /dev/null @@ -1,503 +0,0 @@ -package io.csbroker.apiserver.controller.v1.admin.problem - -import io.csbroker.apiserver.common.enums.GradingStandardType -import io.csbroker.apiserver.controller.RestDocsTest -import io.csbroker.apiserver.dto.user.GradingStandardResponseDto -import io.csbroker.apiserver.dto.useranswer.AssignUserAnswerDto -import io.csbroker.apiserver.dto.useranswer.UserAnswerBatchInsertDto -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.service.problem.UserAnswerService -import io.mockk.every -import io.mockk.justRun -import io.mockk.mockk -import io.restassured.http.Method -import io.restassured.module.mockmvc.specification.MockMvcRequestSpecification -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document -import org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest -import org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse -import org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint -import org.springframework.restdocs.payload.JsonFieldType -import org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath -import org.springframework.restdocs.payload.PayloadDocumentation.requestFields -import org.springframework.restdocs.payload.PayloadDocumentation.responseFields -import org.springframework.restdocs.request.RequestDocumentation.parameterWithName -import org.springframework.restdocs.request.RequestDocumentation.pathParameters -import org.springframework.restdocs.request.RequestDocumentation.queryParameters -import java.time.LocalDateTime -import java.util.UUID - -class AdminUserAnswerProblemControllerTest : RestDocsTest() { - private lateinit var userAnswerService: UserAnswerService - private lateinit var mockMvc: MockMvcRequestSpecification - - @BeforeEach - fun setUp() { - userAnswerService = mockk() - mockMvc = mockMvc( - AdminUserAnswerController(userAnswerService), - ).header("Authorization", "Bearer TEST-TOKEN") - } - - @Test - fun `Create UserAnswer 200`() { - // given - val userAnswerUpsertDto = UserAnswerUpsertDto( - UUID.randomUUID(), - UUID.randomUUID(), - "test", - 1L, - ) - every { userAnswerService.createUserAnswer(any()) } returns 1L - - // when - val result = mockMvc.body(userAnswerUpsertDto).request(Method.POST, "/api/admin/user-answer") - - // then - result.then().statusCode(200) - .apply( - document( - "admin/userAnswer/createOne", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - requestFields( - fieldWithPath("assignedUserId").type(JsonFieldType.STRING) - .description("할당 할 유저 ID ( UUID, null 값 가능 )").optional(), - fieldWithPath("validatingUserId").type(JsonFieldType.STRING) - .description("검수자로 할당 할 유저 ID ( UUID, null 값 가능. )").optional(), - fieldWithPath("answer").type(JsonFieldType.STRING) - .description("유저 답안"), - fieldWithPath("problemId").type(JsonFieldType.NUMBER) - .description("답안에 대한 문제 ID"), - ), - responseFields( - fieldWithPath("status") - .type(JsonFieldType.STRING).description("결과 상태"), - fieldWithPath("data.id") - .type(JsonFieldType.NUMBER).description("유저 답안 ID"), - ), - ), - ) - } - - @Test - fun `Create UserAnswers 200`() { - // given - val userAnswerUpsertDto = UserAnswerBatchInsertDto( - 1, - listOf( - UserAnswerUpsertDto( - UUID.randomUUID(), - UUID.randomUUID(), - "test", - 1L, - ), - ), - ) - every { userAnswerService.createUserAnswers(any()) } returns 1 - - // when - val result = mockMvc.body(userAnswerUpsertDto).request(Method.POST, "/api/admin/user-answers") - - // then - result.then().statusCode(200) - .apply( - document( - "admin/userAnswer/createMany", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - requestFields( - fieldWithPath("size").type(JsonFieldType.NUMBER) - .description("생성할 유저 답안 수"), - fieldWithPath("userAnswers.[].assignedUserId") - .type(JsonFieldType.STRING) - .description("할당 할 유저 ID ( UUID, null 값 가능 )").optional(), - fieldWithPath("userAnswers.[].validatingUserId") - .type(JsonFieldType.STRING) - .description("검수자로 할당 할 유저 ID ( UUID, null 값 가능. )").optional(), - fieldWithPath("userAnswers.[].answer").type(JsonFieldType.STRING) - .description("유저 답안"), - fieldWithPath("userAnswers.[].problemId").type(JsonFieldType.NUMBER) - .description("답안에 대한 문제 ID"), - ), - responseFields( - fieldWithPath("status") - .type(JsonFieldType.STRING).description("결과 상태"), - fieldWithPath("data.size") - .type(JsonFieldType.NUMBER).description("생성 된 유저 답안 수"), - ), - ), - ) - } - - @Test - fun `Get UserAnswer 200`() { - // given - every { userAnswerService.findUserAnswerById(any()) } returns UserAnswerResponseDto( - 1L, - 1L, - "TEST", - "TEST", - "TEST", - true, - true, - listOf( - GradingStandardResponseDto( - 1L, - "TEST", - 1.0, - GradingStandardType.KEYWORD, - ), - ), - listOf( - GradingStandardResponseDto( - 2L, - "TEST", - 1.0, - GradingStandardType.CONTENT, - ), - ), - listOf(1L), - ) - - // when - val result = mockMvc.request(Method.GET, "/api/admin/user-answers/{user_answer_id}", "1") - - // then - result.then().statusCode(200) - .apply( - document( - "admin/userAnswer/findOne", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - pathParameters( - parameterWithName("user_answer_id").description("유저 답안 id"), - ), - responseFields( - fieldWithPath("status") - .type(JsonFieldType.STRING).description("결과 상태"), - fieldWithPath("data.id") - .type(JsonFieldType.NUMBER).description("유저 답안 ID"), - fieldWithPath("data.problemId") - .type(JsonFieldType.NUMBER).description("문제 ID"), - fieldWithPath("data.problemTitle") - .type(JsonFieldType.STRING).description("문제 제목"), - fieldWithPath("data.problemDescription") - .type(JsonFieldType.STRING).description("문제 내용"), - fieldWithPath("data.answer") - .type(JsonFieldType.STRING).description("유저 답안"), - fieldWithPath("data.isLabeled") - .type(JsonFieldType.BOOLEAN).description("유저 답안 라벨링 유무"), - fieldWithPath("data.isValidated") - .type(JsonFieldType.BOOLEAN).description("유저 답안 검수 유무"), - fieldWithPath("data.keywordsGradingStandards") - .type(JsonFieldType.ARRAY).description("키워드 채점 기준"), - fieldWithPath("data.keywordsGradingStandards.[].id") - .type(JsonFieldType.NUMBER).description("키워드 채점기준 ID"), - fieldWithPath("data.keywordsGradingStandards.[].content") - .type(JsonFieldType.STRING).description("채점기준 내용"), - fieldWithPath("data.keywordsGradingStandards.[].score") - .type(JsonFieldType.NUMBER).description("채점기준 점수"), - fieldWithPath("data.keywordsGradingStandards.[].type") - .type(JsonFieldType.STRING).description("채점기준 타입 ( 'KEYWORD' )"), - fieldWithPath("data.contentGradingStandards") - .type(JsonFieldType.ARRAY).description("내용 채점 기준"), - fieldWithPath("data.contentGradingStandards.[].id") - .type(JsonFieldType.NUMBER).description("내용 채점기준 ID"), - fieldWithPath("data.contentGradingStandards.[].content") - .type(JsonFieldType.STRING).description("내용 채점기준 내용"), - fieldWithPath("data.contentGradingStandards.[].score") - .type(JsonFieldType.NUMBER).description("내용 채점기준 점수"), - fieldWithPath("data.contentGradingStandards.[].type") - .type(JsonFieldType.STRING).description("채점기준 타입 ( 'CONTENT' )"), - fieldWithPath("data.selectedGradingStandards") - .type(JsonFieldType.ARRAY).description("선택 된 채점 기준 IDs"), - ), - ), - ) - } - - @Test - fun `Label UserAnswer 200`() { - // given - every { userAnswerService.labelUserAnswer(any(), any(), any()) } returns 1L - val userAnswerLabelRequestDto = UserAnswerLabelRequestDto( - listOf(1L, 2L), - ) - - // when - val result = mockMvc.body(userAnswerLabelRequestDto) - .request(Method.POST, "/api/admin/user-answers/{user_answer_id}/label", "1") - - // then - result.then().statusCode(200) - .apply( - document( - "admin/userAnswer/label", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - pathParameters( - parameterWithName("user_answer_id").description("유저 답안 id"), - ), - requestFields( - fieldWithPath("selectedGradingStandardIds") - .type(JsonFieldType.ARRAY) - .description("선택한 채점 기준 ID 리스트"), - ), - responseFields( - fieldWithPath("status") - .type(JsonFieldType.STRING).description("결과 상태"), - fieldWithPath("data.id") - .type(JsonFieldType.NUMBER).description("유저 답안 ID"), - ), - ), - ) - } - - @Test - fun `Validate UserAnswer 200`() { - // given - every { userAnswerService.validateUserAnswer(any(), any(), any()) } returns 1L - val userAnswerLabelRequestDto = UserAnswerLabelRequestDto( - listOf(1L, 2L), - ) - - // when - val result = mockMvc.body(userAnswerLabelRequestDto) - .request(Method.POST, "/api/admin/user-answers/{user_answer_id}/validate", "1") - - // then - result.then().statusCode(200) - .apply( - document( - "admin/userAnswer/validate", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - pathParameters( - parameterWithName("user_answer_id").description("유저 답안 id"), - ), - requestFields( - fieldWithPath("selectedGradingStandardIds") - .type(JsonFieldType.ARRAY) - .description("선택한 채점 기준 ID 리스트"), - ), - responseFields( - fieldWithPath("status") - .type(JsonFieldType.STRING).description("결과 상태"), - fieldWithPath("data.id") - .type(JsonFieldType.NUMBER).description("유저 답안 ID"), - ), - ), - ) - } - - @Test - fun `Search UserAnswers 200`() { - // given - every { - userAnswerService.findUserAnswersByQuery( - any(), - any(), - any(), - any(), - any(), - any(), - any(), - any(), - ) - } returns UserAnswerSearchResponseDto( - listOf( - UserAnswerSearchResponseDto.UserAnswerDataDto( - 1, - "title", - "assignor", - "validator", - LocalDateTime.now(), - true, - true, - ), - ), - 1, - 1, - ) - - val assignedBy = "assignor" - val validatedBy = "validator" - val problemTitle = "t" - val answer = "t" - val isLabeled = false - val isValidated = false - val size = 5 - val page = 0 - - // when - val result = mockMvc.request( - Method.GET, - "/api/admin/user-answers?assignedBy=$assignedBy&validatedBy=$validatedBy&problemTitle=" + - "$problemTitle&answer=$answer&isLabeled=$isLabeled&isValidated=$isValidated&size=$size&page=$page", - ) - - // then - result.then().statusCode(200) - .apply( - document( - "admin/userAnswer/search", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - queryParameters( - parameterWithName("id") - .description("ID 검색 ( 옵션 )").optional(), - parameterWithName("assignedBy") - .description("할당된 유저의 닉네임 ( 옵션 )").optional(), - parameterWithName("validatedBy") - .description("검수자로 할당된 유저의 닉네임 ( 옵션 )").optional(), - parameterWithName("problemTitle") - .description("답안을 작성한 문제의 제목 ( 옵션 )").optional(), - parameterWithName("answer") - .description("유저 답안 내용 ( 옵션 )"), - parameterWithName("isLabeled") - .description("라벨링 여부 ( 옵션 )"), - parameterWithName("isValidated") - .description("검수 여부 ( 옵션 )"), - parameterWithName("size") - .description("한 페이지당 가져올 개수 ( 옵션 )"), - parameterWithName("page") - .description("페이지 ( 0부터 시작, 옵션 )").optional(), - ), - responseFields( - fieldWithPath("status").type(JsonFieldType.STRING).description("결과 상태"), - fieldWithPath("data.userAnswers").type(JsonFieldType.ARRAY) - .description("유저 답안 데이터"), - fieldWithPath("data.userAnswers.[].id").type(JsonFieldType.NUMBER) - .description("유저 답안 id"), - fieldWithPath("data.userAnswers.[].problemTitle") - .type(JsonFieldType.STRING) - .description("문제 제목"), - fieldWithPath("data.userAnswers.[].assignedUsername") - .type(JsonFieldType.STRING) - .description("할당 된 유저 닉네임 ( null 가능 ) ").optional(), - fieldWithPath("data.userAnswers.[].validatingUsername") - .type(JsonFieldType.STRING) - .description("검수자로 할당 된 유저 닉네임 ( null 가능 ) ").optional(), - fieldWithPath("data.userAnswers.[].updatedAt") - .type(JsonFieldType.STRING) - .description("수정된 날짜"), - fieldWithPath("data.userAnswers.[].isLabeled") - .type(JsonFieldType.BOOLEAN) - .description("라벨링 여부"), - fieldWithPath("data.userAnswers.[].isValidated") - .type(JsonFieldType.BOOLEAN) - .description("검수 여부"), - fieldWithPath("data.totalPages") - .type(JsonFieldType.NUMBER) - .description("총 페이지 수"), - fieldWithPath("data.totalElements") - .type(JsonFieldType.NUMBER) - .description("검색된 총 유저 응답 수"), - ), - ), - ) - } - - @Test - fun `Assign Labeler to User Answer 200`() { - // given - justRun { userAnswerService.assignLabelUserAnswer(any(), any()) } - val assignUserAnswerDto = AssignUserAnswerDto( - listOf(1L, 2L), - UUID.randomUUID(), - ) - - // when - val result = mockMvc.body(assignUserAnswerDto).request(Method.PUT, "/api/admin/user-answers/assign/label") - - // then - result.then().statusCode(200) - .apply( - document( - "admin/userAnswer/assign/label", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - requestFields( - fieldWithPath("userAnswerIds") - .type(JsonFieldType.ARRAY).description("유저 답안 id 리스트"), - fieldWithPath("assigneeId") - .type(JsonFieldType.STRING).description("할당 할 ADMIN 유저 id"), - ), - responseFields( - fieldWithPath("status") - .type(JsonFieldType.STRING).description("결과 상태"), - fieldWithPath("data.size") - .type(JsonFieldType.NUMBER).description("업데이트 된 유저 답안 size"), - ), - ), - ) - } - - @Test - fun `Assign Validator to User Answer 200`() { - // given - justRun { userAnswerService.assignValidationUserAnswer(any(), any()) } - val assignUserAnswerDto = AssignUserAnswerDto( - listOf(1L, 2L), - UUID.randomUUID(), - ) - - // when - val result = mockMvc.body(assignUserAnswerDto).request(Method.PUT, "/api/admin/user-answers/assign/validate") - - // then - result.then().statusCode(200) - .apply( - document( - "admin/userAnswer/assign/validate", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - requestFields( - fieldWithPath("userAnswerIds") - .type(JsonFieldType.ARRAY).description("유저 답안 id 리스트"), - fieldWithPath("assigneeId") - .type(JsonFieldType.STRING).description("할당 할 ADMIN 유저 id"), - ), - responseFields( - fieldWithPath("status") - .type(JsonFieldType.STRING).description("결과 상태"), - fieldWithPath("data.size") - .type(JsonFieldType.NUMBER).description("업데이트 된 유저 답안 size"), - ), - ), - ) - } - - @Test - fun `Delete User Answer By Id 200`() { - // given - justRun { userAnswerService.removeUserAnswerById(any()) } - - // when - val result = mockMvc.request(Method.DELETE, "/api/admin/user-answers/{user_answer_id}", "1") - - // then - result.then().statusCode(200) - .apply( - document( - "admin/userAnswer/deleteOne", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - pathParameters( - parameterWithName("user_answer_id").description("유저 응답 id"), - ), - responseFields( - fieldWithPath("status") - .type(JsonFieldType.STRING).description("결과 상태"), - fieldWithPath("data") - .type(JsonFieldType.BOOLEAN).description("성공 유무 ( 삭제 성공시 true를 return )"), - ), - ), - ) - } -} diff --git a/src/test/kotlin/io/csbroker/apiserver/service/problem/UserAnswerServiceTest.kt b/src/test/kotlin/io/csbroker/apiserver/service/problem/UserAnswerServiceTest.kt deleted file mode 100644 index 04e0d8b7..00000000 --- a/src/test/kotlin/io/csbroker/apiserver/service/problem/UserAnswerServiceTest.kt +++ /dev/null @@ -1,257 +0,0 @@ -package io.csbroker.apiserver.service.problem - -import io.csbroker.apiserver.auth.ProviderType -import io.csbroker.apiserver.common.exception.EntityNotFoundException -import io.csbroker.apiserver.dto.useranswer.UserAnswerUpsertDto -import io.csbroker.apiserver.model.LongProblem -import io.csbroker.apiserver.model.User -import io.csbroker.apiserver.model.UserAnswer -import io.csbroker.apiserver.repository.problem.GradingStandardRepository -import io.csbroker.apiserver.repository.problem.LongProblemRepository -import io.csbroker.apiserver.repository.problem.UserAnswerGradingStandardRepository -import io.csbroker.apiserver.repository.problem.UserAnswerRepository -import io.csbroker.apiserver.repository.user.UserRepository -import io.mockk.every -import io.mockk.just -import io.mockk.mockk -import io.mockk.runs -import io.mockk.verify -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import org.springframework.data.repository.findByIdOrNull -import java.util.UUID - -class UserAnswerServiceTest { - private val userAnswerRepository = mockk() - private val userRepository = mockk() - private val longProblemRepository = mockk() - private val gradingStandardRepository = mockk() - private val userAnswerGradingStandardRepository = mockk() - private lateinit var service: UserAnswerService - private val assignee = User( - id = UUID.randomUUID(), - email = "test@test.com", - password = "test1234!", - username = "test", - providerType = ProviderType.LOCAL, - ) - private val validator = User( - id = UUID.randomUUID(), - email = "test@test.com", - password = "test1234!", - username = "test", - providerType = ProviderType.LOCAL, - ) - private lateinit var userAnswer: UserAnswer - private lateinit var problem: LongProblem - - @BeforeEach - fun setUp() { - service = UserAnswerServiceImpl( - userAnswerRepository, - userRepository, - longProblemRepository, - gradingStandardRepository, - userAnswerGradingStandardRepository, - ) - problem = LongProblem( - creator = assignee, - title = "title", - description = "description", - ) - problem.id = 1L - userAnswer = UserAnswer( - id = 1L, - answer = "answer", - problem = problem, - assignedUser = assignee, - validatingUser = validator, - ) - } - - @Test - fun `createUserAnswer - 존재하지 않는 문제의 답안을 생성시에 예외가 발생합니다`() { - // given - val problemId = 1L - val requestDto = UserAnswerUpsertDto(null, null, "answer", 1L) - every { longProblemRepository.findByIdOrNull(problemId) } returns null - - // when & then - assertThrows { service.createUserAnswer(requestDto) } - verify { longProblemRepository.findByIdOrNull(problemId) } - } - - @Test - fun `createUserAnswer - 존재하지 않는 유저가 담당자로 등록되면 예외가 발생합니다 `() { - // given - val problemId = problem.id - val notExistUserId = UUID.randomUUID() - val requestDto = UserAnswerUpsertDto( - assignedUserId = notExistUserId, - validatingUserId = validator.id!!, - answer = "answer", - problemId = problemId, - ) - every { longProblemRepository.findByIdOrNull(problemId) } returns problem - every { userRepository.findByIdOrNull(notExistUserId) } returns null - - // when & then - assertThrows { service.createUserAnswer(requestDto) } - verify { longProblemRepository.findByIdOrNull(problemId) } - verify { userRepository.findByIdOrNull(notExistUserId) } - } - - @Test - fun `createUserAnswer - 존재하지 않는 유저가 검수자로 등록되면 예외가 발생합니다 `() { - // given - val problemId = problem.id - val notExistUserId = UUID.randomUUID() - val requestDto = UserAnswerUpsertDto( - assignedUserId = assignee.id!!, - validatingUserId = notExistUserId, - answer = "answer", - problemId = problemId, - ) - every { longProblemRepository.findByIdOrNull(problemId) } returns problem - every { userRepository.findByIdOrNull(assignee.id!!) } returns assignee - every { userRepository.findByIdOrNull(notExistUserId) } returns null - - // when & then - assertThrows { service.createUserAnswer(requestDto) } - verify { longProblemRepository.findByIdOrNull(problemId) } - verify { userRepository.findByIdOrNull(assignee.id!!) } - verify { userRepository.findByIdOrNull(notExistUserId) } - } - - @Test - fun `createUserAnswer - success`() { - // given - val problemId = problem.id - val answer = "answer" - val requestDto = UserAnswerUpsertDto( - assignedUserId = assignee.id!!, - validatingUserId = validator.id!!, - answer = answer, - problemId = problemId, - ) - val userAnswer = UserAnswer( - id = 1L, - answer = answer, - problem = problem, - assignedUser = assignee, - validatingUser = validator, - ) - - every { longProblemRepository.findByIdOrNull(problemId) } returns problem - every { userRepository.findByIdOrNull(assignee.id!!) } returns assignee - every { userRepository.findByIdOrNull(validator.id!!) } returns validator - every { userAnswerRepository.save(any()) } returns userAnswer - - // when & then - val result = service.createUserAnswer(requestDto) - assertEquals(userAnswer.id, result) - verify { longProblemRepository.findByIdOrNull(problemId) } - verify { userRepository.findByIdOrNull(assignee.id!!) } - verify { userRepository.findByIdOrNull(validator.id!!) } - verify { userAnswerRepository.save(any()) } - } - - @Test - fun `findUserAnswerById - 없는 유저 답안을 조회할 시 예외가 발생합니다`() { - // given - every { userAnswerRepository.findByIdOrNull(1L) } returns null - - // when & then - assertThrows { service.findUserAnswerById(1L) } - verify { userAnswerRepository.findByIdOrNull(1L) } - } - - @Test - fun `labelUserAnswer - 없는 유저 답안에 라벨링을 진행할 시 예외가 발생합니다`() { - // given - val email = assignee.email - val userAnswerId = 1L - val selectedGradingStandardIds = listOf(1L, 2L, 3L) - every { userAnswerRepository.findByIdOrNull(userAnswerId) } returns null - - // when & then - assertThrows { - service.labelUserAnswer(email, userAnswerId, selectedGradingStandardIds) - } - verify { userAnswerRepository.findByIdOrNull(userAnswerId) } - } - - @Test - fun `labelUserAnswer - 담당자가 존재하지 않는 답안은 라벨링을 할 수 없습니다`() { - // given - val anotherUserEmail = "another@email.com" - val userAnswerId = userAnswer.id - userAnswer.assignedUser = null - val selectedGradingStandardIds = listOf(1L, 2L, 3L) - every { userAnswerRepository.findByIdOrNull(userAnswerId) } returns userAnswer - - // when & then - assertThrows { - service.labelUserAnswer(anotherUserEmail, userAnswerId, selectedGradingStandardIds) - } - verify { userAnswerRepository.findByIdOrNull(userAnswerId) } - } - - @Test - fun `labelUserAnswer - 담당자가 아닌 유저가 라벨링을 진행할 시 예외가 발생합니다`() { - // given - val anotherUserEmail = "another@email.com" - val userAnswerId = userAnswer.id - val selectedGradingStandardIds = listOf(1L, 2L, 3L) - every { userAnswerRepository.findByIdOrNull(userAnswerId) } returns userAnswer - - // when & then - assertThrows { - service.labelUserAnswer(anotherUserEmail, userAnswerId, selectedGradingStandardIds) - } - verify { userAnswerRepository.findByIdOrNull(1L) } - } - - @Test - fun `labelUserAnswer - 존재하지 않는 채점기준이 존재하면 예외가 발생합니다`() { - // given - val userAnswerId = userAnswer.id - val selectedGradingStandardIds = listOf(1L, 2L, 3L) - - every { userAnswerRepository.findByIdOrNull(userAnswerId) } returns userAnswer - every { gradingStandardRepository.countByIdIn(selectedGradingStandardIds) } returns 0 - - // when & then - assertThrows { - service.labelUserAnswer(assignee.email, userAnswerId, selectedGradingStandardIds) - } - verify { gradingStandardRepository.countByIdIn(selectedGradingStandardIds) } - verify { userAnswerRepository.findByIdOrNull(1L) } - } - - @Test - fun `labelUserAnswer - 라벨링이 완료되면 완료 표식을 남깁니다`() { - // given - val userAnswerId = userAnswer.id - val selectedGradingStandardIds = listOf(1L, 2L, 3L) - - every { userAnswerRepository.findByIdOrNull(userAnswerId) } returns userAnswer - every { gradingStandardRepository.countByIdIn(selectedGradingStandardIds) } returns 3 - every { userAnswerGradingStandardRepository.deleteAllByUserAnswerId(userAnswerId) } just runs - every { userAnswerGradingStandardRepository.batchInsert(userAnswerId, selectedGradingStandardIds) } just runs - - // when & then - assertEquals(false, userAnswer.isLabeled) - val result = service.labelUserAnswer(assignee.email, userAnswerId, selectedGradingStandardIds) - assertEquals(userAnswer.id, result) - assertEquals(true, userAnswer.isLabeled) - verify { gradingStandardRepository.countByIdIn(selectedGradingStandardIds) } - verify { userAnswerRepository.findByIdOrNull(1L) } - verify { userAnswerGradingStandardRepository.deleteAllByUserAnswerId(userAnswerId) } - verify { userAnswerGradingStandardRepository.batchInsert(userAnswerId, selectedGradingStandardIds) } - } - - // Todo : validateUserAnswer 테스트코드 추가 -}