FavoriteController.kt
package com.example.realworldkotlinspringbootjdbc.presentation
import com.example.realworldkotlinspringbootjdbc.openapi.generated.controller.FavoritesApi
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.Article
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.GenericErrorModel
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.GenericErrorModelErrors
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.Profile
import com.example.realworldkotlinspringbootjdbc.openapi.generated.model.SingleArticleResponse
import com.example.realworldkotlinspringbootjdbc.presentation.shared.RealworldAuthenticationUseCaseUnauthorizedException
import com.example.realworldkotlinspringbootjdbc.usecase.favorite.FavoriteUseCase
import com.example.realworldkotlinspringbootjdbc.usecase.favorite.UnfavoriteUseCase
import com.example.realworldkotlinspringbootjdbc.usecase.shared.RealworldAuthenticationUseCase
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.RestController
import java.time.ZoneOffset
@RestController
class FavoriteController(
val realworldAuthenticationUseCase: RealworldAuthenticationUseCase,
val favoriteUseCase: FavoriteUseCase,
val unfavoriteUseCase: UnfavoriteUseCase,
) : FavoritesApi {
override fun createArticleFavorite(authorization: String, slug: String): ResponseEntity<SingleArticleResponse> {
val currentUser = realworldAuthenticationUseCase.execute(authorization).fold(
{ throw RealworldAuthenticationUseCaseUnauthorizedException(it) },
{ it }
)
val favoritedArticle = favoriteUseCase.execute(slug, currentUser).fold(
{ throw FavoriteArticleUseCaseErrorException(it) },
{ it }
)
return ResponseEntity(
SingleArticleResponse(
Article(
slug = favoritedArticle.article.slug.value,
title = favoritedArticle.article.title.value,
description = favoritedArticle.article.description.value,
body = favoritedArticle.article.body.value,
tagList = favoritedArticle.article.tagList.map { it.value },
createdAt = favoritedArticle.article.createdAt.toInstant().atOffset(ZoneOffset.UTC),
updatedAt = favoritedArticle.article.updatedAt.toInstant().atOffset(ZoneOffset.UTC),
favorited = favoritedArticle.article.favorited,
favoritesCount = favoritedArticle.article.favoritesCount,
author = Profile(
username = favoritedArticle.author.username.value,
bio = favoritedArticle.author.bio.value,
image = favoritedArticle.author.image.value,
following = favoritedArticle.author.following
)
)
),
HttpStatus.OK
)
}
data class FavoriteArticleUseCaseErrorException(val error: FavoriteUseCase.Error) : Exception()
@ExceptionHandler(value = [FavoriteArticleUseCaseErrorException::class])
fun onFavoriteArticleUseCaseErrorException(e: FavoriteArticleUseCaseErrorException): ResponseEntity<GenericErrorModel> =
when (e.error) {
/**
* 原因: Slug がバリデーションエラー
*/
is FavoriteUseCase.Error.InvalidSlug -> ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = listOf("slug が不正です"))),
HttpStatus.UNPROCESSABLE_ENTITY
)
/**
* 原因: 記事が見つからなかった
*/
is FavoriteUseCase.Error.NotFound -> ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = listOf("記事が見つかりませんでした"))),
HttpStatus.NOT_FOUND
)
}
override fun deleteArticleFavorite(authorization: String, slug: String): ResponseEntity<SingleArticleResponse> {
val currentUser = realworldAuthenticationUseCase.execute(authorization).fold(
{ throw RealworldAuthenticationUseCaseUnauthorizedException(it) },
{ it }
)
val unfavoriteUseCaseResult = unfavoriteUseCase.execute(slug, currentUser).fold(
{ throw UnfavoriteUseCaseErrorException(it) },
{ it }
)
return ResponseEntity(
SingleArticleResponse(
Article(
slug = unfavoriteUseCaseResult.article.slug.value,
title = unfavoriteUseCaseResult.article.title.value,
description = unfavoriteUseCaseResult.article.description.value,
body = unfavoriteUseCaseResult.article.body.value,
tagList = unfavoriteUseCaseResult.article.tagList.map { it.value },
createdAt = unfavoriteUseCaseResult.article.createdAt.toInstant().atOffset(ZoneOffset.UTC),
updatedAt = unfavoriteUseCaseResult.article.updatedAt.toInstant().atOffset(ZoneOffset.UTC),
favorited = unfavoriteUseCaseResult.article.favorited,
favoritesCount = unfavoriteUseCaseResult.article.favoritesCount,
author = Profile(
username = unfavoriteUseCaseResult.author.username.value,
bio = unfavoriteUseCaseResult.author.bio.value,
image = unfavoriteUseCaseResult.author.image.value,
following = unfavoriteUseCaseResult.author.following
)
)
),
HttpStatus.OK
)
}
data class UnfavoriteUseCaseErrorException(val error: UnfavoriteUseCase.Error) : Exception()
@ExceptionHandler(value = [UnfavoriteUseCaseErrorException::class])
fun onUnfavoriteUseCaseErrorException(e: UnfavoriteUseCaseErrorException): ResponseEntity<GenericErrorModel> =
when (e.error) {
/**
* 原因: Slug がバリデーションエラー
*/
is UnfavoriteUseCase.Error.InvalidSlug -> ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = listOf("slug が不正です"))),
HttpStatus.UNPROCESSABLE_ENTITY
)
/**
* 原因: 記事が見つからなかった
*/
is UnfavoriteUseCase.Error.NotFound -> ResponseEntity(
GenericErrorModel(GenericErrorModelErrors(body = listOf("記事が見つかりませんでした"))),
HttpStatus.NOT_FOUND
)
}
}