CreateCommentUseCase.kt

package com.example.realworldkotlinspringbootjdbc.usecase.comment

import arrow.core.Either
import arrow.core.Either.Left
import arrow.core.Either.Right
import arrow.core.left
import arrow.core.right
import com.example.realworldkotlinspringbootjdbc.domain.CommentRepository
import com.example.realworldkotlinspringbootjdbc.domain.OtherUser
import com.example.realworldkotlinspringbootjdbc.domain.RegisteredUser
import com.example.realworldkotlinspringbootjdbc.domain.article.Slug
import com.example.realworldkotlinspringbootjdbc.domain.comment.Body
import com.example.realworldkotlinspringbootjdbc.usecase.shared_model.CommentWithAuthor
import com.example.realworldkotlinspringbootjdbc.util.MyError
import org.springframework.stereotype.Service

/**
 * 作成済み記事のコメントの作成
 */
interface CreateCommentUseCase {
    /**
     * 実行
     *
     * @param slug Slug
     * @param body コメントの本文
     * @param currentUser リクエストしたユーザー
     * @return エラー or 作成済み記事のコメント
     */
    fun execute(slug: String?, body: String?, currentUser: RegisteredUser): Either<Error, CommentWithAuthor> =
        throw NotImplementedError()

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

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

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

        /**
         * Body のバリデーション
         * Invalid -> 早期リターン
         */
        val commentBody = Body.new(body).fold(
            { return CreateCommentUseCase.Error.InvalidCommentBody(it).left() },
            { it }
        )

        return when (val createResult = commentRepository.create(validatedSlug, commentBody, currentUser.userId)) {
            /**
             * コメント登録 失敗
             */
            is Left -> when (val createError = createResult.value) {
                is CommentRepository.CreateError.NotFoundArticleBySlug -> CreateCommentUseCase.Error.NotFound(
                    createError
                ).left()
            }

            /**
             * コメント登録 成功
             */
            is Right -> CommentWithAuthor(
                createResult.value,
                OtherUser.newWithoutValidation(
                    userId = currentUser.userId,
                    username = currentUser.username,
                    bio = currentUser.bio,
                    image = currentUser.image,
                    following = false
                )
            ).right()
        }
    }
}