Big refactoring #77

Merged
flecomte merged 166 commits from refactoring-component-and-immutable into master 2021-03-24 19:06:07 +01:00
24 changed files with 244 additions and 170 deletions
Showing only changes of commit c92d0b5640 - Show all commits

View File

@@ -18,6 +18,7 @@ import fr.dcproject.component.comment.article.routes.installCommentArticleRoutes
import fr.dcproject.component.comment.generic.routes.installCommentRoutes import fr.dcproject.component.comment.generic.routes.installCommentRoutes
import fr.dcproject.component.follow.routes.article.installFollowArticleRoutes import fr.dcproject.component.follow.routes.article.installFollowArticleRoutes
import fr.dcproject.component.follow.routes.constitution.installFollowConstitutionRoutes import fr.dcproject.component.follow.routes.constitution.installFollowConstitutionRoutes
import fr.dcproject.component.opinion.routes.installOpinionRoutes
import fr.dcproject.component.views.ConfigViews import fr.dcproject.component.views.ConfigViews
import fr.dcproject.component.workgroup.routes.installWorkgroupRoutes import fr.dcproject.component.workgroup.routes.installWorkgroupRoutes
import fr.dcproject.event.EventNotification import fr.dcproject.event.EventNotification
@@ -26,8 +27,6 @@ import fr.dcproject.routes.commentConstitution
import fr.dcproject.routes.constitution import fr.dcproject.routes.constitution
import fr.dcproject.routes.definition import fr.dcproject.routes.definition
import fr.dcproject.routes.notificationArticle import fr.dcproject.routes.notificationArticle
import fr.dcproject.routes.opinionArticle
import fr.dcproject.routes.opinionChoice
import fr.dcproject.routes.voteArticle import fr.dcproject.routes.voteArticle
import fr.dcproject.routes.voteConstitution import fr.dcproject.routes.voteConstitution
import fr.dcproject.voter.VoterDeniedException import fr.dcproject.voter.VoterDeniedException
@@ -140,6 +139,7 @@ fun Application.module(env: Env = PROD) {
installFollowArticleRoutes() installFollowArticleRoutes()
installFollowConstitutionRoutes() installFollowConstitutionRoutes()
installWorkgroupRoutes() installWorkgroupRoutes()
installOpinionRoutes()
authenticate(optional = true) { authenticate(optional = true) {
/* TODO */ /* TODO */
@@ -147,8 +147,6 @@ fun Application.module(env: Env = PROD) {
commentConstitution(get(), get()) commentConstitution(get(), get())
voteArticle(get(), get(), get(), get()) voteArticle(get(), get(), get(), get())
voteConstitution(get(), get()) voteConstitution(get(), get())
opinionArticle(get(), get())
opinionChoice(get(), get())
definition() definition()
} }

View File

@@ -8,12 +8,13 @@ import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.citizen.CitizenRepository import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.component.comment.generic.CommentRef import fr.dcproject.component.comment.generic.CommentRef
import fr.dcproject.component.opinion.entity.OpinionChoice
import fr.dcproject.component.workgroup.Workgroup import fr.dcproject.component.workgroup.Workgroup
import fr.dcproject.component.workgroup.WorkgroupRef import fr.dcproject.component.workgroup.WorkgroupRef
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.entity.Constitution import fr.dcproject.entity.Constitution
import fr.dcproject.entity.ConstitutionRef import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.repository.OpinionChoice import fr.dcproject.repository.OpinionChoiceRepository
import io.ktor.features.DataConversion import io.ktor.features.DataConversion
import io.ktor.features.NotFoundException import io.ktor.features.NotFoundException
import io.ktor.util.KtorExperimentalAPI import io.ktor.util.KtorExperimentalAPI
@@ -99,11 +100,11 @@ val converters: ConverterDeclaration = {
} }
} }
convert<fr.dcproject.entity.OpinionChoice> { convert<OpinionChoice> {
decode { values, _ -> decode { values, _ ->
val id = values.singleOrNull()?.let { UUID.fromString(it) } val id = values.singleOrNull()?.let { UUID.fromString(it) }
?: throw InternalError("Cannot convert $values to UUID") ?: throw InternalError("Cannot convert $values to UUID")
get<OpinionChoice>().findOpinionChoiceById(id) get<OpinionChoiceRepository>().findOpinionChoiceById(id)
?: throw NotFoundException("OpinionChoice $values not found") ?: throw NotFoundException("OpinionChoice $values not found")
} }
} }

