From ba2498f877910d764e1f9e288599fa57b3ec713b Mon Sep 17 00:00:00 2001 From: belljun3395 <195850@jnu.ac.kr> Date: Mon, 8 Jul 2024 12:23:04 +0900 Subject: [PATCH] =?UTF-8?q?[Feat/#149]=20=EB=A1=9C=EA=B9=85=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#160)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: log ifo 테이블 추가 * feat: insertLogIfo 메서드 구현 * feat: AddApiLogUseCase 구현 * feat: addApiLogUseCase 컨트롤러 적용 --- .../com/few/api/repo/dao/log/LogIfoDao.kt | 20 +++++ .../repo/dao/log/command/InsertLogCommand.kt | 5 ++ .../few/api/domain/log/AddApiLogUseCase.kt | 19 +++++ .../api/domain/log/dto/AddApiLogUseCaseIn.kt | 5 ++ .../web/controller/admin/ApiLogController.kt | 31 ++++++++ .../controller/admin/request/ApiLogRequest.kt | 5 ++ .../controller/admin/ApiLogControllerTest.kt | 75 +++++++++++++++++++ .../entity/V1.00.0.6__log_ifo_table.sql | 7 ++ 8 files changed, 167 insertions(+) create mode 100644 api-repo/src/main/kotlin/com/few/api/repo/dao/log/LogIfoDao.kt create mode 100644 api-repo/src/main/kotlin/com/few/api/repo/dao/log/command/InsertLogCommand.kt create mode 100644 api/src/main/kotlin/com/few/api/domain/log/AddApiLogUseCase.kt create mode 100644 api/src/main/kotlin/com/few/api/domain/log/dto/AddApiLogUseCaseIn.kt create mode 100644 api/src/main/kotlin/com/few/api/web/controller/admin/ApiLogController.kt create mode 100644 api/src/main/kotlin/com/few/api/web/controller/admin/request/ApiLogRequest.kt create mode 100644 api/src/test/kotlin/com/few/api/web/controller/admin/ApiLogControllerTest.kt create mode 100644 data/db/migration/entity/V1.00.0.6__log_ifo_table.sql diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/log/LogIfoDao.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/log/LogIfoDao.kt new file mode 100644 index 000000000..0e87ee158 --- /dev/null +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/log/LogIfoDao.kt @@ -0,0 +1,20 @@ +package com.few.api.repo.dao.log + +import com.few.api.repo.dao.log.command.InsertLogCommand +import jooq.jooq_dsl.tables.LogIfo +import org.jooq.DSLContext +import org.jooq.JSON +import org.springframework.stereotype.Repository + +@Repository +class LogIfoDao( + private val dslContext: DSLContext +) { + + fun insertLogIfo(command: InsertLogCommand): Long? { + return dslContext.insertInto(LogIfo.LOG_IFO) + .set(LogIfo.LOG_IFO.HISTORY, JSON.valueOf(command.history)) + .returning(LogIfo.LOG_IFO.ID) + .fetchOne()?.id + } +} \ No newline at end of file diff --git a/api-repo/src/main/kotlin/com/few/api/repo/dao/log/command/InsertLogCommand.kt b/api-repo/src/main/kotlin/com/few/api/repo/dao/log/command/InsertLogCommand.kt new file mode 100644 index 000000000..fb9cd7226 --- /dev/null +++ b/api-repo/src/main/kotlin/com/few/api/repo/dao/log/command/InsertLogCommand.kt @@ -0,0 +1,5 @@ +package com.few.api.repo.dao.log.command + +data class InsertLogCommand( + val history: String +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/log/AddApiLogUseCase.kt b/api/src/main/kotlin/com/few/api/domain/log/AddApiLogUseCase.kt new file mode 100644 index 000000000..777bf3fc7 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/log/AddApiLogUseCase.kt @@ -0,0 +1,19 @@ +package com.few.api.domain.log + +import com.few.api.domain.log.dto.AddApiLogUseCaseIn +import com.few.api.repo.dao.log.LogIfoDao +import com.few.api.repo.dao.log.command.InsertLogCommand +import org.springframework.stereotype.Component +import org.springframework.transaction.annotation.Transactional + +@Component +class AddApiLogUseCase( + private val logIfoDao: LogIfoDao +) { + @Transactional + fun execute(useCaseIn: AddApiLogUseCaseIn) { + InsertLogCommand(useCaseIn.history).let { + logIfoDao.insertLogIfo(it) + } + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/domain/log/dto/AddApiLogUseCaseIn.kt b/api/src/main/kotlin/com/few/api/domain/log/dto/AddApiLogUseCaseIn.kt new file mode 100644 index 000000000..3a805f6d9 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/domain/log/dto/AddApiLogUseCaseIn.kt @@ -0,0 +1,5 @@ +package com.few.api.domain.log.dto + +data class AddApiLogUseCaseIn( + val history: String +) \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/web/controller/admin/ApiLogController.kt b/api/src/main/kotlin/com/few/api/web/controller/admin/ApiLogController.kt new file mode 100644 index 000000000..f2a0bf94e --- /dev/null +++ b/api/src/main/kotlin/com/few/api/web/controller/admin/ApiLogController.kt @@ -0,0 +1,31 @@ +package com.few.api.web.controller.admin + +import com.few.api.domain.admin.document.dto.* +import com.few.api.domain.admin.document.usecase.* +import com.few.api.domain.log.AddApiLogUseCase +import com.few.api.domain.log.dto.AddApiLogUseCaseIn +import com.few.api.web.controller.admin.request.* +import com.few.api.web.support.ApiResponse +import com.few.api.web.support.ApiResponseGenerator +import org.springframework.http.HttpStatus +import org.springframework.validation.annotation.Validated +import org.springframework.web.bind.annotation.PostMapping +import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@Validated +@RestController +@RequestMapping(value = ["/api/v1/logs"]) +class ApiLogController( + private val addApiLogUseCase: AddApiLogUseCase +) { + @PostMapping + fun addApiLog(@RequestBody request: ApiLogRequest): ApiResponse { + AddApiLogUseCaseIn(request.history).let { + addApiLogUseCase.execute(it) + } + + return ApiResponseGenerator.success(HttpStatus.OK) + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/few/api/web/controller/admin/request/ApiLogRequest.kt b/api/src/main/kotlin/com/few/api/web/controller/admin/request/ApiLogRequest.kt new file mode 100644 index 000000000..ed73f4fa1 --- /dev/null +++ b/api/src/main/kotlin/com/few/api/web/controller/admin/request/ApiLogRequest.kt @@ -0,0 +1,5 @@ +package com.few.api.web.controller.admin.request + +data class ApiLogRequest( + val history: String +) \ No newline at end of file diff --git a/api/src/test/kotlin/com/few/api/web/controller/admin/ApiLogControllerTest.kt b/api/src/test/kotlin/com/few/api/web/controller/admin/ApiLogControllerTest.kt new file mode 100644 index 000000000..aa2b4d69e --- /dev/null +++ b/api/src/test/kotlin/com/few/api/web/controller/admin/ApiLogControllerTest.kt @@ -0,0 +1,75 @@ +package com.few.api.web.controller.admin + +import com.epages.restdocs.apispec.ResourceDocumentation.resource +import com.epages.restdocs.apispec.ResourceSnippetParameters +import com.epages.restdocs.apispec.Schema +import com.fasterxml.jackson.databind.ObjectMapper +import com.few.api.domain.log.AddApiLogUseCase +import com.few.api.domain.log.dto.AddApiLogUseCaseIn +import com.few.api.web.controller.ControllerTestSpec +import com.few.api.web.controller.admin.request.ApiLogRequest +import com.few.api.web.controller.description.Description +import com.few.api.web.controller.helper.* +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post + +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.mockito.Mockito +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.http.MediaType +import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document +import org.springframework.test.web.servlet.MockMvc +import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status + +class ApiLogControllerTest : ControllerTestSpec() { + @Autowired + private lateinit var objectMapper: ObjectMapper + + @Autowired + private lateinit var mockMvc: MockMvc + + @MockBean + private lateinit var addApiLogUseCase: AddApiLogUseCase + + companion object { + private val BASE_URL = "/api/v1/logs" + private val TAG = "ApiLogControllerTest" + } + + @Test + @DisplayName("[POST] /api/v1/logs") + fun addApiLog() { + // Given + val api = "addApiLog" + val history = + objectMapper.writeValueAsString(mapOf("from" to "email", "to" to "readArticle")) + val request = ApiLogRequest(history) + val content = objectMapper.writeValueAsString(request) + val useCaseIn = AddApiLogUseCaseIn(history) + Mockito.doNothing().`when`(addApiLogUseCase).execute(useCaseIn) + + // When + mockMvc.perform( + post(BASE_URL) + .content(content) + .contentType(MediaType.APPLICATION_JSON) + ).andExpect( + status().is2xxSuccessful + ).andDo( + document( + api.toIdentifier(), + resource( + ResourceSnippetParameters.builder() + .summary(api.toIdentifier()) + .description("API 로그를 기록") + .tag(TAG) + .requestSchema(Schema.schema(api.toRequestSchema())) + .responseSchema(Schema.schema(api.toResponseSchema())).responseFields( + *Description.describe() + ).build() + ) + ) + ) + } +} \ No newline at end of file diff --git a/data/db/migration/entity/V1.00.0.6__log_ifo_table.sql b/data/db/migration/entity/V1.00.0.6__log_ifo_table.sql new file mode 100644 index 000000000..9b952ed14 --- /dev/null +++ b/data/db/migration/entity/V1.00.0.6__log_ifo_table.sql @@ -0,0 +1,7 @@ +-- 로그 기록 테이블 +CREATE TABLE LOG_IFO +( + id BIGINT PRIMARY KEY AUTO_INCREMENT, + history JSON NOT NULL DEFAULT (JSON_OBJECT()), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +);