diff --git a/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCase.kt b/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCase.kt new file mode 100644 index 000000000..c9b90e33d --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/problem/usecase/BrowseProblemsUseCase.kt @@ -0,0 +1,21 @@ +package com.few.api.domain.problem.usecase + +import com.few.api.domain.problem.usecase.`in`.BrowseProblemsUseCaseIn +import com.few.api.domain.problem.usecase.out.BrowseProblemsUseCaseOut +import com.few.api.repo.dao.problem.ProblemDao +import com.few.api.repo.dao.problem.query.SelectProblemsByArticleIdQuery +import org.springframework.stereotype.Component + +@Component +class BrowseProblemsUseCase( + private val problemDao: ProblemDao +) { + + fun execute(useCaseIn: BrowseProblemsUseCaseIn): BrowseProblemsUseCaseOut { + SelectProblemsByArticleIdQuery(useCaseIn.articleId).let { query: SelectProblemsByArticleIdQuery -> + problemDao.selectProblemsByArticleId(query) ?: throw IllegalArgumentException("cannot find problems by articleId: ${query.articleId}") // todo 에러 표준화 + }.let { + return BrowseProblemsUseCaseOut(it.problemIds) + } + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/problem/usecase/in/BrowseProblemsUseCaseIn.kt b/api/src/main/kotlin/com/few/api/domain/problem/usecase/in/BrowseProblemsUseCaseIn.kt new file mode 100644 index 000000000..0b166a37a --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/problem/usecase/in/BrowseProblemsUseCaseIn.kt @@ -0,0 +1,5 @@ +package com.few.api.domain.problem.usecase.`in` + +data class BrowseProblemsUseCaseIn( + val articleId: Long +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/problem/usecase/out/BrowseProblemsUseCaseOut.kt b/api/src/main/kotlin/com/few/api/domain/problem/usecase/out/BrowseProblemsUseCaseOut.kt new file mode 100644 index 000000000..0962e63ae --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/problem/usecase/out/BrowseProblemsUseCaseOut.kt @@ -0,0 +1,5 @@ +package com.few.api.domain.problem.usecase.out + +data class BrowseProblemsUseCaseOut( + val problemIds: List +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/web/controller/problem/ProblemController.kt b/api/src/main/kotlin/com/few/api/web/controller/problem/ProblemController.kt index f8d5cd201..c3946de3b 100644 --- a/api/src/main/kotlin/com/few/api/web/controller/problem/ProblemController.kt +++ b/api/src/main/kotlin/com/few/api/web/controller/problem/ProblemController.kt @@ -1,5 +1,6 @@ package com.few.api.web.controller.problem +import com.few.api.domain.problem.usecase.BrowseProblemsUseCase import com.few.api.web.controller.problem.request.CheckProblemBody import com.few.api.web.controller.problem.response.CheckProblemResponse import com.few.api.web.controller.problem.response.ProblemContents @@ -8,8 +9,10 @@ import com.few.api.web.support.ApiResponse import com.few.api.web.support.ApiResponseGenerator import com.few.api.domain.problem.usecase.CheckProblemUseCase import com.few.api.domain.problem.usecase.ReadProblemUseCase +import com.few.api.domain.problem.usecase.`in`.BrowseProblemsUseCaseIn import com.few.api.domain.problem.usecase.`in`.CheckProblemUseCaseIn import com.few.api.domain.problem.usecase.`in`.ReadProblemUseCaseIn +import com.few.api.web.controller.problem.response.BrowseProblemsResponse import jakarta.validation.Valid import jakarta.validation.constraints.Min import org.springframework.http.HttpStatus @@ -22,10 +25,26 @@ import java.util.* @RestController @RequestMapping(value = ["/api/v1/problems"], produces = [MediaType.APPLICATION_JSON_VALUE]) class ProblemController( + private val browseProblemsUseCase: BrowseProblemsUseCase, private val readProblemUseCase: ReadProblemUseCase, private val checkProblemUseCase: CheckProblemUseCase ) { + @GetMapping + fun browseProblems(@RequestParam(value = "articleId", required = false) articleId: Long?): ApiResponse> { + articleId?.let { id -> + val useCaseOut = BrowseProblemsUseCaseIn(id).let { useCaseIn -> + browseProblemsUseCase.execute(useCaseIn) + } + + val response = BrowseProblemsResponse(useCaseOut.problemIds) + + return ApiResponseGenerator.success(response, HttpStatus.OK) + } + + throw IllegalArgumentException("Invalid parameter") + } + @GetMapping("/{problemId}") fun readProblem( @PathVariable(value = "problemId") diff --git a/api/src/main/kotlin/com/few/api/web/controller/problem/response/BrowseProblemsResponse.kt b/api/src/main/kotlin/com/few/api/web/controller/problem/response/BrowseProblemsResponse.kt new file mode 100644 index 000000000..d8894b81d --- /dev/null +++ b/api/src/main/kotlin/com/few/api/web/controller/problem/response/BrowseProblemsResponse.kt @@ -0,0 +1,5 @@ +package com.few.api.web.controller.problem.response + +data class BrowseProblemsResponse( + val problemIds: List +) \ No newline at end of file diff --git a/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt index f93ea21d0..d502abf6d 100644 --- a/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt +++ b/api/src/test/kotlin/com/few/api/web/controller/problem/ProblemControllerTest.kt @@ -5,14 +5,17 @@ import com.epages.restdocs.apispec.ResourceDocumentation.parameterWithName import com.epages.restdocs.apispec.ResourceSnippetParameters import com.epages.restdocs.apispec.Schema import com.fasterxml.jackson.databind.ObjectMapper +import com.few.api.domain.problem.usecase.BrowseProblemsUseCase import com.few.api.web.controller.ControllerTestSpec import com.few.api.web.controller.description.Description import com.few.api.web.controller.helper.* import com.few.api.web.controller.problem.request.CheckProblemBody import com.few.api.domain.problem.usecase.CheckProblemUseCase import com.few.api.domain.problem.usecase.ReadProblemUseCase +import com.few.api.domain.problem.usecase.`in`.BrowseProblemsUseCaseIn import com.few.api.domain.problem.usecase.`in`.CheckProblemUseCaseIn import com.few.api.domain.problem.usecase.`in`.ReadProblemUseCaseIn +import com.few.api.domain.problem.usecase.out.BrowseProblemsUseCaseOut import com.few.api.domain.problem.usecase.out.CheckProblemUseCaseOut import com.few.api.domain.problem.usecase.out.ReadProblemContentsUseCaseOutDetail import com.few.api.domain.problem.usecase.out.ReadProblemUseCaseOut @@ -40,6 +43,9 @@ class ProblemControllerTest : ControllerTestSpec() { @Autowired private lateinit var problemController: ProblemController + @MockBean + private lateinit var browseProblemsUseCase: BrowseProblemsUseCase + @MockBean private lateinit var readProblemUseCase: ReadProblemUseCase @@ -61,6 +67,57 @@ class ProblemControllerTest : ControllerTestSpec() { .build() } + @Test + @DisplayName("[GET] /api/v1/problems?articleId=") + fun browseProblems() { + // given + val api = "BrowseProblems" + val articleId = 1L + val uri = UriComponentsBuilder.newInstance() + .path(BASE_URL) + .queryParam("articleId", articleId) + .build() + .toUriString() + + val useCaseIn = BrowseProblemsUseCaseIn(articleId) + val useCaseOut = BrowseProblemsUseCaseOut(listOf(1L, 2L, 3L)) + `when`(browseProblemsUseCase.execute(useCaseIn)) + .thenReturn(useCaseOut) + + // when + this.webTestClient.get() + .uri(uri) + .accept(MediaType.APPLICATION_JSON) + .exchange().expectStatus().isOk() + .expectBody().consumeWith( + WebTestClientRestDocumentation.document( + api.toIdentifier(), + ResourceDocumentation.resource( + ResourceSnippetParameters.builder() + .description("아티클 Id로 문제 목록 조회") + .summary(api.toIdentifier()) + .privateResource(false) + .deprecated(false) + .tag(TAG) + .requestSchema(Schema.schema(api.toRequestSchema())) + .queryParameters(parameterWithName("articleId").description("아티클 Id").optional()) + .responseSchema(Schema.schema(api.toResponseSchema())) + .responseFields( + *Description.describe( + arrayOf( + PayloadDocumentation.fieldWithPath("data") + .fieldWithObject("data"), + PayloadDocumentation.fieldWithPath("data.problemIds[]") + .fieldWithArray("문제 Id 목록") + ) + ) + ) + .build() + ) + ) + ) + } + @Test @DisplayName("[GET] /api/v1/problems/{problemId}") fun readProblem() {