View File

@@ -18,6 +18,8 @@ import fr.dcproject.component.citizen.CitizenVoter
import fr.dcproject.component.comment.article.CommentArticleRepository import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.component.comment.generic.CommentVoter import fr.dcproject.component.comment.generic.CommentVoter
import fr.dcproject.component.follow.FollowVoter import fr.dcproject.component.follow.FollowVoter
import fr.dcproject.component.opinion.OpinionChoiceVoter
import fr.dcproject.component.opinion.OpinionVoter
import fr.dcproject.component.workgroup.WorkgroupRepository import fr.dcproject.component.workgroup.WorkgroupRepository
import fr.dcproject.component.workgroup.WorkgroupVoter import fr.dcproject.component.workgroup.WorkgroupVoter
import fr.dcproject.event.publisher.Publisher import fr.dcproject.event.publisher.Publisher
@@ -25,8 +27,6 @@ import fr.dcproject.messages.Mailer
import fr.dcproject.messages.NotificationEmailSender import fr.dcproject.messages.NotificationEmailSender
import fr.dcproject.repository.CommentConstitutionRepository import fr.dcproject.repository.CommentConstitutionRepository
import fr.dcproject.security.voter.ConstitutionVoter import fr.dcproject.security.voter.ConstitutionVoter
import fr.dcproject.security.voter.OpinionChoiceVoter
import fr.dcproject.security.voter.OpinionVoter
import fr.dcproject.security.voter.VoteVoter import fr.dcproject.security.voter.VoteVoter
import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Connection
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
@@ -44,8 +44,8 @@ import fr.dcproject.component.comment.generic.CommentRepository as CommentGeneri
import fr.dcproject.component.follow.FollowArticleRepository as FollowArticleRepository import fr.dcproject.component.follow.FollowArticleRepository as FollowArticleRepository
import fr.dcproject.component.follow.FollowConstitutionRepository as FollowConstitutionRepository import fr.dcproject.component.follow.FollowConstitutionRepository as FollowConstitutionRepository
import fr.dcproject.repository.Constitution as ConstitutionRepository import fr.dcproject.repository.Constitution as ConstitutionRepository
import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository import fr.dcproject.repository.OpinionChoiceRepository as OpinionChoiceRepository
import fr.dcproject.repository.OpinionChoice as OpinionChoiceRepository import fr.dcproject.repository.OpinionRepositoryArticle as OpinionArticleRepository
import fr.dcproject.repository.VoteArticle as VoteArticleRepository import fr.dcproject.repository.VoteArticle as VoteArticleRepository
import fr.dcproject.repository.VoteComment as VoteCommentRepository import fr.dcproject.repository.VoteComment as VoteCommentRepository
import fr.dcproject.repository.VoteConstitution as VoteConstitutionRepository import fr.dcproject.repository.VoteConstitution as VoteConstitutionRepository

View File

