From 2bb0fa58573d93c645ce1e94312c0b0ae338ccb2 Mon Sep 17 00:00:00 2001 From: irene Date: Sun, 25 Jun 2023 19:16:34 +0800 Subject: [PATCH] feat: GET /users/me (#91) * feat: get user me * feat: get user me via jwt * refactor: coding style --- .../application/usecases/GetUserUseCase.kt | 15 ++++++-- .../spring/controllers/OAuth2Controller.kt | 2 +- .../gaas/spring/controllers/UserController.kt | 20 ++++++++--- .../controllers/presenter/GetUserPresenter.kt | 20 +++++++++++ .../controllers/viewmodel/GetUserViewModel.kt | 7 ++++ .../it/controllers/UserControllerTest.kt | 36 ++++++++++--------- 6 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/presenter/GetUserPresenter.kt create mode 100644 spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/viewmodel/GetUserViewModel.kt diff --git a/application/src/main/kotlin/tw/waterballsa/gaas/application/usecases/GetUserUseCase.kt b/application/src/main/kotlin/tw/waterballsa/gaas/application/usecases/GetUserUseCase.kt index 5929914b..a7252da1 100644 --- a/application/src/main/kotlin/tw/waterballsa/gaas/application/usecases/GetUserUseCase.kt +++ b/application/src/main/kotlin/tw/waterballsa/gaas/application/usecases/GetUserUseCase.kt @@ -9,6 +9,17 @@ import javax.inject.Named class GetUserUseCase( private val userRepository: UserRepository, ) { - fun execute(id: User.Id): User = - userRepository.findById(id) ?: throw notFound(User::class).id(id) + fun execute(request: Request, presenter: Presenter) { + with(request) { + val user = userRepository.findByEmail(email) + ?: throw notFound(User::class).identifyBy("email", email) + presenter.present(user) + } + } + + class Request(val email: String) + + interface Presenter { + fun present(user: User) + } } diff --git a/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/OAuth2Controller.kt b/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/OAuth2Controller.kt index 6bb02183..20ed67c9 100644 --- a/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/OAuth2Controller.kt +++ b/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/OAuth2Controller.kt @@ -44,7 +44,7 @@ class OAuth2Controller( } } -fun Jwt.toRequest(): CreateUserUseCase.Request = +private fun Jwt.toRequest(): CreateUserUseCase.Request = CreateUserUseCase.Request( email = claims["email"] as String? ?: throw PlatformException("JWT email should exist."), identityProviderId = subject ?: throw PlatformException("JWT subject should exist.") diff --git a/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/UserController.kt b/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/UserController.kt index 548525cf..a4812668 100644 --- a/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/UserController.kt +++ b/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/UserController.kt @@ -1,15 +1,27 @@ package tw.waterballsa.gaas.spring.controllers +import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.security.oauth2.jwt.Jwt import org.springframework.web.bind.annotation.GetMapping -import org.springframework.web.bind.annotation.PathVariable +import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController import tw.waterballsa.gaas.application.usecases.GetUserUseCase -import tw.waterballsa.gaas.domain.User +import tw.waterballsa.gaas.spring.controllers.presenter.GetUserPresenter +import tw.waterballsa.gaas.spring.controllers.viewmodel.GetUserViewModel @RestController +@RequestMapping("/users") class UserController( private val getUserUseCase: GetUserUseCase ) { - @GetMapping("/users/{id}") - fun getUser(@PathVariable id: String): User = getUserUseCase.execute(User.Id(id)) + @GetMapping("/me") + fun getUser(@AuthenticationPrincipal principal: Jwt): GetUserViewModel { + val request = principal.toRequest() + val presenter = GetUserPresenter() + getUserUseCase.execute(request, presenter) + return presenter.viewModel + } } + +private fun Jwt.toRequest(): GetUserUseCase.Request = + GetUserUseCase.Request(claims["email"] as String) diff --git a/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/presenter/GetUserPresenter.kt b/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/presenter/GetUserPresenter.kt new file mode 100644 index 00000000..494f8d1d --- /dev/null +++ b/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/presenter/GetUserPresenter.kt @@ -0,0 +1,20 @@ +package tw.waterballsa.gaas.spring.controllers.presenter + +import tw.waterballsa.gaas.application.usecases.GetUserUseCase +import tw.waterballsa.gaas.domain.User +import tw.waterballsa.gaas.spring.controllers.viewmodel.GetUserViewModel + +class GetUserPresenter : GetUserUseCase.Presenter { + lateinit var viewModel: GetUserViewModel + + override fun present(user: User) { + viewModel = user.toViewModel() + } + + private fun User.toViewModel(): GetUserViewModel = + GetUserViewModel( + id = id!!.value, + email = email, + nickname = nickname, + ) +} \ No newline at end of file diff --git a/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/viewmodel/GetUserViewModel.kt b/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/viewmodel/GetUserViewModel.kt new file mode 100644 index 00000000..f49e1538 --- /dev/null +++ b/spring/src/main/kotlin/tw/waterballsa/gaas/spring/controllers/viewmodel/GetUserViewModel.kt @@ -0,0 +1,7 @@ +package tw.waterballsa.gaas.spring.controllers.viewmodel + +data class GetUserViewModel( + val id: String, + val email: String, + val nickname: String +) diff --git a/spring/src/test/kotlin/tw/waterballsa/gaas/spring/it/controllers/UserControllerTest.kt b/spring/src/test/kotlin/tw/waterballsa/gaas/spring/it/controllers/UserControllerTest.kt index 1c78527b..2a97f7f1 100644 --- a/spring/src/test/kotlin/tw/waterballsa/gaas/spring/it/controllers/UserControllerTest.kt +++ b/spring/src/test/kotlin/tw/waterballsa/gaas/spring/it/controllers/UserControllerTest.kt @@ -3,7 +3,6 @@ package tw.waterballsa.gaas.spring.it.controllers import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc import org.springframework.test.web.servlet.ResultActions import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath @@ -12,8 +11,6 @@ import tw.waterballsa.gaas.application.repositories.UserRepository import tw.waterballsa.gaas.domain.User import tw.waterballsa.gaas.spring.it.AbstractSpringBootTest - -@AutoConfigureMockMvc(addFilters = false) class UserControllerTest @Autowired constructor( val userRepository: UserRepository, ) : AbstractSpringBootTest() { @@ -24,28 +21,35 @@ class UserControllerTest @Autowired constructor( } @Test - fun givenUserCreated_whenGetUser_thenGetUserSuccessfully() { - val user = User(User.Id("1"), "test@mail.com", "winner5566") - givenUserCreated(user) - findUserById("1").thenGetUserSuccessfully(user) + fun givenUserHasLoggedIn_whenGetUserSelf_thenGetUserSuccessfully() { + givenUserHasLoggedIn() + .whenGetUserSelf() + .thenGetUserSuccessfully() } @Test - fun givenUserNotCreated_whenGetUser_thenUserNotFound() { - findUserById("0").thenUserNotFound() + fun givenUserDoesNotLogIn_whenGetUserSelf_thenUserNotFound() { + givenUserDoesNotLogIn() + .whenGetUserSelf() + .thenUserNotFound() } - private fun givenUserCreated(user: User) { - userRepository.createUser(user) + private fun givenUserDoesNotLogIn(): User = this.mockUser + + private fun givenUserHasLoggedIn(): User { + return userRepository.createUser(mockUser) } - private fun findUserById(id: String): ResultActions = mockMvc.perform(get("/users/$id")) + private fun User.whenGetUserSelf(): ResultActions { + val jwt = identities.first().toJwt() + return mockMvc.perform(get("/users/me").withJwt(jwt)) + } - private fun ResultActions.thenGetUserSuccessfully(user: User) { + private fun ResultActions.thenGetUserSuccessfully() { this.andExpect(status().isOk) - .andExpect(jsonPath("$.id").value(user.id!!.value)) - .andExpect(jsonPath("$.email").value(user.email)) - .andExpect(jsonPath("$.nickname").value(user.nickname)) + .andExpect(jsonPath("$.id").value(mockUser.id!!.value)) + .andExpect(jsonPath("$.email").value(mockUser.email)) + .andExpect(jsonPath("$.nickname").value(mockUser.nickname)) } private fun ResultActions.thenUserNotFound() {