Can vote on Comment
This commit is contained in:
@@ -164,7 +164,7 @@ fun Application.module(env: Env = PROD) {
|
||||
comment(get())
|
||||
commentArticle(get())
|
||||
commentConstitution(get())
|
||||
voteArticle(get())
|
||||
voteArticle(get(), get(), get())
|
||||
voteConstitution(get())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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 <T: UuidEntity>(override var requester: Requester): RepositoryI<
|
||||
override val entityName = VoteEntity::class as KClass<VoteEntity<T>>
|
||||
|
||||
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 anonymous = author.voteAnonymous
|
||||
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) {
|
||||
fun findByCitizen(
|
||||
citizen: CitizenEntity,
|
||||
|
||||
@@ -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<VoteArticlePaths.ArticleVoteRequest> {
|
||||
val content = call.receive<Content>()
|
||||
fun Route.voteArticle(repo: VoteArticleRepository, voteCommentRepo: VoteArticleComment, commentRepo: CommentArticle) {
|
||||
put<ArticleVoteRequest> {
|
||||
val content = call.receive<ArticleVoteRequest.Content>()
|
||||
val vote = VoteEntity(
|
||||
target = it.article,
|
||||
note = content.note,
|
||||
@@ -58,6 +66,19 @@ fun Route.voteArticle(repo: VoteArticleRepository) {
|
||||
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> {
|
||||
val votes = repo.findByCitizen(it.citizen, it.page, it.limit)
|
||||
assertCan(VIEW, votes.result)
|
||||
|
||||
@@ -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<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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user