Create OpinionChoice

This commit is contained in:
2020-02-11 23:03:12 +01:00
parent 42781565dd
commit ec6e39b130
8 changed files with 127 additions and 6 deletions

View File

@@ -39,6 +39,7 @@ import java.util.concurrent.CompletionException
import fr.dcproject.repository.Article as RepositoryArticle
import fr.dcproject.repository.Citizen as RepositoryCitizen
import fr.dcproject.repository.Constitution as RepositoryConstitution
import fr.dcproject.repository.OpinionChoice as OpinionChoiceRepository
import fr.dcproject.repository.User as UserRepository
fun main(args: Array<String>): Unit = io.ktor.server.jetty.EngineMain.main(args)
@@ -121,6 +122,14 @@ fun Application.module(env: Env = PROD) {
get<RepositoryCitizen>().findById(id, true) ?: throw NotFoundException("Citizen $values not found")
}
}
convert<OpinionChoice> {
decode { values, _ ->
val id = values.singleOrNull()?.let { UUID.fromString(it) }
?: throw InternalError("Cannot convert $values to UUID")
get<OpinionChoiceRepository>().findOpinionChoiceById(id) ?: throw NotFoundException("OpinionChoice $values not found")
}
}
}
install(Locations) {
@@ -133,7 +142,8 @@ fun Application.module(env: Env = PROD) {
CitizenVoter(),
CommentVoter(),
VoteVoter(),
FollowVoter()
FollowVoter(),
OpinionChoiceVoter()
)
}
@@ -186,6 +196,7 @@ fun Application.module(env: Env = PROD) {
commentConstitution(get())
voteArticle(get(), get(), get())
voteConstitution(get())
opinionChoice(get())
definition()
}
}

View File

@@ -15,6 +15,7 @@ import fr.dcproject.repository.CommentGeneric as CommentGenericRepository
import fr.dcproject.repository.Constitution as ConstitutionRepository
import fr.dcproject.repository.FollowArticle as FollowArticleRepository
import fr.dcproject.repository.FollowConstitution as FollowConstitutionRepository
import fr.dcproject.repository.OpinionChoice as OpinionChoiceRepository
import fr.dcproject.repository.User as UserRepository
import fr.dcproject.repository.VoteArticle as VoteArticleRepository
import fr.dcproject.repository.VoteComment as VoteCommentRepository
@@ -57,6 +58,7 @@ val Module = module {
single { VoteArticleRepository(get()) }
single { VoteConstitutionRepository(get()) }
single { VoteCommentRepository(get()) }
single { OpinionChoiceRepository(get()) }
single { Migrations(connection = get(), directory = config.sqlFiles) }

View File

@@ -0,0 +1,16 @@
package fr.dcproject.entity
import fr.postgresjson.entity.immutable.EntityCreatedAt
import fr.postgresjson.entity.immutable.EntityCreatedAtImp
import fr.postgresjson.entity.immutable.UuidEntity
import fr.postgresjson.entity.mutable.EntityDeletedAt
import fr.postgresjson.entity.mutable.EntityDeletedAtImp
import java.util.*
class OpinionChoice(
id: UUID,
val name: String,
val target: List<String>
) : UuidEntity(id),
EntityCreatedAt by EntityCreatedAtImp(),
EntityDeletedAt by EntityDeletedAtImp()

View File

@@ -0,0 +1,37 @@
package fr.dcproject.routes
import fr.dcproject.entity.OpinionChoice
import fr.dcproject.security.voter.OpinionVoter.Action.VIEW
import fr.dcproject.security.voter.assertCan
import io.ktor.application.call
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.get
import io.ktor.response.respond
import io.ktor.routing.Route
import fr.dcproject.repository.OpinionChoice as OpinionChoiceRepository
@KtorExperimentalLocationsAPI
object OpinionChoicePaths {
@Location("/opinions/{opinionChoice}")
class OpinionChoiceRequest(val opinionChoice: OpinionChoice)
@Location("/opinions")
class OpinionChoicesRequest(val targets: List<String>)
}
@KtorExperimentalLocationsAPI
fun Route.opinionChoice(repo: OpinionChoiceRepository) {
get<OpinionChoicePaths.OpinionChoiceRequest> {
assertCan(VIEW, it.opinionChoice)
call.respond(it.opinionChoice)
}
get<OpinionChoicePaths.OpinionChoicesRequest> {
val opinions = repo.findOpinionsChoices(it.targets)
assertCan(VIEW, opinions)
call.respond(opinions)
}
}

View File

@@ -0,0 +1,34 @@
package fr.dcproject.security.voter
import fr.dcproject.entity.OpinionChoice
import io.ktor.application.ApplicationCall
class OpinionChoiceVoter : Voter {
enum class Action : ActionI {
VIEW
}
override fun supports(action: ActionI, call: ApplicationCall, subject: Any?): Boolean {
return (action is Action)
.and(subject is OpinionChoice? || subject is List<*>)
}
override fun vote(action: ActionI, call: ApplicationCall, subject: Any?): Vote {
if (action == Action.VIEW) {
if (subject is OpinionChoice) {
return Vote.GRANTED
}
if (subject is List<*>) {
subject.forEach {
if (it !is OpinionChoice) {
return Vote.DENIED
}
}
return Vote.GRANTED
}
return Vote.DENIED
}
return Vote.ABSTAIN
}
}

View File

@@ -0,0 +1,15 @@
create or replace function find_opinion_choice_by_id(_id uuid, out resource json)
language plpgsql as
$$
begin
select to_json(ol) into resource
from opinion_list ol
where (ol.deleted_at <= now()
or ol.deleted_at is null)
and (ol.id = _id);
end;
$$;
-- drop function if exists find_opinion_choice_by_id();
-- select find_opinion_choice_by_id('8c6cb3cc-cac5-93ad-312e-6bd87d9916d9');

View File

@@ -1,4 +1,4 @@
create or replace function find_opinions(out resource json)
create or replace function find_opinion_choices(targets text[] default null, out resource json)
language plpgsql as
$$
begin
@@ -7,8 +7,10 @@ begin
from (
select ol.*
from opinion_list ol
where ol.deleted_at <= now()
or ol.deleted_at is null
where (ol.deleted_at <= now()
or ol.deleted_at is null)
and (ol.target is null or array_length(targets) = 0 or ol.target = any(targets))
order by ol.name
) t;
end;

View File

@@ -90,8 +90,12 @@ begin
'The first opinion must have a name';
assert(
select find_opinions()#>>'{0, name}' = 'Opinion1'
), 'find_opinions mst be return all opinions';
select find_opinion_choices()#>>'{0, name}' = 'Opinion1'
), 'find_opinion_choices mst be return all opinions';
assert(
select (find_opinion_choice_by_id(opinion1)->>'name') = 'Opinion1'
), 'find_opinion_choice_by_id must return the opinion_choice';
-- delete vote and context
delete from opinion;