@@ -6,15 +6,15 @@ import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenCartI import fr.dcproject.component.citizen.CitizenCartI
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.opinion.entity.Opinionable
import fr.dcproject.component.opinion.entity.OpinionableImp
import fr.dcproject.component.opinion.entity.Opinions
import fr.dcproject.component.workgroup.WorkgroupCart import fr.dcproject.component.workgroup.WorkgroupCart
import fr.dcproject.component.workgroup.WorkgroupCartI import fr.dcproject.component.workgroup.WorkgroupCartI
import fr.dcproject.component.workgroup.WorkgroupRef import fr.dcproject.component.workgroup.WorkgroupRef
import fr.dcproject.component.workgroup.WorkgroupSimple import fr.dcproject.component.workgroup.WorkgroupSimple
import fr.dcproject.entity.CreatedBy import fr.dcproject.entity.CreatedBy
import fr.dcproject.entity.CreatedByImp import fr.dcproject.entity.CreatedByImp
import fr.dcproject.entity.Opinionable
import fr.dcproject.entity.OpinionableImp
import fr.dcproject.entity.Opinions
import fr.dcproject.entity.TargetI import fr.dcproject.entity.TargetI
import fr.dcproject.entity.TargetRef import fr.dcproject.entity.TargetRef
import fr.dcproject.entity.VersionableRef import fr.dcproject.entity.VersionableRef

View File

@@ -6,8 +6,8 @@ import fr.dcproject.component.article.ArticleViewManager
import fr.dcproject.component.article.ArticleVoter import fr.dcproject.component.article.ArticleVoter
import fr.dcproject.component.article.routes.GetOneArticle.ArticleRequest.Output import fr.dcproject.component.article.routes.GetOneArticle.ArticleRequest.Output
import fr.dcproject.component.auth.citizenOrNull import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.opinion.dto.Opinionable
import fr.dcproject.dto.CreatedAt import fr.dcproject.dto.CreatedAt
import fr.dcproject.dto.Opinionable
import fr.dcproject.dto.Versionable import fr.dcproject.dto.Versionable
import fr.dcproject.dto.Viewable import fr.dcproject.dto.Viewable
import fr.dcproject.dto.Votable import fr.dcproject.dto.Votable

View File

@@ -1,7 +1,7 @@
package fr.dcproject.security.voter package fr.dcproject.component.opinion
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.OpinionChoice import fr.dcproject.component.opinion.entity.OpinionChoice
import fr.dcproject.voter.Voter import fr.dcproject.voter.Voter
import fr.dcproject.voter.VoterResponse import fr.dcproject.voter.VoterResponse

View File

