Can vote on Comment
This commit is contained in:
@@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user