Can vote on Comment

This commit is contained in:
2019-10-06 23:50:42 +02:00
parent 646c199292
commit d90d3117d5
6 changed files with 96 additions and 6 deletions

View File

@@ -164,7 +164,7 @@ fun Application.module(env: Env = PROD) {
comment(get()) comment(get())
commentArticle(get()) commentArticle(get())
commentConstitution(get()) commentConstitution(get())
voteArticle(get()) voteArticle(get(), get(), get())
voteConstitution(get()) voteConstitution(get())
} }
} }

View File

@@ -15,6 +15,7 @@ import fr.dcproject.repository.FollowArticle as FollowArticleRepository
import fr.dcproject.repository.FollowConstitution as FollowConstitutionRepository import fr.dcproject.repository.FollowConstitution as FollowConstitutionRepository
import fr.dcproject.repository.User as UserRepository import fr.dcproject.repository.User as UserRepository
import fr.dcproject.repository.VoteArticle as VoteArticleRepository import fr.dcproject.repository.VoteArticle as VoteArticleRepository
import fr.dcproject.repository.VoteArticleComment as VoteArticleCommentRepository
import fr.dcproject.repository.VoteConstitution as VoteConstitutionRepository import fr.dcproject.repository.VoteConstitution as VoteConstitutionRepository
val config = Config() val config = Config()
@@ -43,6 +44,7 @@ val Module = module {
single { CommentConstitutionRepository(get()) } single { CommentConstitutionRepository(get()) }
single { VoteArticleRepository(get()) } single { VoteArticleRepository(get()) }
single { VoteConstitutionRepository(get()) } single { VoteConstitutionRepository(get()) }
single { VoteArticleCommentRepository(get()) }
single { Migrations(connection = get(), directory = config.sqlFiles) } single { Migrations(connection = get(), directory = config.sqlFiles) }
} }

View File