@@ -2,7 +2,7 @@ package fr.dcproject.repository
import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.core.type.TypeReference
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.entity.OpinionForUpdate import fr.dcproject.component.opinion.entity.OpinionForUpdate
import fr.dcproject.entity.TargetRef import fr.dcproject.entity.TargetRef
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
@@ -10,11 +10,11 @@ import fr.postgresjson.repository.RepositoryI
import net.pearx.kasechange.toSnakeCase import net.pearx.kasechange.toSnakeCase
import java.util.UUID import java.util.UUID
import fr.dcproject.component.citizen.Citizen as CitizenEntity import fr.dcproject.component.citizen.Citizen as CitizenEntity
import fr.dcproject.entity.Opinion as OpinionEntity import fr.dcproject.component.opinion.entity.Opinion as OpinionEntity
import fr.dcproject.entity.OpinionArticle as OpinionArticleEntity import fr.dcproject.component.opinion.entity.OpinionArticle as OpinionArticleEntity
import fr.dcproject.entity.OpinionChoice as OpinionChoiceEntity import fr.dcproject.component.opinion.entity.OpinionChoice as OpinionChoiceEntity
open class OpinionChoice(override val requester: Requester) : RepositoryI { open class OpinionChoiceRepository(override val requester: Requester) : RepositoryI {
/** /**
* find all opinion choices * find all opinion choices
* can be filtered by target compatibility * can be filtered by target compatibility
@@ -61,7 +61,7 @@ open class OpinionChoice(override val requester: Requester) : RepositoryI {
)!! )!!
} }
abstract class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requester) { abstract class OpinionRepository<T : TargetRef>(requester: Requester) : OpinionChoiceRepository(requester) {
/** /**
* Create an Opinion on target (article,...) * Create an Opinion on target (article,...)
*/ */
@@ -133,7 +133,7 @@ abstract class Opinion<T : TargetRef>(requester: Requester) : OpinionChoice(requ
} }
} }
class OpinionArticle(requester: Requester) : Opinion<ArticleRef>(requester) { class OpinionRepositoryArticle(requester: Requester) : OpinionRepository<ArticleRef>(requester) {
/** /**
* Update Opinions on Article (Delete old one) * Update Opinions on Article (Delete old one)
*/ */

View File

@@ -1,8 +1,8 @@
package fr.dcproject.security.voter package fr.dcproject.component.opinion
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.opinion.entity.OpinionI
import fr.dcproject.entity.HasTarget import fr.dcproject.entity.HasTarget
import fr.dcproject.entity.OpinionI
import fr.dcproject.voter.Voter import fr.dcproject.voter.Voter
import fr.dcproject.voter.VoterResponse import fr.dcproject.voter.VoterResponse
import fr.postgresjson.entity.EntityCreatedBy import fr.postgresjson.entity.EntityCreatedBy

View File

@@ -1,11 +1,11 @@
package fr.dcproject.dto package fr.dcproject.component.opinion.dto
typealias Opinions = Map<String, Int> typealias Opinions = Map<String, Int>
interface Opinionable { interface Opinionable {
val opinions: Opinions val opinions: Opinions
class Imp(parent: fr.dcproject.entity.Opinionable) : Opinionable { class Imp(parent: fr.dcproject.component.opinion.entity.Opinionable) : Opinionable {
override val opinions: Opinions = parent.opinions override val opinions: Opinions = parent.opinions
} }
} }

View File

@@ -1,10 +1,14 @@
package fr.dcproject.entity package fr.dcproject.component.opinion.entity
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenBasic import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenBasicI import fr.dcproject.component.citizen.CitizenBasicI
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.ExtraI
import fr.dcproject.entity.HasTarget
import fr.dcproject.entity.TargetI
import fr.dcproject.entity.TargetRef
import fr.postgresjson.entity.EntityCreatedAt import fr.postgresjson.entity.EntityCreatedAt
import fr.postgresjson.entity.EntityCreatedAtImp import fr.postgresjson.entity.EntityCreatedAtImp
import fr.postgresjson.entity.EntityCreatedBy import fr.postgresjson.entity.EntityCreatedBy

View File

@@ -1,4 +1,4 @@
package fr.dcproject.entity package fr.dcproject.component.opinion.entity
import fr.postgresjson.entity.EntityCreatedAt import fr.postgresjson.entity.EntityCreatedAt
import fr.postgresjson.entity.EntityCreatedAtImp import fr.postgresjson.entity.EntityCreatedAtImp

View File

@@ -1,4 +1,4 @@
package fr.dcproject.entity package fr.dcproject.component.opinion.entity
typealias Opinions = Map<String, Int> typealias Opinions = Map<String, Int>
typealias OpinionsMutable = MutableMap<String, Int> typealias OpinionsMutable = MutableMap<String, Int>

View File

@@ -0,0 +1,38 @@
package fr.dcproject.component.opinion.routes
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.opinion.OpinionVoter
import fr.dcproject.component.opinion.entity.Opinion
import fr.dcproject.utils.toUUID
import fr.dcproject.voter.assert
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 org.koin.core.KoinComponent
import java.util.UUID
import fr.dcproject.component.citizen.Citizen as CitizenEntity
import fr.dcproject.repository.OpinionRepositoryArticle as OpinionArticleRepository
@KtorExperimentalLocationsAPI
object GetCitizenOpinions {
/**
* Get all Opinion of citizen on targets by target ids
*/
@Location("/citizens/{citizen}/opinions")
class CitizenOpinions(val citizen: CitizenEntity, id: List<String>) : KoinComponent {
val id: List<UUID> = id.toUUID()
}
fun Route.getCitizenOpinions(repo: OpinionArticleRepository, voter: OpinionVoter) {
get<CitizenOpinions> {
val opinionsEntities: List<Opinion<ArticleRef>> = repo.findCitizenOpinionsByTargets(it.citizen, it.id)
voter.assert { canView(opinionsEntities, citizenOrNull) }
call.respond(opinionsEntities)
}
}
}

View File

@@ -0,0 +1,37 @@
package fr.dcproject.component.opinion.routes
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.opinion.OpinionVoter
import fr.dcproject.routes.PaginatedRequest
import fr.dcproject.routes.PaginatedRequestI
import fr.dcproject.voter.assert
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.OpinionRepositoryArticle as OpinionArticleRepository
@KtorExperimentalLocationsAPI
object GetMyOpinionsArticle {
/**
* Get paginated opinions of citizen for all articles
*/
@Location("/citizens/{citizen}/opinions/articles")
class CitizenOpinionsArticleRequest(
val citizen: CitizenRef,
page: Int = 1,
limit: Int = 50
) : PaginatedRequestI by PaginatedRequest(page, limit)
fun Route.getMyOpinionsArticle(repo: OpinionArticleRepository, voter: OpinionVoter) {
get<CitizenOpinionsArticleRequest> {
val opinions = repo.findCitizenOpinions(citizen, it.page, it.limit)
voter.assert { canView(opinions.result, citizenOrNull) }
call.respond(opinions)
}
}
}

View File

@@ -0,0 +1,26 @@
package fr.dcproject.component.opinion.routes
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.opinion.OpinionChoiceVoter
import fr.dcproject.component.opinion.entity.OpinionChoice
import fr.dcproject.voter.assert
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
@KtorExperimentalLocationsAPI
object GetOpinionChoice {
@Location("/opinions/{opinionChoice}")
class OpinionChoiceRequest(val opinionChoice: OpinionChoice)
fun Route.getOpinionChoice(voter: OpinionChoiceVoter) {
get<OpinionChoiceRequest> {
voter.assert { canView(it.opinionChoice, citizenOrNull) }
call.respond(it.opinionChoice)
}
}
}

View File

@@ -0,0 +1,27 @@
package fr.dcproject.component.opinion.routes
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.opinion.OpinionChoiceVoter
import fr.dcproject.voter.assert
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.OpinionChoiceRepository as OpinionChoiceRepository
@KtorExperimentalLocationsAPI
object GetOpinionChoices {
@Location("/opinions")
class OpinionChoicesRequest(val targets: List<String> = emptyList())
fun Route.getOpinionChoices(repo: OpinionChoiceRepository, voter: OpinionChoiceVoter) {
get<OpinionChoicesRequest> {
val opinionChoices = repo.findOpinionsChoices(it.targets)
voter.assert { canView(opinionChoices, citizenOrNull) }
call.respond(opinionChoices)
}
}
}

View File

@@ -0,0 +1,50 @@
package fr.dcproject.component.opinion.routes
import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.opinion.OpinionVoter
import fr.dcproject.component.opinion.entity.OpinionChoiceRef
import fr.dcproject.component.opinion.entity.OpinionForUpdate
import fr.dcproject.utils.toUUID
import fr.dcproject.voter.assert
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.put
import io.ktor.request.receive
import io.ktor.response.respond
import io.ktor.routing.Route
import java.util.UUID
import fr.dcproject.repository.OpinionRepositoryArticle as OpinionArticleRepository
@KtorExperimentalLocationsAPI
object OpinionArticle {
/**
* Put an opinion on one article
*/
@Location("/articles/{article}/opinions")
class ArticleOpinion(val article: ArticleForView) {
class Body(ids: List<String>) {
val ids: List<UUID> = ids.map { it.toUUID() }
}
}
fun Route.setOpinionOnArticle(repo: OpinionArticleRepository, voter: OpinionVoter) {
put<ArticleOpinion> {
call.receive<ArticleOpinion.Body>().ids.map { id ->
OpinionForUpdate(
choice = OpinionChoiceRef(id),
target = it.article,
createdBy = citizen
)
}.let { opinions ->
voter.assert { canCreate(opinions, citizenOrNull) }
repo.updateOpinions(opinions)
}.let {
call.respond(HttpStatusCode.Created, it)
}
}
}
}

View File

@@ -0,0 +1,22 @@
package fr.dcproject.component.opinion.routes
import fr.dcproject.component.opinion.routes.GetCitizenOpinions.getCitizenOpinions
import fr.dcproject.component.opinion.routes.GetMyOpinionsArticle.getMyOpinionsArticle
import fr.dcproject.component.opinion.routes.GetOpinionChoice.getOpinionChoice
import fr.dcproject.component.opinion.routes.GetOpinionChoices.getOpinionChoices
import fr.dcproject.component.opinion.routes.OpinionArticle.setOpinionOnArticle
import io.ktor.auth.authenticate
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.routing.Routing
import org.koin.ktor.ext.get
@KtorExperimentalLocationsAPI
fun Routing.installOpinionRoutes() {
authenticate(optional = true) {
getCitizenOpinions(get(), get())
getMyOpinionsArticle(get(), get())
setOpinionOnArticle(get(), get())
getOpinionChoice(get())
getOpinionChoices(get(), get())
}
}

View File

@@ -3,6 +3,7 @@ package fr.dcproject.entity
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.comment.generic.CommentRef import fr.dcproject.component.comment.generic.CommentRef
import fr.dcproject.component.opinion.entity.OpinionRef
import fr.postgresjson.entity.EntityCreatedAt import fr.postgresjson.entity.EntityCreatedAt
import fr.postgresjson.entity.EntityCreatedBy import fr.postgresjson.entity.EntityCreatedBy
import fr.postgresjson.entity.UuidEntity import fr.postgresjson.entity.UuidEntity

View File

@@ -1,92 +0,0 @@
package fr.dcproject.routes
import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.Opinion
import fr.dcproject.entity.OpinionChoiceRef
import fr.dcproject.entity.OpinionForUpdate
import fr.dcproject.security.voter.OpinionVoter
import fr.dcproject.utils.toUUID
import fr.dcproject.voter.assert
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
import io.ktor.routing.Route
import io.ktor.util.KtorExperimentalAPI
import org.koin.core.KoinComponent
import org.koin.core.get
import java.util.UUID
import fr.dcproject.component.citizen.Citizen as CitizenEntity
import fr.dcproject.repository.OpinionArticle as OpinionArticleRepository
@KtorExperimentalLocationsAPI
object OpinionArticlePaths {
/**
* Get paginated opinions of citizen for all articles
*/
@Location("/citizens/{citizen}/opinions/articles")
class CitizenOpinionArticleRequest(
val citizen: CitizenRef,
page: Int = 1,
limit: Int = 50
) : PaginatedRequestI by PaginatedRequest(page, limit)
/**
* Put an opinion on one article
*/
@Location("/articles/{article}/opinions")
@KtorExperimentalAPI
class ArticleOpinion(val article: ArticleForView) {
class Body(ids: List<String>) {
val ids: List<UUID> = ids.map { it.toUUID() }
}
}
/**
* Get all Opinion of citizen on targets by target ids
*/
@Location("/citizens/{citizen}/opinions")
class CitizenOpinions(val citizen: CitizenEntity, id: List<String>) : KoinComponent {
val id: List<UUID> = id.toUUID()
val opinionsEntities: List<Opinion<ArticleRef>> = get<OpinionArticleRepository>()
.findCitizenOpinionsByTargets(citizen, this.id)
}
}
@KtorExperimentalAPI
@KtorExperimentalLocationsAPI
fun Route.opinionArticle(repo: OpinionArticleRepository, voter: OpinionVoter) {
get<OpinionArticlePaths.CitizenOpinionArticleRequest> {
val opinions = repo.findCitizenOpinions(citizen, it.page, it.limit)
call.respond(opinions)
}
get<OpinionArticlePaths.CitizenOpinions> {
voter.assert { canView(it.opinionsEntities, citizenOrNull) }
call.respond(it.opinionsEntities)
}
put<OpinionArticlePaths.ArticleOpinion> {
call.receive<OpinionArticlePaths.ArticleOpinion.Body>().ids.map { id ->
OpinionForUpdate(
choice = OpinionChoiceRef(id),
target = it.article,
createdBy = citizen
)
}.let { opinions ->
voter.assert { canCreate(opinions, citizenOrNull) }
repo.updateOpinions(opinions)
}.let {
call.respond(HttpStatusCode.Created, it)
}
}
}

View File

@@ -1,38 +0,0 @@
package fr.dcproject.routes
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.entity.OpinionChoice
import fr.dcproject.security.voter.OpinionChoiceVoter
import fr.dcproject.voter.assert
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> = emptyList())
}
@KtorExperimentalLocationsAPI
fun Route.opinionChoice(repo: OpinionChoiceRepository, voter: OpinionChoiceVoter) {
get<OpinionChoicePaths.OpinionChoiceRequest> {
voter.assert { canView(it.opinionChoice, citizenOrNull) }
call.respond(it.opinionChoice)
}
get<OpinionChoicePaths.OpinionChoicesRequest> {
val opinionChoices = repo.findOpinionsChoices(it.targets)
voter.assert { canView(opinionChoices, citizenOrNull) }
call.respond(opinionChoices)
}
}

