From d90d3117d570b2c702e5840b1d2277eea9b2a7de Mon Sep 17 00:00:00 2001 From: Fabrice Lecomte Date: Sun, 6 Oct 2019 23:50:42 +0200 Subject: [PATCH] Can vote on Comment --- src/main/kotlin/fr/dcproject/Application.kt | 2 +- src/main/kotlin/fr/dcproject/Module.kt | 2 ++ .../kotlin/fr/dcproject/repository/Vote.kt | 25 +++++++++++++- .../kotlin/fr/dcproject/routes/VoteArticle.kt | 29 +++++++++++++--- src/test/kotlin/feature/ArticleSteps.kt | 33 +++++++++++++++++++ src/test/resources/feature/vote.feature | 11 +++++++ 6 files changed, 96 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/fr/dcproject/Application.kt b/src/main/kotlin/fr/dcproject/Application.kt index 30b3e15..9cea0c3 100644 --- a/src/main/kotlin/fr/dcproject/Application.kt +++ b/src/main/kotlin/fr/dcproject/Application.kt @@ -164,7 +164,7 @@ fun Application.module(env: Env = PROD) { comment(get()) commentArticle(get()) commentConstitution(get()) - voteArticle(get()) + voteArticle(get(), get(), get()) voteConstitution(get()) } } diff --git a/src/main/kotlin/fr/dcproject/Module.kt b/src/main/kotlin/fr/dcproject/Module.kt index f3f09ee..92338dd 100644 --- a/src/main/kotlin/fr/dcproject/Module.kt +++ b/src/main/kotlin/fr/dcproject/Module.kt @@ -15,6 +15,7 @@ import fr.dcproject.repository.FollowArticle as FollowArticleRepository import fr.dcproject.repository.FollowConstitution as FollowConstitutionRepository import fr.dcproject.repository.User as UserRepository import fr.dcproject.repository.VoteArticle as VoteArticleRepository +import fr.dcproject.repository.VoteArticleComment as VoteArticleCommentRepository import fr.dcproject.repository.VoteConstitution as VoteConstitutionRepository val config = Config() @@ -43,6 +44,7 @@ val Module = module { single { CommentConstitutionRepository(get()) } single { VoteArticleRepository(get()) } single { VoteConstitutionRepository(get()) } + single { VoteArticleCommentRepository(get()) } single { Migrations(connection = get(), directory = config.sqlFiles) } } diff --git a/src/main/kotlin/fr/dcproject/repository/Vote.kt b/src/main/kotlin/fr/dcproject/repository/Vote.kt index df98ea6..16081a6 100644 --- a/src/main/kotlin/fr/dcproject/repository/Vote.kt +++ b/src/main/kotlin/fr/dcproject/repository/Vote.kt @@ -2,6 +2,7 @@ package fr.dcproject.repository import com.fasterxml.jackson.core.type.TypeReference import fr.dcproject.entity.Article +import fr.dcproject.entity.Comment import fr.dcproject.entity.Constitution import fr.dcproject.entity.VoteAggregation import fr.postgresjson.connexion.Paginated @@ -17,7 +18,14 @@ open class Vote (override var requester: Requester): RepositoryI< override val entityName = VoteEntity::class as KClass> fun vote(vote: VoteEntity): VoteAggregation { - val reference = vote.target::class.simpleName!!.toLowerCase() + val target = vote.target + val reference = if (target is Comment<*>) { + target::class.simpleName!!.toLowerCase() + + "_on_" + + target.target::class.simpleName!!.toLowerCase() + } else { + target::class.simpleName!!.toLowerCase() + } val author = vote.createdBy ?: error("vote must be contain an author") val anonymous = author.voteAnonymous return requester @@ -78,6 +86,21 @@ class VoteArticle (requester: Requester): Vote
(requester) { ) } +class VoteArticleComment (requester: Requester): Vote>(requester) { + fun findByCitizen( + citizen: CitizenEntity, + page: Int = 1, + limit: Int = 50 + ): Paginated>> = + findByCitizen( + citizen.id ?: error("The citizen must have an id"), + "article", + object: TypeReference>>>() {}, + page, + limit + ) +} + class VoteConstitution (requester: Requester): Vote(requester) { fun findByCitizen( citizen: CitizenEntity, diff --git a/src/main/kotlin/fr/dcproject/routes/VoteArticle.kt b/src/main/kotlin/fr/dcproject/routes/VoteArticle.kt index 4fbca9e..a9bd41b 100644 --- a/src/main/kotlin/fr/dcproject/routes/VoteArticle.kt +++ b/src/main/kotlin/fr/dcproject/routes/VoteArticle.kt @@ -2,7 +2,10 @@ package fr.dcproject.routes import fr.dcproject.citizen import fr.dcproject.entity.Citizen -import fr.dcproject.routes.VoteArticlePaths.ArticleVoteRequest.Content +import fr.dcproject.repository.CommentArticle +import fr.dcproject.repository.VoteArticleComment +import fr.dcproject.routes.VoteArticlePaths.ArticleCommentVoteRequest +import fr.dcproject.routes.VoteArticlePaths.ArticleVoteRequest import fr.dcproject.security.voter.VoteVoter.Action.CREATE import fr.dcproject.security.voter.VoteVoter.Action.VIEW import fr.dcproject.security.voter.assertCan @@ -27,6 +30,11 @@ object VoteArticlePaths { data class Content(var note: Int) } + @Location("/articles/{article}/comments/{comment}/vote") + class ArticleCommentVoteRequest(val article: ArticleEntity, val comment: UUID) { + data class Content(var note: Int) + } + @Location("/citizens/{citizen}/votes/articles") class CitizenVoteArticleRequest( val citizen: Citizen, @@ -45,9 +53,9 @@ object VoteArticlePaths { } @KtorExperimentalLocationsAPI -fun Route.voteArticle(repo: VoteArticleRepository) { - put { - val content = call.receive() +fun Route.voteArticle(repo: VoteArticleRepository, voteCommentRepo: VoteArticleComment, commentRepo: CommentArticle) { + put { + val content = call.receive() val vote = VoteEntity( target = it.article, note = content.note, @@ -58,6 +66,19 @@ fun Route.voteArticle(repo: VoteArticleRepository) { call.respond(HttpStatusCode.Created, votes) } + put { + val comment = commentRepo.findById(it.comment)!! + val content = call.receive() + val vote = VoteEntity( + target = comment, + note = content.note, + createdBy = this.citizen + ) + assertCan(CREATE, vote) + val votes = voteCommentRepo.vote(vote) + call.respond(HttpStatusCode.Created, votes) + } + get { val votes = repo.findByCitizen(it.citizen, it.page, it.limit) assertCan(VIEW, votes.result) diff --git a/src/test/kotlin/feature/ArticleSteps.kt b/src/test/kotlin/feature/ArticleSteps.kt index c38f1ae..8aab8ce 100644 --- a/src/test/kotlin/feature/ArticleSteps.kt +++ b/src/test/kotlin/feature/ArticleSteps.kt @@ -1,6 +1,7 @@ package feature import fr.dcproject.entity.Citizen +import fr.dcproject.repository.CommentArticle import io.cucumber.java8.En import org.joda.time.DateTime import org.koin.test.KoinTest @@ -8,6 +9,7 @@ import org.koin.test.get import java.util.* import java.util.concurrent.CompletionException import fr.dcproject.entity.Article as ArticleEntity +import fr.dcproject.entity.Comment as CommentEntity import fr.dcproject.entity.User as UserEntity import fr.dcproject.repository.Article as ArticleRepository import fr.dcproject.repository.Citizen as CitizenRepository @@ -49,5 +51,36 @@ class ArticleSteps: En, KoinTest { ) get().upsert(article) } + + Given("I have comment {string} on article {string}") { commentId: String, articleId: String -> + var citizen = Citizen( + name = Citizen.Name("John", "Doe"), + birthday = DateTime.now(), + user = UserEntity(username = "john-doe", plainPassword = "azerty") + ) + + try { + get().insertWithUser(citizen) + } catch (e: CompletionException) { + citizen = get().findByUsername("john-doe")!! + } + + val article = ArticleEntity( + id = UUID.fromString(articleId), + title = "hello", + content = "bla bla bla", + description = "A super article", + createdBy = citizen + ) + get().upsert(article) + + val comment = CommentEntity( + id = UUID.fromString(commentId), + createdBy = citizen, + target = article, + content = "hello" + ) + get().comment(comment) + } } } \ No newline at end of file diff --git a/src/test/resources/feature/vote.feature b/src/test/resources/feature/vote.feature index b4a24ec..bd1360b 100644 --- a/src/test/resources/feature/vote.feature +++ b/src/test/resources/feature/vote.feature @@ -36,3 +36,14 @@ Feature: vote Article Then the response status code should be 200 And the response should contain object: | [0].note | 1 | + + Scenario: Can vote a comment on article + Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd" + And I have comment "ea5c9e87-c99e-4646-a381-2910219e077f" on article "cc9c624e-a27e-42de-af78-ae821c657a68" + When I send a PUT request to "/articles/cc9c624e-a27e-42de-af78-ae821c657a68/comments/ea5c9e87-c99e-4646-a381-2910219e077f/vote" with body: + """ + { + "note": -1 + } + """ + Then the response status code should be 201