@@ -2,6 +2,7 @@ package fr.dcproject.repository
import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.core.type.TypeReference
import fr.dcproject.entity.Article import fr.dcproject.entity.Article
import fr.dcproject.entity.Comment
import fr.dcproject.entity.Constitution import fr.dcproject.entity.Constitution
import fr.dcproject.entity.VoteAggregation import fr.dcproject.entity.VoteAggregation
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
@@ -17,7 +18,14 @@ open class Vote <T: UuidEntity>(override var requester: Requester): RepositoryI<
override val entityName = VoteEntity::class as KClass<VoteEntity<T>> override val entityName = VoteEntity::class as KClass<VoteEntity<T>>
fun vote(vote: VoteEntity<T>): VoteAggregation { fun vote(vote: VoteEntity<T>): 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 author = vote.createdBy ?: error("vote must be contain an author")
val anonymous = author.voteAnonymous val anonymous = author.voteAnonymous
return requester return requester
@@ -78,6 +86,21 @@ class VoteArticle (requester: Requester): Vote<Article>(requester) {
) )
} }
class VoteArticleComment (requester: Requester): Vote<Comment<Article>>(requester) {
fun findByCitizen(
citizen: CitizenEntity,
page: Int = 1,
limit: Int = 50
): Paginated<VoteEntity<Comment<Article>>> =
findByCitizen(
citizen.id ?: error("The citizen must have an id"),
"article",
object: TypeReference<List<VoteEntity<Comment<Article>>>>() {},
page,
limit
)
}
class VoteConstitution (requester: Requester): Vote<Constitution>(requester) { class VoteConstitution (requester: Requester): Vote<Constitution>(requester) {
fun findByCitizen( fun findByCitizen(
citizen: CitizenEntity, citizen: CitizenEntity,

View File

@@ -2,7 +2,10 @@ package fr.dcproject.routes
import fr.dcproject.citizen import fr.dcproject.citizen
import fr.dcproject.entity.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.CREATE
import fr.dcproject.security.voter.VoteVoter.Action.VIEW import fr.dcproject.security.voter.VoteVoter.Action.VIEW
import fr.dcproject.security.voter.assertCan import fr.dcproject.security.voter.assertCan
@@ -27,6 +30,11 @@ object VoteArticlePaths {
data class Content(var note: Int) 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") @Location("/citizens/{citizen}/votes/articles")
class CitizenVoteArticleRequest( class CitizenVoteArticleRequest(
val citizen: Citizen, val citizen: Citizen,
@@ -45,9 +53,9 @@ object VoteArticlePaths {
} }
@KtorExperimentalLocationsAPI @KtorExperimentalLocationsAPI
fun Route.voteArticle(repo: VoteArticleRepository) { fun Route.voteArticle(repo: VoteArticleRepository, voteCommentRepo: VoteArticleComment, commentRepo: CommentArticle) {
put<VoteArticlePaths.ArticleVoteRequest> { put<ArticleVoteRequest> {
val content = call.receive<Content>() val content = call.receive<ArticleVoteRequest.Content>()
val vote = VoteEntity( val vote = VoteEntity(
target = it.article, target = it.article,
note = content.note, note = content.note,
@@ -58,6 +66,19 @@ fun Route.voteArticle(repo: VoteArticleRepository) {
call.respond(HttpStatusCode.Created, votes) call.respond(HttpStatusCode.Created, votes)
} }
put<ArticleCommentVoteRequest> {
val comment = commentRepo.findById(it.comment)!!
val content = call.receive<ArticleCommentVoteRequest.Content>()
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<VoteArticlePaths.CitizenVoteArticleRequest> { get<VoteArticlePaths.CitizenVoteArticleRequest> {
val votes = repo.findByCitizen(it.citizen, it.page, it.limit) val votes = repo.findByCitizen(it.citizen, it.page, it.limit)
assertCan(VIEW, votes.result) assertCan(VIEW, votes.result)

View File

@@ -1,6 +1,7 @@
package feature package feature
import fr.dcproject.entity.Citizen import fr.dcproject.entity.Citizen
import fr.dcproject.repository.CommentArticle
import io.cucumber.java8.En import io.cucumber.java8.En
import org.joda.time.DateTime import org.joda.time.DateTime
import org.koin.test.KoinTest import org.koin.test.KoinTest
@@ -8,6 +9,7 @@ import org.koin.test.get
import java.util.* import java.util.*
import java.util.concurrent.CompletionException import java.util.concurrent.CompletionException
import fr.dcproject.entity.Article as ArticleEntity import fr.dcproject.entity.Article as ArticleEntity
import fr.dcproject.entity.Comment as CommentEntity
import fr.dcproject.entity.User as UserEntity import fr.dcproject.entity.User as UserEntity
import fr.dcproject.repository.Article as ArticleRepository import fr.dcproject.repository.Article as ArticleRepository
import fr.dcproject.repository.Citizen as CitizenRepository import fr.dcproject.repository.Citizen as CitizenRepository
@@ -49,5 +51,36 @@ class ArticleSteps: En, KoinTest {
) )
get<ArticleRepository>().upsert(article) get<ArticleRepository>().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<CitizenRepository>().insertWithUser(citizen)
} catch (e: CompletionException) {
citizen = get<CitizenRepository>().findByUsername("john-doe")!!
}
val article = ArticleEntity(
id = UUID.fromString(articleId),
title = "hello",
content = "bla bla bla",
description = "A super article",
createdBy = citizen
)
get<ArticleRepository>().upsert(article)
val comment = CommentEntity(
id = UUID.fromString(commentId),
createdBy = citizen,
target = article,
content = "hello"
)
get<CommentArticle>().comment(comment)
}
} }
} }

View File

@@ -36,3 +36,14 @@ Feature: vote Article
Then the response status code should be 200 Then the response status code should be 200
And the response should contain object: And the response should contain object:
| [0].note | 1 | | [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