View File

@@ -3,16 +3,16 @@ package steps
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.article.ArticleRepository import fr.dcproject.component.article.ArticleRepository
import fr.dcproject.component.citizen.CitizenRepository import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.entity.OpinionChoice import fr.dcproject.component.opinion.entity.OpinionChoice
import fr.dcproject.entity.OpinionForUpdate import fr.dcproject.component.opinion.entity.OpinionForUpdate
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import io.cucumber.datatable.DataTable import io.cucumber.datatable.DataTable
import io.cucumber.java8.En import io.cucumber.java8.En
import org.koin.test.KoinTest import org.koin.test.KoinTest
import org.koin.test.get import org.koin.test.get
import java.util.UUID import java.util.UUID
import fr.dcproject.repository.OpinionArticle as OpinionRepository import fr.dcproject.repository.OpinionChoiceRepository as OpinionChoiceRepository
import fr.dcproject.repository.OpinionChoice as OpinionChoiceRepository import fr.dcproject.repository.OpinionRepositoryArticle as OpinionRepository
class OpinionSteps : En, KoinTest { class OpinionSteps : En, KoinTest {
init { init {

View File

@@ -6,8 +6,8 @@ import fr.dcproject.component.auth.UserI
import fr.dcproject.component.citizen.CitizenBasic import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenCart import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.OpinionChoice import fr.dcproject.component.opinion.OpinionChoiceVoter
import fr.dcproject.security.voter.OpinionChoiceVoter import fr.dcproject.component.opinion.entity.OpinionChoice
import fr.dcproject.voter.Vote.GRANTED import fr.dcproject.voter.Vote.GRANTED
import org.amshove.kluent.`should be` import org.amshove.kluent.`should be`
import org.joda.time.DateTime import org.joda.time.DateTime

View File

@@ -6,9 +6,9 @@ import fr.dcproject.component.auth.UserI
import fr.dcproject.component.citizen.CitizenBasic import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenCart import fr.dcproject.component.citizen.CitizenCart
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.Opinion import fr.dcproject.component.opinion.OpinionVoter
import fr.dcproject.entity.OpinionChoice import fr.dcproject.component.opinion.entity.Opinion
import fr.dcproject.security.voter.OpinionVoter import fr.dcproject.component.opinion.entity.OpinionChoice
import fr.dcproject.voter.Vote.DENIED import fr.dcproject.voter.Vote.DENIED
import fr.dcproject.voter.Vote.GRANTED import fr.dcproject.voter.Vote.GRANTED
import org.amshove.kluent.`should be` import org.amshove.kluent.`should be`