Move constitution to component

This commit is contained in:
2021-01-23 21:18:42 +01:00
parent f34407962b
commit 1e5598cb91
28 changed files with 207 additions and 152 deletions

View File

@@ -16,6 +16,7 @@ import fr.dcproject.component.auth.user
import fr.dcproject.component.citizen.routes.installCitizenRoutes import fr.dcproject.component.citizen.routes.installCitizenRoutes
import fr.dcproject.component.comment.article.routes.installCommentArticleRoutes 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.constitution.routes.installConstitutionRoutes
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.opinion.routes.installOpinionRoutes
@@ -25,7 +26,6 @@ import fr.dcproject.component.workgroup.routes.installWorkgroupRoutes
import fr.dcproject.event.EventNotification import fr.dcproject.event.EventNotification
import fr.dcproject.event.EventSubscriber import fr.dcproject.event.EventSubscriber
import fr.dcproject.routes.commentConstitution import fr.dcproject.routes.commentConstitution
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.security.AccessDeniedException import fr.dcproject.security.AccessDeniedException
@@ -140,10 +140,10 @@ fun Application.module(env: Env = PROD) {
installWorkgroupRoutes() installWorkgroupRoutes()
installOpinionRoutes() installOpinionRoutes()
installVoteRoutes() installVoteRoutes()
installConstitutionRoutes()
authenticate(optional = true) { authenticate(optional = true) {
/* TODO */ /* TODO */
constitution(get(), get())
commentConstitution(get(), get()) commentConstitution(get(), get())
definition() definition()
} }

View File

@@ -8,13 +8,14 @@ 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.constitution.Constitution
import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.component.constitution.ConstitutionRepository
import fr.dcproject.component.opinion.OpinionChoiceRepository import fr.dcproject.component.opinion.OpinionChoiceRepository
import fr.dcproject.component.opinion.entity.OpinionChoice 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.ConstitutionRef
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
@@ -80,7 +81,7 @@ val converters: ConverterDeclaration = {
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<fr.dcproject.repository.Constitution>().findById(id) ?: throw NotFoundException("Constitution $values not found") get<ConstitutionRepository>().findById(id) ?: throw NotFoundException("Constitution $values not found")
} }
} }

View File

