UserAndAuthenticationController.kt
package com.example.realworldkotlinspringbootjdbc.presentation
import com.example.realworldkotlinspringbootjdbc.openapi.generated.controller.UserAndAuthenticationApi
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.GenericErrorModel
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.GenericErrorModelErrors
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.LoginUserRequest
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.NewUserRequest
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.UpdateUserRequest
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.User
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.UserResponse
import com.example.realworldkotlinspringbootjdbc.presentation.shared.RealworldAuthenticationUseCaseUnauthorizedException
import com.example.realworldkotlinspringbootjdbc.presentation.shared.RealworldSessionEncodeErrorException
import com.example.realworldkotlinspringbootjdbc.usecase.shared.RealworldAuthenticationUseCase
import com.example.realworldkotlinspringbootjdbc.usecase.user_and_authentication.LoginUseCase
import com.example.realworldkotlinspringbootjdbc.usecase.user_and_authentication.RegisterUserUseCase
import com.example.realworldkotlinspringbootjdbc.usecase.user_and_authentication.UpdateUserUseCase
import com.example.realworldkotlinspringbootjdbc.util.MySession
import com.example.realworldkotlinspringbootjdbc.util.MySessionJwt
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestController
@RestController
class UserAndAuthenticationController(
val mySessionJwt: MySessionJwt,
val realworldAuthenticationUseCase: RealworldAuthenticationUseCase,
val registerUserUseCase: RegisterUserUseCase,
val loginUseCase: LoginUseCase,
val updateUserUseCase: UpdateUserUseCase,
) : UserAndAuthenticationApi {
override fun createUser(body: NewUserRequest): ResponseEntity<UserResponse> {
val registeredUser = registerUserUseCase.execute(
email = body.user.email,
password = body.user.password,
username = body.user.username,
).fold(
{ throw CreateUserUseCaseErrorException(it) },
{ it }
)
val token = mySessionJwt.encode(MySession(registeredUser.userId, registeredUser.email)).fold(
{ throw RealworldSessionEncodeErrorException(it) },
{ it }
)
return ResponseEntity(
UserResponse(
user = User(
email = registeredUser.email.value,
username = registeredUser.username.value,
bio = registeredUser.bio.value,
image = registeredUser.image.value,
token = token
)
),
HttpStatus.valueOf(201)
)
}
data class CreateUserUseCaseErrorException(val error: RegisterUserUseCase.Error) : Exception()
@ExceptionHandler(value = [CreateUserUseCaseErrorException::class])
fun onCreateUserUseCaseErrorException(e: CreateUserUseCaseErrorException): ResponseEntity<GenericErrorModel> {
val generateResponseEntity: (List<String>) -> ResponseEntity<GenericErrorModel> = { body ->
ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = body)),
HttpStatus.valueOf(422)
)
}
return when (val error = e.error) {
is RegisterUserUseCase.Error.AlreadyRegisteredEmail -> generateResponseEntity(listOf("メールアドレスは既に登録されています"))
is RegisterUserUseCase.Error.AlreadyRegisteredUsername -> generateResponseEntity(listOf("ユーザー名は既に登録されています"))
is RegisterUserUseCase.Error.InvalidUser -> generateResponseEntity(error.errors.map { it.message })
}
}
override fun login(body: LoginUserRequest): ResponseEntity<UserResponse> {
val registeredUser = loginUseCase.execute(
email = body.user.email,
password = body.user.password,
).fold(
{ throw LoginUseCaseErrorException(it) },
{ it }
)
val token: String = mySessionJwt.encode(MySession(registeredUser.userId, registeredUser.email)).fold(
{ throw RealworldSessionEncodeErrorException(it) },
{ it }
)
return ResponseEntity(
UserResponse(
user = User(
email = registeredUser.email.value,
username = registeredUser.username.value,
bio = registeredUser.bio.value,
image = registeredUser.image.value,
token = token
)
),
HttpStatus.valueOf(200)
)
}
data class LoginUseCaseErrorException(val error: LoginUseCase.Error) : Exception()
@ExceptionHandler(value = [LoginUseCaseErrorException::class])
fun onLoginUseCaseErrorException(e: LoginUseCaseErrorException): ResponseEntity<GenericErrorModel> {
val generateResponseEntity: (List<String>) -> ResponseEntity<GenericErrorModel> = { body ->
ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = body)),
HttpStatus.valueOf(401)
)
}
return when (val error = e.error) {
is LoginUseCase.Error.InvalidEmailOrPassword -> generateResponseEntity(error.errors.map { it.message })
is LoginUseCase.Error.Unauthorized -> generateResponseEntity(listOf("認証に失敗しました"))
}
}
/**
* 現在ログイン中の登録済みユーザー取得
* スキーマはOpenAPIで生成されたインターフェースを参照してください
*
* このactionで利用しているメインの UseCase は shared です(ログイン済みであることが前提である操作は全てこのUseCaseを利用します)
* なので、例外もSharedなので、このコントローラで定義していません
*/
override fun getCurrentUser(authorization: String): ResponseEntity<UserResponse> {
val currentUser = realworldAuthenticationUseCase.execute(authorization).fold(
{ throw RealworldAuthenticationUseCaseUnauthorizedException(it) },
{ it }
)
val token = mySessionJwt.encode(MySession(currentUser.userId, currentUser.email)).fold(
{ throw RealworldSessionEncodeErrorException(it) },
{ it }
)
return ResponseEntity(
UserResponse(
user = User(
email = currentUser.email.value,
username = currentUser.username.value,
bio = currentUser.bio.value,
image = currentUser.image.value,
token = token
)
),
HttpStatus.valueOf(200)
)
}
override fun updateCurrentUser(authorization: String, body: UpdateUserRequest): ResponseEntity<UserResponse> {
val currentUser = realworldAuthenticationUseCase.execute(authorization).fold(
{ throw RealworldAuthenticationUseCaseUnauthorizedException(it) },
{ it }
)
val updatedUser = updateUserUseCase.execute(
currentUser = currentUser,
email = body.user.email,
username = body.user.username,
bio = body.user.bio,
image = body.user.image,
).fold(
{ throw UpdateUserUseCaseErrorException(it) },
{ it }
)
val newToken = mySessionJwt.encode(MySession(updatedUser.userId, updatedUser.email)).fold(
{ throw RealworldSessionEncodeErrorException(it) },
{ it }
)
return ResponseEntity(
UserResponse(
user = User(
email = updatedUser.email.value,
username = updatedUser.username.value,
bio = updatedUser.bio.value,
image = updatedUser.image.value,
token = newToken
)
),
HttpStatus.valueOf(200)
)
}
data class UpdateUserUseCaseErrorException(val error: UpdateUserUseCase.Error) : Exception()
@ExceptionHandler(value = [UpdateUserUseCaseErrorException::class])
fun onUpdateUserUseCaseErrorException(e: UpdateUserUseCaseErrorException): ResponseEntity<GenericErrorModel> =
when (val error = e.error) {
is UpdateUserUseCase.Error.AlreadyUsedEmail -> ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = listOf("メールアドレスは既に登録されています"))),
HttpStatus.valueOf(422)
)
is UpdateUserUseCase.Error.AlreadyUsedUsername -> ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = listOf("ユーザー名は既に登録されています"))),
HttpStatus.valueOf(422)
)
is UpdateUserUseCase.Error.InvalidAttributes -> ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = error.errors.map { it.message })),
HttpStatus.valueOf(400)
)
is UpdateUserUseCase.Error.NotFoundUser -> ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = listOf("セッション情報取得時にはユーザーは見つかりましたが、更新時にユーザーが見つかりませんでした(ほぼ有りえません)"))),
HttpStatus.valueOf(422)
)
}
}