ListCommentUseCase.kt

package com.example.realworldkotlinspringbootjdbc.usecase.comment

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.left
import com.example.realworldkotlinspringbootjdbc.domain.CommentRepository
import com.example.realworldkotlinspringbootjdbc.domain.RegisteredUser
import com.example.realworldkotlinspringbootjdbc.domain.article.Slug
import com.example.realworldkotlinspringbootjdbc.util.MyError
import org.springframework.stereotype.Service

/**
 * 作成済記事のコメント取得
 *
 * - ログイン済みでリクエストした場合、author との followings が表示される
 */
interface ListCommentUseCase {
    /**
     * 実行
     *
     * @param slug Slug
     * @param currentUser リクエストユーザー or 未ログイン状態
     * @return エラー or Slug に該当する作成済み記事の一覧
     */
    fun execute(slug: String, currentUser: Option<RegisteredUser> = None): Either<Error, List<CommentWithAuthor>> =
        throw NotImplementedError()

    sealed interface Error : MyError {
        data class InvalidSlug(override val errors: List<MyError.ValidationError>) : Error, MyError.ValidationErrors
        data class NotFound(override val cause: MyError) : Error, MyError.MyErrorWithMyError
    }
}

@Service
class ListCommentUseCaseImpl(
    val commentRepository: CommentRepository,
    val commentWithAuthorsQueryModel: CommentWithAuthorsQueryModel
) : ListCommentUseCase {
    override fun execute(
        slug: String,
        currentUser: Option<RegisteredUser>
    ): Either<ListCommentUseCase.Error, List<CommentWithAuthor>> {
        /**
         * Slug のバリデーション
         * Invalid -> 早期リターン
         */
        val validatedSlug = Slug.new(slug).fold(
            { return ListCommentUseCase.Error.InvalidSlug(it).left() },
            { it }
        )

        /**
         * コメントの取得
         * NotFoundArticleBySlug -> 早期リターン
         */
        val commentList = when (val listResult = commentRepository.list(validatedSlug)) {
            is Left -> when (val listError = listResult.value) {
                is CommentRepository.ListError.NotFoundArticleBySlug -> return ListCommentUseCase.Error.NotFound(
                    listError
                ).left()
            }

            is Right -> listResult
        }

        /**
         * comment の author を QueryModel で取得
         */
        return when (
            val commentWithAuthorResult =
                commentWithAuthorsQueryModel.fetchList(commentList.value, currentUser)
        ) {
            is Left -> throw UnsupportedOperationException("現在この分岐に入ることは無い")
            is Right -> commentWithAuthorResult
        }
    }
}