@@ -18,6 +18,8 @@ import fr.dcproject.component.citizen.CitizenAccessControl
import fr.dcproject.component.citizen.CitizenRepository import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.component.comment.article.CommentArticleRepository import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.component.comment.generic.CommentAccessControl import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.constitution.ConstitutionAccessControl
import fr.dcproject.component.constitution.ConstitutionRepository
import fr.dcproject.component.follow.FollowAccessControl import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.opinion.OpinionAccessControl import fr.dcproject.component.opinion.OpinionAccessControl
import fr.dcproject.component.opinion.OpinionChoiceAccessControl import fr.dcproject.component.opinion.OpinionChoiceAccessControl
@@ -33,7 +35,6 @@ import fr.dcproject.event.publisher.Publisher
import fr.dcproject.messages.Mailer 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.ConstitutionAccessControl
import fr.postgresjson.connexion.Connection import fr.postgresjson.connexion.Connection
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.migration.Migrations import fr.postgresjson.migration.Migrations
@@ -50,7 +51,6 @@ 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.component.opinion.OpinionRepositoryArticle as OpinionArticleRepository import fr.dcproject.component.opinion.OpinionRepositoryArticle as OpinionArticleRepository
import fr.dcproject.repository.Constitution as ConstitutionRepository
@KtorExperimentalAPI @KtorExperimentalAPI
val KoinModule = module { val KoinModule = module {

View File

@@ -2,8 +2,8 @@ package fr.dcproject.component.article
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.views.ViewManager import fr.dcproject.component.views.ViewManager
import fr.dcproject.component.views.entity.ViewAggregation
import fr.dcproject.entity.VersionableRef import fr.dcproject.entity.VersionableRef
import fr.dcproject.entity.ViewAggregation
import fr.dcproject.utils.contentToString import fr.dcproject.utils.contentToString
import fr.dcproject.utils.getJsonField import fr.dcproject.utils.getJsonField
import fr.dcproject.utils.toIso import fr.dcproject.utils.toIso

View File

@@ -7,10 +7,11 @@ import fr.dcproject.component.article.ArticleViewManager
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.component.opinion.dto.Opinionable
import fr.dcproject.component.views.dto.Viewable
import fr.dcproject.component.views.entity.ViewAggregation
import fr.dcproject.component.vote.dto.Votable import fr.dcproject.component.vote.dto.Votable
import fr.dcproject.dto.CreatedAt import fr.dcproject.dto.CreatedAt
import fr.dcproject.dto.Versionable import fr.dcproject.dto.Versionable
import fr.dcproject.dto.Viewable
import fr.dcproject.security.assert import fr.dcproject.security.assert
import io.ktor.application.call import io.ktor.application.call
import io.ktor.features.NotFoundException import io.ktor.features.NotFoundException
@@ -34,7 +35,7 @@ object GetOneArticle {
class Output( class Output(
article: ArticleForView, article: ArticleForView,
views: fr.dcproject.entity.ViewAggregation = fr.dcproject.entity.ViewAggregation() views: ViewAggregation = ViewAggregation()
) : CreatedAt by CreatedAt.Imp(article), ) : CreatedAt by CreatedAt.Imp(article),
Opinionable by Opinionable.Imp(article), Opinionable by Opinionable.Imp(article),
Votable by Votable.Imp(article), Votable by Votable.Imp(article),

View File

@@ -1,9 +1,12 @@
package fr.dcproject.entity package fr.dcproject.component.constitution
import fr.dcproject.component.article.ArticleForListing import fr.dcproject.component.article.ArticleForListing
import fr.dcproject.component.article.ArticleI import fr.dcproject.component.article.ArticleI
import fr.dcproject.component.citizen.CitizenSimple import fr.dcproject.component.citizen.CitizenSimple
import fr.dcproject.component.citizen.CitizenWithUserI import fr.dcproject.component.citizen.CitizenWithUserI
import fr.dcproject.component.constitution.ConstitutionSimple.TitleSimple
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
@@ -23,7 +26,7 @@ class Constitution(
draft: Boolean = false, draft: Boolean = false,
lastVersion: Boolean = false, lastVersion: Boolean = false,
override val createdBy: CitizenSimple override val createdBy: CitizenSimple
) : ConstitutionSimple<CitizenSimple, ConstitutionSimple.TitleSimple<ArticleForListing>>( ) : ConstitutionSimple<CitizenSimple, TitleSimple<ArticleForListing>>(
id, id,
title = title, title = title,
anonymous = anonymous, anonymous = anonymous,
@@ -38,10 +41,10 @@ class Constitution(
name: String, name: String,
rank: Int? = null, rank: Int? = null,
override val articles: MutableList<ArticleForListing> = mutableListOf() override val articles: MutableList<ArticleForListing> = mutableListOf()
) : ConstitutionSimple.TitleSimple<ArticleForListing>(id, name, rank) ) : TitleSimple<ArticleForListing>(id, name, rank)
} }
open class ConstitutionSimple<Cr : CitizenWithUserI, T : ConstitutionSimple.TitleSimple<*>>( open class ConstitutionSimple<Cr : CitizenWithUserI, T : TitleSimple<*>>(
id: UUID = UUID.randomUUID(), id: UUID = UUID.randomUUID(),
val title: String, val title: String,
val anonymous: Boolean = true, val anonymous: Boolean = true,

View File

@@ -1,8 +1,6 @@
package fr.dcproject.security.voter package fr.dcproject.component.constitution
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.ConstitutionS
import fr.dcproject.entity.ConstitutionSimple
import fr.dcproject.security.AccessControl import fr.dcproject.security.AccessControl
import fr.dcproject.security.AccessResponse import fr.dcproject.security.AccessResponse

View File

@@ -1,17 +1,17 @@
package fr.dcproject.repository package fr.dcproject.component.constitution
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenWithUserI import fr.dcproject.component.citizen.CitizenWithUserI
import fr.dcproject.entity.ConstitutionSimple import fr.dcproject.component.constitution.ConstitutionSimple.TitleSimple
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester
import fr.postgresjson.repository.RepositoryI import fr.postgresjson.repository.RepositoryI
import fr.postgresjson.repository.RepositoryI.Direction import fr.postgresjson.repository.RepositoryI.Direction
import net.pearx.kasechange.toSnakeCase import net.pearx.kasechange.toSnakeCase
import java.util.UUID import java.util.UUID
import fr.dcproject.entity.Constitution as ConstitutionEntity import fr.dcproject.component.constitution.Constitution as ConstitutionEntity
class Constitution(override var requester: Requester) : RepositoryI { class ConstitutionRepository(override var requester: Requester) : RepositoryI {
fun findById(id: UUID): ConstitutionEntity? { fun findById(id: UUID): ConstitutionEntity? {
val function = requester.getFunction("find_constitution_by_id") val function = requester.getFunction("find_constitution_by_id")
return function.selectOne("id" to id) return function.selectOne("id" to id)
@@ -35,7 +35,7 @@ class Constitution(override var requester: Requester) : RepositoryI {
) )
} }
fun upsert(constitution: ConstitutionSimple<CitizenWithUserI, ConstitutionSimple.TitleSimple<ArticleRef>>): ConstitutionEntity? { fun upsert(constitution: ConstitutionSimple<CitizenWithUserI, TitleSimple<ArticleRef>>): ConstitutionEntity? {
return requester return requester
.getFunction("upsert_constitution") .getFunction("upsert_constitution")
.selectOne("resource" to constitution) .selectOne("resource" to constitution)

View File

@@ -0,0 +1,82 @@
package fr.dcproject.component.constitution.routes
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenWithUserI
import fr.dcproject.component.constitution.ConstitutionAccessControl
import fr.dcproject.component.constitution.ConstitutionRepository
import fr.dcproject.component.constitution.ConstitutionSimple
import fr.dcproject.component.constitution.ConstitutionSimple.TitleSimple
import fr.dcproject.component.constitution.routes.CreateConstitution.PostConstitutionRequest.Input
import fr.dcproject.component.constitution.routes.CreateConstitution.PostConstitutionRequest.Input.Title
import fr.dcproject.security.assert
import fr.postgresjson.entity.UuidEntity
import io.ktor.application.call
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.post
import io.ktor.request.receive
import io.ktor.response.respond
import io.ktor.routing.Route
import java.util.UUID
@KtorExperimentalLocationsAPI
object CreateConstitution {
@Location("/constitutions")
class PostConstitutionRequest {
class Input(
var title: String,
var anonymous: Boolean = true,
var titles: MutableList<Title> = mutableListOf(),
var draft: Boolean = false,
var lastVersion: Boolean = false,
var versionId: UUID = UUID.randomUUID()
) {
init {
titles.forEachIndexed { index, title ->
title.rank = index
}
}
class Title(
id: UUID = UUID.randomUUID(),
var name: String,
var rank: Int? = null,
var articles: MutableList<ArticleRef> = mutableListOf()
) : UuidEntity(id)
}
}
private fun getNewConstitution(input: Input, citizen: Citizen) = input.run {
ConstitutionSimple<CitizenWithUserI, TitleSimple<ArticleRef>>(
id = UUID.randomUUID(),
title = title,
titles = titles.create(),
createdBy = citizen,
versionId = versionId
)
}
private fun List<Title>.create(): MutableList<TitleSimple<ArticleRef>> =
map { it.create() }.toMutableList()
private fun Title.create(): TitleSimple<ArticleRef> =
TitleSimple(
id,
name,
rank,
articles
)
fun Route.createConstitution(repo: ConstitutionRepository, ac: ConstitutionAccessControl) {
post<PostConstitutionRequest> {
getNewConstitution(call.receive(), citizen).let {
ac.assert { canCreate(it, citizenOrNull) }
repo.upsert(it)
call.respond(it)
}
}
}
}

View File

@@ -0,0 +1,35 @@
package fr.dcproject.component.constitution.routes
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.constitution.ConstitutionAccessControl
import fr.dcproject.component.constitution.ConstitutionRepository
import fr.dcproject.routes.PaginatedRequest
import fr.dcproject.routes.PaginatedRequestI
import fr.dcproject.security.assert
import fr.postgresjson.repository.RepositoryI
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 FindConstitutions {
@Location("/constitutions")
class FindConstitutionsRequest(
page: Int = 1,
limit: Int = 50,
val sort: String? = null,
val direction: RepositoryI.Direction? = null,
val search: String? = null
) : PaginatedRequestI by PaginatedRequest(page, limit)
fun Route.findConstitutions(repo: ConstitutionRepository, ac: ConstitutionAccessControl) {
get<FindConstitutionsRequest> {
val constitutions = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
ac.assert { canView(constitutions.result, citizenOrNull) }
call.respond(constitutions)
}
}
}

View File

@@ -0,0 +1,25 @@
package fr.dcproject.component.constitution.routes
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.constitution.ConstitutionAccessControl
import fr.dcproject.security.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.component.constitution.Constitution as ConstitutionEntity
@KtorExperimentalLocationsAPI
object GetConstitution {
@Location("/constitutions/{constitution}")
class GetConstitutionRequest(val constitution: ConstitutionEntity)
fun Route.getConstitution(ac: ConstitutionAccessControl) {
get<GetConstitutionRequest> {
ac.assert { canView(it.constitution, citizenOrNull) }
call.respond(it.constitution)
}
}
}

View File

@@ -0,0 +1,18 @@
package fr.dcproject.component.constitution.routes
import fr.dcproject.component.constitution.routes.CreateConstitution.createConstitution
import fr.dcproject.component.constitution.routes.FindConstitutions.findConstitutions
import fr.dcproject.component.constitution.routes.GetConstitution.getConstitution
import io.ktor.auth.authenticate
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.routing.Routing
import org.koin.ktor.ext.get
@KtorExperimentalLocationsAPI
fun Routing.installConstitutionRoutes() {
authenticate(optional = true) {
getConstitution(get())
findConstitutions(get(), get())
createConstitution(get(), get())
}
}

View File

@@ -4,7 +4,7 @@ import fr.dcproject.component.article.ArticleForView
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.citizen.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.ConstitutionRef import fr.dcproject.component.constitution.ConstitutionRef
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
@@ -13,8 +13,8 @@ import fr.postgresjson.repository.RepositoryI
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import java.util.UUID import java.util.UUID
import fr.dcproject.component.constitution.Constitution as ConstitutionEntity
import fr.dcproject.component.follow.Follow as FollowEntity import fr.dcproject.component.follow.Follow as FollowEntity
import fr.dcproject.entity.Constitution as ConstitutionEntity
sealed class FollowRepository<IN : TargetRef, OUT : TargetRef>(override var requester: Requester) : RepositoryI { sealed class FollowRepository<IN : TargetRef, OUT : TargetRef>(override var requester: Requester) : RepositoryI {
open fun findByCitizen( open fun findByCitizen(

View File

@@ -2,10 +2,10 @@ package fr.dcproject.component.follow.routes.constitution
import fr.dcproject.component.auth.citizen import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.component.follow.FollowAccessControl import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowConstitutionRepository import fr.dcproject.component.follow.FollowConstitutionRepository
import fr.dcproject.component.follow.FollowForUpdate import fr.dcproject.component.follow.FollowForUpdate
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.security.assert import fr.dcproject.security.assert
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode

View File

@@ -2,9 +2,9 @@ package fr.dcproject.component.follow.routes.constitution
import fr.dcproject.component.auth.citizen import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.component.follow.FollowAccessControl import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowConstitutionRepository import fr.dcproject.component.follow.FollowConstitutionRepository
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.security.assert import fr.dcproject.security.assert
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode

View File

@@ -2,10 +2,10 @@ package fr.dcproject.component.follow.routes.constitution
import fr.dcproject.component.auth.citizen import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.component.follow.FollowAccessControl import fr.dcproject.component.follow.FollowAccessControl
import fr.dcproject.component.follow.FollowConstitutionRepository import fr.dcproject.component.follow.FollowConstitutionRepository
import fr.dcproject.component.follow.FollowForUpdate import fr.dcproject.component.follow.FollowForUpdate
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.security.assert import fr.dcproject.security.assert
import io.ktor.application.call import io.ktor.application.call
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode

View File

@@ -1,7 +1,7 @@
package fr.dcproject.component.views package fr.dcproject.component.views
import fr.dcproject.component.citizen.CitizenI import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.entity.ViewAggregation import fr.dcproject.component.views.entity.ViewAggregation
import org.elasticsearch.client.Response import org.elasticsearch.client.Response
import org.joda.time.DateTime import org.joda.time.DateTime

View File

@@ -1,6 +1,6 @@
package fr.dcproject.dto package fr.dcproject.component.views.dto
import fr.dcproject.entity.ViewAggregation import fr.dcproject.component.views.entity.ViewAggregation
class ViewAggregation( class ViewAggregation(
val total: Int, val total: Int,

View File

@@ -1,9 +1,9 @@
package fr.dcproject.dto package fr.dcproject.component.views.dto
interface Viewable { interface Viewable {
var views: ViewAggregation var views: ViewAggregation
class Imp(views: fr.dcproject.entity.ViewAggregation) : Viewable { class Imp(views: fr.dcproject.component.views.entity.ViewAggregation) : Viewable {
override var views: ViewAggregation = ViewAggregation(views.total, views.unique) override var views: ViewAggregation = ViewAggregation(views.total, views.unique)
} }
} }

View File

@@ -1,4 +1,4 @@
package fr.dcproject.entity package fr.dcproject.component.views.entity
import fr.postgresjson.entity.EntityI import fr.postgresjson.entity.EntityI
import fr.postgresjson.entity.EntityUpdatedAt import fr.postgresjson.entity.EntityUpdatedAt

View File

@@ -4,9 +4,9 @@ import com.fasterxml.jackson.core.type.TypeReference
import fr.dcproject.component.article.ArticleForView import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.citizen.CitizenRef import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.comment.generic.CommentForView import fr.dcproject.component.comment.generic.CommentForView
import fr.dcproject.component.constitution.Constitution
import fr.dcproject.component.vote.entity.VoteAggregation import fr.dcproject.component.vote.entity.VoteAggregation
import fr.dcproject.component.vote.entity.VoteForUpdateI import fr.dcproject.component.vote.entity.VoteForUpdateI
import fr.dcproject.entity.Constitution
import fr.dcproject.entity.TargetI import fr.dcproject.entity.TargetI
import fr.dcproject.entity.TargetRef import fr.dcproject.entity.TargetRef
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated

View File

@@ -15,7 +15,7 @@ import io.ktor.locations.put
import io.ktor.request.receive import io.ktor.request.receive
import io.ktor.response.respond import io.ktor.response.respond
import io.ktor.routing.Route import io.ktor.routing.Route
import fr.dcproject.entity.Constitution as ConstitutionEntity import fr.dcproject.component.constitution.Constitution as ConstitutionEntity
@KtorExperimentalLocationsAPI @KtorExperimentalLocationsAPI
object VoteConstitution { object VoteConstitution {

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.constitution.ConstitutionRef
import fr.dcproject.component.opinion.entity.OpinionRef 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

View File

@@ -5,7 +5,7 @@ import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.comment.article.CommentArticleRepository import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.component.comment.generic.CommentForView import fr.dcproject.component.comment.generic.CommentForView
import fr.dcproject.component.comment.generic.CommentRepositoryAbs import fr.dcproject.component.comment.generic.CommentRepositoryAbs
import fr.dcproject.entity.ConstitutionRef import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.entity.TargetI import fr.dcproject.entity.TargetI
import fr.postgresjson.connexion.Paginated import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester import fr.postgresjson.connexion.Requester

View File

@@ -5,7 +5,7 @@ import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.citizen.Citizen import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.comment.generic.CommentAccessControl import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.CommentForUpdate import fr.dcproject.component.comment.generic.CommentForUpdate
import fr.dcproject.entity.ConstitutionRef import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.repository.CommentConstitutionRepository import fr.dcproject.repository.CommentConstitutionRepository
import fr.dcproject.security.assert import fr.dcproject.security.assert
import io.ktor.application.call import io.ktor.application.call

View File

@@ -1,110 +0,0 @@
package fr.dcproject.routes
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.citizen.CitizenWithUserI
import fr.dcproject.entity.ConstitutionSimple
import fr.dcproject.entity.ConstitutionSimple.TitleSimple
import fr.dcproject.security.assert
import fr.dcproject.security.voter.ConstitutionAccessControl
import fr.postgresjson.entity.UuidEntity
import fr.postgresjson.repository.RepositoryI
import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.get
import io.ktor.locations.post
import io.ktor.request.receive
import io.ktor.response.respond
import io.ktor.routing.Route
import java.util.UUID
import fr.dcproject.entity.Constitution as ConstitutionEntity
import fr.dcproject.repository.Constitution as ConstitutionRepository
@KtorExperimentalLocationsAPI
object ConstitutionPaths {
@Location("/constitutions")
class ConstitutionsRequest(
page: Int = 1,
limit: Int = 50,
val sort: String? = null,
val direction: RepositoryI.Direction? = null,
val search: String? = null
) {
val page: Int = if (page < 1) 1 else page
val limit: Int = if (limit > 50) 50 else if (limit < 1) 1 else limit
}
@Location("/constitutions/{constitution}")
class ConstitutionRequest(val constitution: ConstitutionEntity)
@Location("/constitutions")
class PostConstitutionRequest {
class Constitution(
var title: String,
var anonymous: Boolean = true,
var titles: MutableList<Title> = mutableListOf(),
var draft: Boolean = false,
var lastVersion: Boolean = false,
var versionId: UUID = UUID.randomUUID()
) {
init {
titles.forEachIndexed { index, title ->
title.rank = index
}
}
class Title(
id: UUID = UUID.randomUUID(),
var name: String,
var rank: Int? = null,
var articles: MutableList<ArticleRef> = mutableListOf()
) : UuidEntity(id) {
fun create(): TitleSimple<ArticleRef> =
TitleSimple(
id,
name,
rank,
articles
)
}
fun List<Title>.create(): MutableList<TitleSimple<ArticleRef>> =
map { it.create() }.toMutableList()
}
suspend fun getNewConstitution(call: ApplicationCall): ConstitutionSimple<CitizenWithUserI, TitleSimple<ArticleRef>> = call.receive<Constitution>().run {
ConstitutionSimple<CitizenWithUserI, TitleSimple<ArticleRef>>(
id = UUID.randomUUID(),
title = title,
titles = titles.create(),
createdBy = call.citizen,
versionId = versionId
)
}
}
}
@KtorExperimentalLocationsAPI
fun Route.constitution(repo: ConstitutionRepository, ac: ConstitutionAccessControl) {
get<ConstitutionPaths.ConstitutionsRequest> {
val constitutions = repo.find(it.page, it.limit, it.sort, it.direction, it.search)
ac.assert { canView(constitutions.result, citizenOrNull) }
call.respond(constitutions)
}
get<ConstitutionPaths.ConstitutionRequest> {
ac.assert { canView(it.constitution, citizenOrNull) }
call.respond(it.constitution)
}
post<ConstitutionPaths.PostConstitutionRequest> {
it.getNewConstitution(call).let { constitution ->
ac.assert { canCreate(constitution, citizenOrNull) }
repo.upsert(constitution)
call.respond(constitution)
}
}
}

View File

@@ -6,8 +6,10 @@ import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRepository import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.component.citizen.CitizenWithUserI import fr.dcproject.component.citizen.CitizenWithUserI
import fr.dcproject.component.comment.generic.CommentForUpdate import fr.dcproject.component.comment.generic.CommentForUpdate
import fr.dcproject.entity.ConstitutionRef import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.entity.ConstitutionSimple import fr.dcproject.component.constitution.ConstitutionRepository
import fr.dcproject.component.constitution.ConstitutionSimple
import fr.dcproject.component.constitution.ConstitutionSimple.TitleSimple
import fr.dcproject.repository.CommentConstitutionRepository import fr.dcproject.repository.CommentConstitutionRepository
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import io.cucumber.datatable.DataTable import io.cucumber.datatable.DataTable
@@ -17,7 +19,6 @@ 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.component.auth.User as UserEntity import fr.dcproject.component.auth.User as UserEntity
import fr.dcproject.repository.Constitution as ConstitutionRepository
class ConstitutionSteps : En, KoinTest { class ConstitutionSteps : En, KoinTest {
init { init {
@@ -71,7 +72,7 @@ class ConstitutionSteps : En, KoinTest {
name = "My Title" name = "My Title"
) )
val constitution = ConstitutionSimple<CitizenWithUserI, ConstitutionSimple.TitleSimple<ArticleRef>>( val constitution = ConstitutionSimple<CitizenWithUserI, TitleSimple<ArticleRef>>(
id = id ?: params?.get("id")?.toUUID() ?: UUID.randomUUID(), id = id ?: params?.get("id")?.toUUID() ?: UUID.randomUUID(),
title = "hello", title = "hello",
titles = mutableListOf(title1), titles = mutableListOf(title1),

View File

@@ -2,8 +2,8 @@ package steps
import fr.dcproject.component.article.ArticleRef import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenRepository import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.component.constitution.ConstitutionRef
import fr.dcproject.component.follow.FollowForUpdate import fr.dcproject.component.follow.FollowForUpdate
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.utils.toUUID import fr.dcproject.utils.toUUID
import io.cucumber.java8.En import io.cucumber.java8.En
import org.koin.test.KoinTest import org.koin.test.KoinTest