ShowProfileUseCase.kt

package com.example.realworldkotlinspringbootjdbc.usecase.profile

import arrow.core.Either
import arrow.core.Either.Left
import arrow.core.Either.Right
import arrow.core.None
import arrow.core.Option
import arrow.core.Some
import arrow.core.left
import arrow.core.right
import arrow.core.toOption
import com.example.realworldkotlinspringbootjdbc.domain.OtherUser
import com.example.realworldkotlinspringbootjdbc.domain.ProfileRepository
import com.example.realworldkotlinspringbootjdbc.domain.RegisteredUser
import com.example.realworldkotlinspringbootjdbc.domain.user.Username
import com.example.realworldkotlinspringbootjdbc.util.MyError
import org.springframework.stereotype.Service

interface ShowProfileUseCase {
    fun execute(username: String?, currentUser: Option<RegisteredUser>): Either<Error, OtherUser> =
        throw NotImplementedError()

    sealed interface Error : MyError {
        data class InvalidUsername(override val errors: List<MyError.ValidationError>) :
            Error,
            MyError.ValidationErrors

        data class NotFound(override val cause: MyError) : Error, MyError.MyErrorWithMyError
    }
}

@Service
class ShowProfileUseCaseImpl(
    val profileRepository: ProfileRepository
) : ShowProfileUseCase {
    override fun execute(
        username: String?,
        currentUser: Option<RegisteredUser>
    ): Either<ShowProfileUseCase.Error, OtherUser> {
        /**
         * Username のバリデーション
         * Invalid -> 早期リターン
         */
        val validatedUsername = Username.new(username).fold(
            { return ShowProfileUseCase.Error.InvalidUsername(it).left() },
            { it }
        )
        return when (currentUser) {
            /**
             * JWT 認証 失敗 or 未ログイン
             */
            is None -> when (val showProfileResult = profileRepository.show(validatedUsername)) {
                /**
                 * プロフィール取得失敗
                 */
                is Left -> when (val error = showProfileResult.value) {
                    is ProfileRepository.ShowError.NotFoundProfileByUsername -> ShowProfileUseCase.Error.NotFound(
                        error
                    ).left()
                }
                /**
                 * プロフィール取得成功
                 */
                is Right -> OtherUser.newWithoutValidation(
                    showProfileResult.value.userId,
                    showProfileResult.value.username,
                    showProfileResult.value.bio,
                    showProfileResult.value.image,
                    showProfileResult.value.following,
                ).right()
            }
            /**
             * JWT 認証成功
             */
            is Some -> when (
                val showProfileResult =
                    profileRepository.show(validatedUsername, currentUser.value.userId.toOption())
            ) {
                /**
                 * プロフィール取得失敗
                 */
                is Left -> when (val error = showProfileResult.value) {
                    is ProfileRepository.ShowError.NotFoundProfileByUsername -> ShowProfileUseCase.Error.NotFound(
                        error
                    ).left()
                }
                /**
                 * プロフィール取得成功
                 */
                is Right -> OtherUser.newWithoutValidation(
                    showProfileResult.value.userId,
                    showProfileResult.value.username,
                    showProfileResult.value.bio,
                    showProfileResult.value.image,
                    showProfileResult.value.following,
                ).right()
            }
        }
    }
}