DeleteCreatedArticleUseCase.kt

package com.example.realworldkotlinspringbootjdbc.usecase.article

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.ArticleRepository
import com.example.realworldkotlinspringbootjdbc.domain.CreatedArticle
import com.example.realworldkotlinspringbootjdbc.domain.CreatedArticleAuthorVerification
import com.example.realworldkotlinspringbootjdbc.domain.DeleteCreatedArticleAndComments
import com.example.realworldkotlinspringbootjdbc.domain.RegisteredUser
import com.example.realworldkotlinspringbootjdbc.domain.article.Slug
import com.example.realworldkotlinspringbootjdbc.util.MyError
import org.springframework.stereotype.Service

interface DeleteCreatedArticleUseCase {
    fun execute(author: RegisteredUser, slug: String?): Either<Error, Unit> = throw NotImplementedError()

    sealed interface Error : MyError {
        data class ValidationError(override val errors: List<MyError.ValidationError>) :
            Error, MyError.ValidationErrors
        data class NotFoundArticle(val slug: Slug) : Error, MyError.Basic
        data class NotAuthor(
            override val cause: MyError,
            val targetArticle: CreatedArticle,
            val notAuthorizedUser: RegisteredUser,
        ) : Error, MyError.MyErrorWithMyError
    }
}

@Service
class DeleteCreatedArticleUseCaseImpl(
    val articleRepository: ArticleRepository,
    val deleteCreatedArticleAndComments: DeleteCreatedArticleAndComments,
) : DeleteCreatedArticleUseCase {
    override fun execute(author: RegisteredUser, slug: String?): Either<DeleteCreatedArticleUseCase.Error, Unit> {
        /**
         * バリデーション
         * Invalid -> 早期リターン
         */
        val validatedSlug = Slug.new(slug).fold(
            { return DeleteCreatedArticleUseCase.Error.ValidationError(errors = it).left() },
            { it }
        )

        /**
         * 検索
         * NotFound -> 早期リターン
         */
        val foundArticle = when (val targetArticle = articleRepository.findBySlug(validatedSlug)) {
            is Left -> when (targetArticle.value) {
                is ArticleRepository.FindBySlugError.NotFound -> return DeleteCreatedArticleUseCase.Error.NotFoundArticle(
                    slug = validatedSlug
                ).left()
            }
            is Right -> targetArticle.value
        }

        /**
         * 著者かどうか
         * 著者ではない -> 早期リターン
         * 著者である -> 削除
         */
        return when (
            val result = CreatedArticleAuthorVerification.verify(
                article = foundArticle,
                user = author
            )
        ) {
            /**
             * 著者ではない
             */
            is Left -> DeleteCreatedArticleUseCase.Error.NotAuthor(
                cause = result.value,
                targetArticle = foundArticle,
                notAuthorizedUser = author
            ).left()

            /**
             * 著者である -> 削除
             */
            is Right -> when (val deleteResult = deleteCreatedArticleAndComments.execute(foundArticle.id)) {
                /**
                 * 削除: 失敗
                 */
                is Left -> when (deleteResult.value) {
                    is DeleteCreatedArticleAndComments.Error.NotFoundArticle -> DeleteCreatedArticleUseCase.Error.NotFoundArticle(
                        slug = validatedSlug
                    ).left()
                }
                /**
                 * 削除: 成功
                 */
                is Right -> Unit.right()
            }
        }
    }
}