Refactoring of updateOpinions (route/repo/query)
Can nw be set multiple opinion on sigle query fix OpinionVoter on CREATE
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
package fr.dcproject.repository
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference
|
||||
import fr.dcproject.entity.Article
|
||||
import fr.dcproject.entity.OpinionAggregation
|
||||
import fr.dcproject.entity.ArticleRef
|
||||
import fr.dcproject.entity.CitizenRef
|
||||
import fr.dcproject.entity.OpinionChoiceRef
|
||||
import fr.dcproject.entity.TargetRef
|
||||
import fr.postgresjson.connexion.Paginated
|
||||
import fr.postgresjson.connexion.Requester
|
||||
@@ -44,6 +45,16 @@ open class OpinionChoice(override val requester: Requester) : RepositoryI {
|
||||
"id" to id
|
||||
)
|
||||
|
||||
/**
|
||||
* find one opinion choices by id
|
||||
*/
|
||||
fun findOpinionChoicesByIds(ids: List<UUID>): List<OpinionChoiceEntity> =
|
||||
requester
|
||||
.getFunction("find_opinion_choices_by_ids")
|
||||
.select(
|
||||
"ids" to ids
|
||||
)
|
||||
|
||||
fun upsertOpinionChoice(opinionChoice: OpinionChoiceEntity): OpinionChoiceEntity = requester
|
||||
.getFunction("upsert_opinion_choice")
|
||||
.selectOne(
|
||||
@@ -51,20 +62,13 @@ open class OpinionChoice(override val requester: Requester) : RepositoryI {
|
||||
)!!
|
||||
}
|
||||
|
||||
open class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requester) {
|
||||
abstract class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requester) {
|
||||
/**
|
||||
* Create an Opinion on target (article,...)
|
||||
*/
|
||||
fun opinion(opinion: OpinionEntity<T>): OpinionAggregation {
|
||||
return requester
|
||||
.getFunction("opinion")
|
||||
.selectOne(
|
||||
"reference" to opinion.target.reference,
|
||||
"target_id" to opinion.target.id,
|
||||
"opinion" to opinion.id,
|
||||
"created_by_id" to opinion.createdBy
|
||||
)!!
|
||||
}
|
||||
abstract fun updateOpinions(choices: List<OpinionChoiceRef>, citizen: CitizenRef, target: TargetRef): List<OpinionEntity<T>>
|
||||
fun updateOpinions(choice: OpinionChoiceRef, citizen: CitizenRef, target: TargetRef): List<OpinionEntity<T>> =
|
||||
updateOpinions(listOf(choice), citizen, target)
|
||||
|
||||
/**
|
||||
* Find opinions of one citizen filtered by target ids
|
||||
@@ -124,13 +128,18 @@ open class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requeste
|
||||
}
|
||||
}
|
||||
|
||||
class OpinionArticle(requester: Requester) : Opinion<Article>(requester) {
|
||||
class OpinionArticle(requester: Requester) : Opinion<ArticleRef>(requester) {
|
||||
/**
|
||||
* Create an Opinion on Article
|
||||
* Create an Opinions on Article
|
||||
*/
|
||||
fun opinion(opinion: OpinionArticleEntity): OpinionArticleEntity {
|
||||
override fun updateOpinions(choices: List<OpinionChoiceRef>, citizen: CitizenRef, target: TargetRef): List<OpinionArticleEntity> {
|
||||
return requester
|
||||
.getFunction("upsert_opinion")
|
||||
.selectOne(opinion) ?: error("query 'upsert_opinion' return null")
|
||||
.getFunction("update_citizen_opinions_by_target_id")
|
||||
.select(
|
||||
"choices_ids" to choices.map { it.id },
|
||||
"citizen_id" to citizen.id,
|
||||
"target_id" to target.id,
|
||||
"target_reference" to target.reference
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,16 @@
|
||||
package fr.dcproject.routes
|
||||
|
||||
import fr.dcproject.citizen
|
||||
import fr.dcproject.entity.Citizen
|
||||
import fr.dcproject.entity.CitizenRef
|
||||
import fr.dcproject.entity.OpinionArticle
|
||||
import fr.dcproject.entity.OpinionChoiceRef
|
||||
import fr.dcproject.entity.request.RequestBuilder
|
||||
import fr.dcproject.entity.request.getContent
|
||||
import fr.dcproject.repository.OpinionChoice
|
||||
import fr.dcproject.security.voter.OpinionVoter.Action.CREATE
|
||||
import fr.dcproject.security.voter.OpinionVoter.Action.VIEW
|
||||
import fr.dcproject.security.voter.assertCan
|
||||
import fr.dcproject.utils.toUUID
|
||||
import io.ktor.application.ApplicationCall
|
||||
import io.ktor.application.call
|
||||
import io.ktor.features.BadRequestException
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||
import io.ktor.locations.Location
|
||||
@@ -47,25 +44,14 @@ object OpinionArticlePaths {
|
||||
*/
|
||||
@Location("/articles/{article}/opinions")
|
||||
@KtorExperimentalAPI
|
||||
class ArticleOpinion(val article: ArticleEntity) : RequestBuilder<OpinionArticle> {
|
||||
class ArticleOpinion(val article: ArticleEntity) : RequestBuilder<List<OpinionChoiceRef>> {
|
||||
|
||||
private class Content(
|
||||
opinionChoice: String
|
||||
) : KoinComponent {
|
||||
val opinionChoice = OpinionChoiceRef(opinionChoice.toUUID())
|
||||
|
||||
fun create(citizen: Citizen, article: ArticleEntity): OpinionArticle {
|
||||
return OpinionArticle(
|
||||
choice = get<OpinionChoice>().findOpinionChoiceById(opinionChoice.id) ?: throw BadRequestException("OpinionChoice not exist: id(${opinionChoice.id})"),
|
||||
target = article,
|
||||
createdBy = citizen
|
||||
)
|
||||
}
|
||||
private class Content(ids: List<String>) : KoinComponent {
|
||||
val ids = ids.map { it.toUUID() }
|
||||
}
|
||||
|
||||
override suspend fun getContent(call: ApplicationCall): OpinionArticle {
|
||||
return call.receive<Content>().create(call.citizen, article)
|
||||
}
|
||||
override suspend fun getContent(call: ApplicationCall): List<OpinionChoiceRef> =
|
||||
call.receive<Content>().ids.map { OpinionChoiceRef(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,9 +81,9 @@ fun Route.opinionArticle(repo: OpinionArticleRepository) {
|
||||
|
||||
put<OpinionArticlePaths.ArticleOpinion> {
|
||||
call.getContent(it)
|
||||
.let { opinion ->
|
||||
assertCan(VIEW, opinion)
|
||||
repo.opinion(opinion)
|
||||
.let { choices ->
|
||||
assertCan(CREATE, it.article)
|
||||
repo.updateOpinions(choices, citizen, it.article)
|
||||
}.let {
|
||||
call.respond(HttpStatusCode.Created, it)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.dcproject.security.voter
|
||||
|
||||
import fr.dcproject.entity.ArticleAuthI
|
||||
import fr.dcproject.entity.Opinion
|
||||
import io.ktor.application.ApplicationCall
|
||||
|
||||
@@ -12,13 +13,17 @@ class OpinionVoter : Voter {
|
||||
|
||||
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
|
||||
return (action is Action)
|
||||
.and(subject is Opinion<*>?)
|
||||
.and(subject is Opinion<*>? || subject is ArticleAuthI<*>)
|
||||
}
|
||||
|
||||
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
|
||||
val user = call.user
|
||||
if (action == Action.CREATE) {
|
||||
return if (user != null) Vote.GRANTED else Vote.DENIED
|
||||
return if (user != null && (
|
||||
(subject is ArticleAuthI<*> && !subject.isDeleted()) ||
|
||||
(subject is Opinion<*> && subject.createdBy.user.id == user.id)
|
||||
)) Vote.GRANTED
|
||||
else Vote.DENIED
|
||||
}
|
||||
|
||||
if (action == Action.VIEW) {
|
||||
|
||||
@@ -38,14 +38,15 @@ enum class Vote {
|
||||
|
||||
private val votersAttributeKey = AttributeKey<List<Voter>>("voters")
|
||||
|
||||
fun ApplicationCall.assertCan(action: ActionI, subject: Any? = null) {
|
||||
if (!can(action, subject)) {
|
||||
fun ApplicationCall.assertCan(action: ActionI, subject: Any? = null, agreeIfNullOrEmpty: Boolean = true) {
|
||||
val isNullOrEmpty = (subject == null || (subject is Collection<*> && subject.isNullOrEmpty()))
|
||||
if (!can(action, subject) && !agreeIfNullOrEmpty && isNullOrEmpty) {
|
||||
throw UnauthorizedException(action)
|
||||
}
|
||||
}
|
||||
|
||||
fun PipelineContext<Unit, ApplicationCall>.assertCan(action: ActionI, subject: Any? = null) =
|
||||
context.assertCan(action, subject)
|
||||
fun PipelineContext<Unit, ApplicationCall>.assertCan(action: ActionI, subject: Any? = null, agreeIfNullOrEmpty: Boolean = true) =
|
||||
context.assertCan(action, subject, agreeIfNullOrEmpty)
|
||||
|
||||
fun PipelineContext<Unit, ApplicationCall>.can(action: ActionI, subject: Any? = null) =
|
||||
context.can(action, subject)
|
||||
|
||||
Reference in New Issue
Block a user