Can get votes for one citizen
This commit is contained in:
@@ -3,10 +3,13 @@ package fr.dcproject.repository
|
||||
import fr.dcproject.entity.Article
|
||||
import fr.dcproject.entity.Constitution
|
||||
import fr.dcproject.entity.VoteAggregation
|
||||
import fr.postgresjson.connexion.Paginated
|
||||
import fr.postgresjson.connexion.Requester
|
||||
import fr.postgresjson.entity.UuidEntity
|
||||
import fr.postgresjson.repository.RepositoryI
|
||||
import java.util.*
|
||||
import kotlin.reflect.KClass
|
||||
import fr.dcproject.entity.Citizen as CitizenEntity
|
||||
import fr.dcproject.entity.Vote as VoteEntity
|
||||
|
||||
open class Vote <T: UuidEntity>(override var requester: Requester): RepositoryI<VoteEntity<T>> {
|
||||
@@ -26,6 +29,26 @@ open class Vote <T: UuidEntity>(override var requester: Requester): RepositoryI<
|
||||
"anonymous" to anonymous
|
||||
)!!
|
||||
}
|
||||
|
||||
open fun findByCitizen(
|
||||
citizen: CitizenEntity,
|
||||
page: Int = 1,
|
||||
limit: Int = 50
|
||||
): Paginated<VoteEntity<T>> =
|
||||
findByCitizen(citizen.id ?: error("The citizen must have an id"), page, limit)
|
||||
|
||||
open fun findByCitizen(
|
||||
citizenId: UUID,
|
||||
page: Int = 1,
|
||||
limit: Int = 50
|
||||
): Paginated<VoteEntity<T>> {
|
||||
return requester.run {
|
||||
getFunction("find_votes_by_citizen")
|
||||
.select(page, limit,
|
||||
"created_by_id" to citizenId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VoteArticle (requester: Requester): Vote<Article>(requester)
|
||||
|
||||
14
src/main/kotlin/fr/dcproject/routes/PaginatedRequest.kt
Normal file
14
src/main/kotlin/fr/dcproject/routes/PaginatedRequest.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package fr.dcproject.routes
|
||||
|
||||
interface PaginatedRequestI {
|
||||
val page: Int
|
||||
val limit: Int
|
||||
}
|
||||
|
||||
open class PaginatedRequest(
|
||||
page: Int = 1,
|
||||
limit: Int = 50
|
||||
): PaginatedRequestI {
|
||||
override val page: Int = if (page < 1) 1 else page
|
||||
override val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
|
||||
}
|
||||
@@ -4,11 +4,13 @@ import fr.dcproject.citizen
|
||||
import fr.dcproject.entity.Citizen
|
||||
import fr.dcproject.routes.VoteArticlePaths.ArticleVoteRequest.Content
|
||||
import fr.dcproject.security.voter.VoteVoter.Action.CREATE
|
||||
import fr.dcproject.security.voter.VoteVoter.Action.VIEW
|
||||
import fr.dcproject.security.voter.assertCan
|
||||
import io.ktor.application.call
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.locations.Location
|
||||
import io.ktor.locations.get
|
||||
import io.ktor.locations.put
|
||||
import io.ktor.request.receive
|
||||
import io.ktor.response.respond
|
||||
@@ -19,10 +21,18 @@ import fr.dcproject.repository.VoteArticle as VoteArticleRepository
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
object VoteArticlePaths {
|
||||
@Location("/articles/{article}/vote") class ArticleVoteRequest(val article: ArticleEntity) {
|
||||
@Location("/articles/{article}/vote")
|
||||
class ArticleVoteRequest(val article: ArticleEntity) {
|
||||
data class Content(var note: Int)
|
||||
}
|
||||
@Location("/citizens/{citizen}/votes/articles") class CitizenVoteArticleRequest(val citizen: Citizen)
|
||||
|
||||
@Location("/citizens/{citizen}/votes/articles")
|
||||
class CitizenVoteArticleRequest(
|
||||
val citizen: Citizen,
|
||||
page: Int = 1,
|
||||
limit: Int = 50,
|
||||
val search: String? = null
|
||||
): PaginatedRequestI by PaginatedRequest(page, limit)
|
||||
}
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@@ -38,4 +48,11 @@ fun Route.voteArticle(repo: VoteArticleRepository) {
|
||||
val votes = repo.vote(vote)
|
||||
call.respond(HttpStatusCode.Created, votes)
|
||||
}
|
||||
|
||||
get<VoteArticlePaths.CitizenVoteArticleRequest> {
|
||||
val votes = repo.findByCitizen(it.citizen)
|
||||
assertCan(VIEW, votes.result)
|
||||
|
||||
call.respond(votes)
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,10 @@ class VoteVoter: Voter {
|
||||
}
|
||||
|
||||
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
|
||||
return action is Action && subject is VoteEntity<*>?
|
||||
return action is Action && (
|
||||
subject is VoteEntity<*>?
|
||||
|| subject is List<*>
|
||||
)
|
||||
}
|
||||
|
||||
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
|
||||
@@ -19,8 +22,25 @@ class VoteVoter: Voter {
|
||||
return Vote.GRANTED
|
||||
}
|
||||
|
||||
if (action == Action.VIEW) {
|
||||
return Vote.GRANTED
|
||||
if (action == Action.VIEW && user != null) {
|
||||
if (subject is VoteEntity<*>) {
|
||||
return if (subject.createdBy?.userId != user.id) {
|
||||
Vote.DENIED
|
||||
} else {
|
||||
Vote.GRANTED
|
||||
}
|
||||
}
|
||||
|
||||
if (subject is List<*>) {
|
||||
subject.forEach {
|
||||
if (it !is VoteEntity<*> || it.createdBy?.userId != user.id) {
|
||||
return Vote.DENIED
|
||||
}
|
||||
}
|
||||
return Vote.GRANTED
|
||||
}
|
||||
|
||||
return Vote.DENIED
|
||||
}
|
||||
|
||||
return Vote.ABSTAIN
|
||||
|
||||
130
src/test/kotlin/VoteTest.kt
Normal file
130
src/test/kotlin/VoteTest.kt
Normal file
@@ -0,0 +1,130 @@
|
||||
import fr.dcproject.entity.Article
|
||||
import fr.dcproject.entity.Citizen
|
||||
import fr.dcproject.entity.User
|
||||
import fr.dcproject.entity.Vote
|
||||
import fr.postgresjson.serializer.deserialize
|
||||
import fr.postgresjson.serializer.serialize
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.util.KtorExperimentalAPI
|
||||
import org.amshove.kluent.`should equal`
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.intellij.lang.annotations.Language
|
||||
import org.joda.time.DateTime
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS
|
||||
|
||||
@KtorExperimentalLocationsAPI
|
||||
@KtorExperimentalAPI
|
||||
@TestInstance(PER_CLASS)
|
||||
class VoteTest {
|
||||
@Language("JSON")
|
||||
private val voteJson: String = """
|
||||
{
|
||||
"id": "032acc3d-e8c5-4cb2-9297-bec913ff8d9b",
|
||||
"created_by": {
|
||||
"id": "40c65a43-f9f8-45cd-aa31-953376e7c94a",
|
||||
"name": {
|
||||
"first_name": "Jaque",
|
||||
"last_name": "Bono",
|
||||
"civility": null
|
||||
},
|
||||
"birthday": "2019-10-01T10:59:40.570Z",
|
||||
"user_id": null,
|
||||
"vote_anonymous": true,
|
||||
"follow_anonymous": true,
|
||||
"user": {
|
||||
"id": "f68df389-fb0d-423e-90fd-a140a9ed29b9",
|
||||
"username": "jaque",
|
||||
"blocked_at": null,
|
||||
"plain_password": "azerty",
|
||||
"roles": [],
|
||||
"created_at": null,
|
||||
"updated_at": null
|
||||
},
|
||||
"deleted": false,
|
||||
"created_at": null,
|
||||
"deleted_at": null
|
||||
},
|
||||
"target": {
|
||||
"id": "90f28912-7bd5-4f37-a0ea-8620e3817d51",
|
||||
"title": "Hello world!",
|
||||
"anonymous": true,
|
||||
"content": "bla bla bla",
|
||||
"description": "this is the changement !",
|
||||
"tags": [],
|
||||
"draft": false,
|
||||
"last_version": false,
|
||||
"created_by": {
|
||||
"id": "40c65a43-f9f8-45cd-aa31-953376e7c94a",
|
||||
"name": {
|
||||
"first_name": "Jaque",
|
||||
"last_name": "Bono",
|
||||
"civility": null
|
||||
},
|
||||
"birthday": "2019-10-01T10:59:40.570Z",
|
||||
"user_id": null,
|
||||
"vote_anonymous": true,
|
||||
"follow_anonymous": true,
|
||||
"user": {
|
||||
"id": "f68df389-fb0d-423e-90fd-a140a9ed29b9",
|
||||
"username": "jaque",
|
||||
"blocked_at": null,
|
||||
"plain_password": "azerty",
|
||||
"roles": [],
|
||||
"created_at": null,
|
||||
"updated_at": null
|
||||
},
|
||||
"deleted": false,
|
||||
"created_at": null,
|
||||
"deleted_at": null
|
||||
},
|
||||
"votes": {
|
||||
"up": 0,
|
||||
"neutral": 0,
|
||||
"down": 0,
|
||||
"updated_at": null
|
||||
},
|
||||
"version_id": "48dad61e-c54b-4f4c-9f66-428f90b94045",
|
||||
"version_number": null,
|
||||
"deleted": false,
|
||||
"created_at": null,
|
||||
"deleted_at": null
|
||||
},
|
||||
"note": -1,
|
||||
"anonymous": true,
|
||||
"updated_at": null,
|
||||
"created_at": null
|
||||
}""".trimIndent()
|
||||
|
||||
@Test
|
||||
fun `test Vote Article serialize`() {
|
||||
val user = User(username = "jaque", plainPassword = "azerty")
|
||||
val citizen = Citizen(
|
||||
name = Citizen.Name("Jaque", "Bono"),
|
||||
birthday = DateTime.now(),
|
||||
user = user
|
||||
)
|
||||
val article = Article(
|
||||
title = "Hello world!",
|
||||
content = "bla bla bla",
|
||||
description = "this is the changement !",
|
||||
createdBy = citizen
|
||||
)
|
||||
val vote = Vote(
|
||||
createdBy = citizen,
|
||||
target = article,
|
||||
note = -1
|
||||
)
|
||||
vote.serialize().contains("""Hello world!""") shouldBe true
|
||||
vote.serialize().contains("-1") shouldBe true
|
||||
println(vote.serialize())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `test Vote Article Deserialize`() {
|
||||
val vote: Vote<Article> = voteJson.deserialize()!!
|
||||
vote.id.toString() `should equal` "032acc3d-e8c5-4cb2-9297-bec913ff8d9b"
|
||||
vote.note.toString() `should equal` "-1"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
Feature: vote Article
|
||||
|
||||
Scenario: Vote article
|
||||
Scenario: Can Vote article
|
||||
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd"
|
||||
When I send a PUT request to "/articles/9226c1a3-8091-c3fa-7d0d-c2e98c9bee7b/vote" with body:
|
||||
"""
|
||||
@@ -10,7 +10,7 @@ Feature: vote Article
|
||||
"""
|
||||
Then the response status code should be 201
|
||||
|
||||
Scenario: Vote constitution
|
||||
Scenario: Can Vote constitution
|
||||
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd"
|
||||
When I send a PUT request to "/constitutions/64b1f265-bfb3-332b-eef9-d00f63a3beaa/vote" with body:
|
||||
"""
|
||||
@@ -19,3 +19,13 @@ Feature: vote Article
|
||||
}
|
||||
"""
|
||||
Then the response status code should be 201
|
||||
|
||||
Scenario: Can get votes of current citizen
|
||||
Given I am authenticated as John Doe with id "64b7b379-2298-43ec-b428-ba134930cabd"
|
||||
When I send a GET request to "/citizens/64b7b379-2298-43ec-b428-ba134930cabd/votes/articles"
|
||||
Then the response status code should be 200
|
||||
And the response should contain object:
|
||||
| current_page | 1 |
|
||||
| limit | 50 |
|
||||
| total | 2 |
|
||||
| result[0].note | -1 |
|
||||
|
||||
Reference in New Issue
Block a user