diff --git a/src/main/kotlin/team/incube/flooding/domain/ai/adapter/AiChatbotAdapter.kt b/src/main/kotlin/team/incube/flooding/domain/ai/adapter/AiChatbotAdapter.kt index 0c97d734..928dfc87 100644 --- a/src/main/kotlin/team/incube/flooding/domain/ai/adapter/AiChatbotAdapter.kt +++ b/src/main/kotlin/team/incube/flooding/domain/ai/adapter/AiChatbotAdapter.kt @@ -34,11 +34,14 @@ class AiChatbotAdapter( }, ).build() - fun chat(request: SendAiChatRequest): SendAiChatResponse = + fun chat( + request: SendAiChatRequest, + authorization: String, + ): SendAiChatResponse = try { circuitBreaker.executeCheckedSupplier { retry.executeCheckedSupplier { - doChat(request) + doChat(request, authorization) } } } catch (e: CallNotPermittedException) { @@ -50,11 +53,15 @@ class AiChatbotAdapter( throw ExpectedException("AI 챗봇 서버와 통신 중 오류가 발생했습니다.", HttpStatus.BAD_GATEWAY) } - private fun doChat(request: SendAiChatRequest): SendAiChatResponse = + private fun doChat( + request: SendAiChatRequest, + authorization: String, + ): SendAiChatResponse = restClient .post() .uri("/ai/chat") .contentType(MediaType.APPLICATION_JSON) + .header("Authorization", authorization) .body(request) .retrieve() .onStatus({ it.isError }) { _, response -> diff --git a/src/main/kotlin/team/incube/flooding/domain/ai/presentation/controller/AiController.kt b/src/main/kotlin/team/incube/flooding/domain/ai/presentation/controller/AiController.kt index 8fb15d02..cc72543d 100644 --- a/src/main/kotlin/team/incube/flooding/domain/ai/presentation/controller/AiController.kt +++ b/src/main/kotlin/team/incube/flooding/domain/ai/presentation/controller/AiController.kt @@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.tags.Tag import jakarta.validation.Valid import org.springframework.web.bind.annotation.PostMapping import org.springframework.web.bind.annotation.RequestBody +import org.springframework.web.bind.annotation.RequestHeader import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController import team.incube.flooding.domain.ai.presentation.data.request.SendAiChatRequest @@ -34,7 +35,9 @@ class AiController( @PostMapping("/chat") fun chat( @Valid @RequestBody request: SendAiChatRequest, - ): CommonApiResponse = CommonApiResponse.success("OK", sendAiChatService.execute(request)) + @RequestHeader("Authorization") authorization: String, + ): CommonApiResponse = + CommonApiResponse.success("OK", sendAiChatService.execute(request, authorization)) @Operation( summary = "AI 음악 추천", diff --git a/src/main/kotlin/team/incube/flooding/domain/ai/service/SendAiChatService.kt b/src/main/kotlin/team/incube/flooding/domain/ai/service/SendAiChatService.kt index b474444e..d11d4157 100644 --- a/src/main/kotlin/team/incube/flooding/domain/ai/service/SendAiChatService.kt +++ b/src/main/kotlin/team/incube/flooding/domain/ai/service/SendAiChatService.kt @@ -4,5 +4,8 @@ import team.incube.flooding.domain.ai.presentation.data.request.SendAiChatReques import team.incube.flooding.domain.ai.presentation.data.response.SendAiChatResponse interface SendAiChatService { - fun execute(request: SendAiChatRequest): SendAiChatResponse + fun execute( + request: SendAiChatRequest, + authorization: String, + ): SendAiChatResponse } diff --git a/src/main/kotlin/team/incube/flooding/domain/ai/service/impl/SendAiChatServiceImpl.kt b/src/main/kotlin/team/incube/flooding/domain/ai/service/impl/SendAiChatServiceImpl.kt index 5847229b..5d87c2ff 100644 --- a/src/main/kotlin/team/incube/flooding/domain/ai/service/impl/SendAiChatServiceImpl.kt +++ b/src/main/kotlin/team/incube/flooding/domain/ai/service/impl/SendAiChatServiceImpl.kt @@ -10,5 +10,8 @@ import team.incube.flooding.domain.ai.service.SendAiChatService class SendAiChatServiceImpl( private val aiChatbotAdapter: AiChatbotAdapter, ) : SendAiChatService { - override fun execute(request: SendAiChatRequest): SendAiChatResponse = aiChatbotAdapter.chat(request) + override fun execute( + request: SendAiChatRequest, + authorization: String, + ): SendAiChatResponse = aiChatbotAdapter.chat(request, authorization) } diff --git a/src/test/kotlin/team/incube/flooding/domain/ai/service/SendAiChatServiceTest.kt b/src/test/kotlin/team/incube/flooding/domain/ai/service/SendAiChatServiceTest.kt index 97eb1778..df208094 100644 --- a/src/test/kotlin/team/incube/flooding/domain/ai/service/SendAiChatServiceTest.kt +++ b/src/test/kotlin/team/incube/flooding/domain/ai/service/SendAiChatServiceTest.kt @@ -19,10 +19,10 @@ class SendAiChatServiceTest : val service = SendAiChatServiceImpl(adapter) Given("챗봇 어댑터가 정상 응답을 반환할 때") { - every { adapter.chat(any()) } returns SendAiChatResponse("안녕하세요!") + every { adapter.chat(any(), any()) } returns SendAiChatResponse("안녕하세요!") When("서비스를 실행하면") { - val result = service.execute(SendAiChatRequest("안녕")) + val result = service.execute(SendAiChatRequest("안녕"), "Bearer token") Then("챗봇 응답이 반환된다") { result.response shouldBe "안녕하세요!" @@ -32,26 +32,28 @@ class SendAiChatServiceTest : Given("챗봇 어댑터에 요청이 전달될 때") { val requestSlot = slot() - every { adapter.chat(capture(requestSlot)) } returns SendAiChatResponse("응답") + val authorizationSlot = slot() + every { adapter.chat(capture(requestSlot), capture(authorizationSlot)) } returns SendAiChatResponse("응답") When("서비스를 실행하면") { - service.execute(SendAiChatRequest("테스트 메시지")) + service.execute(SendAiChatRequest("테스트 메시지"), "Bearer token") - Then("요청이 어댑터에 그대로 전달된다") { + Then("요청과 인증 토큰이 어댑터에 그대로 전달된다") { requestSlot.captured.userInput shouldBe "테스트 메시지" + authorizationSlot.captured shouldBe "Bearer token" } } } Given("챗봇 서버가 오류를 반환할 때") { - every { adapter.chat(any()) } throws + every { adapter.chat(any(), any()) } throws ExpectedException("AI 챗봇 서버와 통신 중 오류가 발생했습니다.", HttpStatus.BAD_GATEWAY) When("서비스를 실행하면") { Then("BAD_GATEWAY 예외가 발생한다") { val exception = shouldThrow { - service.execute(SendAiChatRequest("안녕")) + service.execute(SendAiChatRequest("안녕"), "Bearer token") } exception.statusCode shouldBe HttpStatus.BAD_GATEWAY }