Move comments classes into comment component

This commit is contained in:
2021-01-14 22:51:33 +01:00
parent 64f74d0449
commit 91ab800272
22 changed files with 304 additions and 215 deletions

View File

@@ -16,7 +16,11 @@ import fr.dcproject.component.citizen.routes.changeMyPassword
import fr.dcproject.component.citizen.routes.findCitizen
import fr.dcproject.component.citizen.routes.getCurrentCitizen
import fr.dcproject.component.citizen.routes.getOneCitizen
import fr.dcproject.component.comment.routes.comment
import fr.dcproject.component.comment.generic.CommentVoter
import fr.dcproject.component.comment.generic.routes.createCommentChildren
import fr.dcproject.component.comment.generic.routes.editComment
import fr.dcproject.component.comment.generic.routes.getChildrenComments
import fr.dcproject.component.comment.generic.routes.getOneComment
import fr.dcproject.elasticsearch.configElasticIndexes
import fr.dcproject.entity.User
import fr.dcproject.event.EventNotification
@@ -165,12 +169,16 @@ fun Application.module(env: Env = PROD) {
getOneCitizen(get())
getCurrentCitizen(get())
changeMyPassword(get(), get())
/* Comment */
editComment(get())
getOneComment(get())
createCommentChildren(get())
getChildrenComments(get())
/* TODO */
auth(get(), get(), get())
constitution(get())
followArticle(get())
followConstitution(get())
comment(get())
commentArticle(get())
commentConstitution(get())
voteArticle(get(), get(), get())

View File

@@ -6,7 +6,7 @@ import fr.dcproject.component.article.ArticleRepository
import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.citizen.CitizenBasic
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.CommentRef
import fr.dcproject.component.comment.generic.CommentRef
import fr.dcproject.entity.Constitution
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.entity.WorkgroupRef

View File

@@ -13,10 +13,12 @@ import fr.dcproject.component.article.ArticleViewManager
import fr.dcproject.component.article.ArticleVoter
import fr.dcproject.component.citizen.CitizenRepository
import fr.dcproject.component.citizen.CitizenVoter
import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.event.publisher.Publisher
import fr.dcproject.messages.Mailer
import fr.dcproject.messages.NotificationEmailSender
import fr.dcproject.messages.SsoManager
import fr.dcproject.repository.CommentConstitutionRepository
import fr.postgresjson.connexion.Connection
import fr.postgresjson.connexion.Requester
import fr.postgresjson.migration.Migrations
@@ -29,9 +31,7 @@ import org.apache.http.HttpHost
import org.elasticsearch.client.RestClient
import org.koin.core.qualifier.named
import org.koin.dsl.module
import fr.dcproject.repository.CommentArticle as CommentArticleRepository
import fr.dcproject.repository.CommentConstitution as CommentConstitutionRepository
import fr.dcproject.repository.CommentGeneric as CommentGenericRepository
import fr.dcproject.component.comment.generic.CommentRepository as CommentGenericRepository
import fr.dcproject.repository.Constitution as ConstitutionRepository
import fr.dcproject.repository.FollowArticle as FollowArticleRepository
import fr.dcproject.repository.FollowConstitution as FollowConstitutionRepository

View File

@@ -0,0 +1,59 @@
package fr.dcproject.component.comment.article
import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.comment.generic.CommentForView
import fr.dcproject.component.comment.generic.CommentRepositoryAbs
import fr.dcproject.entity.TargetI
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.UuidEntityI
import java.util.*
class CommentArticleRepository(requester: Requester) : CommentRepositoryAbs<ArticleForView>(requester) {
override fun findById(id: UUID): CommentForView<ArticleForView, CitizenRef>? {
return requester
.getFunction("find_comment_by_id")
.selectOne(mapOf("id" to id))
}
override fun findByCitizen(
citizen: CitizenI,
page: Int,
limit: Int
): Paginated<CommentForView<ArticleForView, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_citizen")
.select(
page, limit,
"created_by_id" to citizen.id,
"reference" to TargetI.getReference(ArticleRef::class)
)
}
}
override fun findByTarget(
target: UuidEntityI,
page: Int,
limit: Int,
sort: Sort
): Paginated<CommentForView<ArticleForView, CitizenRef>> = requester
.getFunction("find_comments_by_target")
.select(
page, limit,
"target_id" to target.id,
"sort" to sort.sql
)
enum class Sort(val sql: String) {
CREATED_AT("created_at"), VOTES("votes");
companion object {
fun fromString(string: String): Sort? {
return values().firstOrNull { it.sql == string }
}
}
}
}

View File

@@ -1,6 +1,8 @@
package fr.dcproject.entity
package fr.dcproject.component.comment.generic
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.*
import fr.dcproject.entity.EntityI
import fr.postgresjson.entity.*
import org.joda.time.DateTime
import java.util.*

View File

@@ -1,17 +1,17 @@
package fr.dcproject.repository
package fr.dcproject.component.comment.generic
import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.entity.*
import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.entity.TargetI
import fr.dcproject.entity.TargetRef
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.UuidEntityI
import fr.postgresjson.repository.RepositoryI
import java.util.*
abstract class Comment<T : TargetI>(override var requester: Requester) : RepositoryI {
abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Requester) : RepositoryI {
abstract fun findById(id: UUID): CommentForView<T, CitizenRef>?
abstract fun findByCitizen(
@@ -46,7 +46,7 @@ abstract class Comment<T : TargetI>(override var requester: Requester) : Reposit
target: UuidEntityI,
page: Int = 1,
limit: Int = 50,
sort: CommentArticle.Sort = CommentArticle.Sort.CREATED_AT
sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.CREATED_AT
): Paginated<CommentForView<T, CitizenRef>> {
return findByTarget(target.id, page, limit, sort)
}
@@ -55,7 +55,7 @@ abstract class Comment<T : TargetI>(override var requester: Requester) : Reposit
targetId: UUID,
page: Int = 1,
limit: Int = 50,
sort: CommentArticle.Sort = CommentArticle.Sort.CREATED_AT
sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.CREATED_AT
): Paginated<CommentForView<T, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_target")
@@ -86,7 +86,7 @@ abstract class Comment<T : TargetI>(override var requester: Requester) : Reposit
}
}
class CommentGeneric(requester: Requester) : Comment<TargetRef>(requester) {
class CommentRepository(requester: Requester) : CommentRepositoryAbs<TargetRef>(requester) {
override fun findById(id: UUID): CommentForView<TargetRef, CitizenRef>? {
return requester
.getFunction("find_comment_by_id")
@@ -121,88 +121,3 @@ class CommentGeneric(requester: Requester) : Comment<TargetRef>(requester) {
}
}
}
class CommentArticle(requester: Requester) : Comment<ArticleForView>(requester) {
override fun findById(id: UUID): CommentForView<ArticleForView, CitizenRef>? {
return requester
.getFunction("find_comment_by_id")
.selectOne(mapOf("id" to id))
}
override fun findByCitizen(
citizen: CitizenI,
page: Int,
limit: Int
): Paginated<CommentForView<ArticleForView, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_citizen")
.select(
page, limit,
"created_by_id" to citizen.id,
"reference" to TargetI.getReference(ArticleRef::class)
)
}
}
override fun findByTarget(
target: UuidEntityI,
page: Int,
limit: Int,
sort: Sort
): Paginated<CommentForView<ArticleForView, CitizenRef>> = requester
.getFunction("find_comments_by_target")
.select(
page, limit,
"target_id" to target.id,
"sort" to sort.sql
)
enum class Sort(val sql: String) {
CREATED_AT("created_at"), VOTES("votes");
companion object {
fun fromString(string: String): Sort? {
return values().firstOrNull { it.sql == string }
}
}
}
}
class CommentConstitution(requester: Requester) : Comment<ConstitutionRef>(requester) {
override fun findById(id: UUID): CommentForView<ConstitutionRef, CitizenRef>? {
return requester
.getFunction("find_comment_by_id")
.selectOne(mapOf("id" to id))
}
override fun findByCitizen(
citizen: CitizenI,
page: Int,
limit: Int
): Paginated<CommentForView<ConstitutionRef, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_citizen")
.select(
page, limit,
"created_by_id" to citizen.id,
"reference" to TargetI.getReference(ConstitutionRef::class)
)
}
}
override fun findByTarget(
target: UuidEntityI,
page: Int,
limit: Int,
sort: CommentArticle.Sort
): Paginated<CommentForView<ConstitutionRef, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_target")
.select(
page, limit,
"target_id" to target.id,
"sort" to sort.sql
)
}
}
}

View File

@@ -1,9 +1,6 @@
package fr.dcproject.security.voter
package fr.dcproject.component.comment.generic
import fr.dcproject.citizenOrNull
import fr.dcproject.entity.CommentForUpdate
import fr.dcproject.entity.CommentForView
import fr.dcproject.entity.CommentI
import fr.dcproject.voter.NoRuleDefinedException
import fr.dcproject.voter.NoSubjectDefinedException
import fr.ktorVoter.*

View File

@@ -0,0 +1,40 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.citizen
import fr.dcproject.component.comment.generic.CommentForUpdate
import fr.dcproject.component.comment.generic.CommentRef
import fr.dcproject.component.comment.generic.CommentRepository
import fr.dcproject.component.comment.generic.CommentVoter
import fr.ktorVoter.assertCan
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.locations.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.util.*
@KtorExperimentalLocationsAPI
@Location("/comments/{comment}/children")
class CreateCommentChildrenRequest(val comment: CommentRef) {
class Input(val content: String)
}
@KtorExperimentalAPI
@KtorExperimentalLocationsAPI
fun Route.createCommentChildren(repo: CommentRepository) {
post<CreateCommentChildrenRequest> {
val parent = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
val newComment = CommentForUpdate(
content = call.receive<CreateCommentChildrenRequest.Input>().content,
createdBy = citizen,
parent = parent
)
assertCan(CommentVoter.Action.CREATE, newComment)
repo.comment(newComment)
call.respond(HttpStatusCode.Created, newComment)
}
}

View File

@@ -0,0 +1,31 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.component.comment.generic.CommentRef
import fr.dcproject.component.comment.generic.CommentRepository
import fr.dcproject.component.comment.generic.CommentVoter
import fr.ktorVoter.assertCan
import io.ktor.application.*
import io.ktor.http.*
import io.ktor.locations.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.util.*
@KtorExperimentalLocationsAPI
@Location("/comments/{comment}")
class EditCommentRequest(val comment: CommentRef)
@KtorExperimentalAPI
@KtorExperimentalLocationsAPI
fun Route.editComment(repo: CommentRepository) {
put<EditCommentRequest> {
val comment = repo.findById(it.comment.id)!!
assertCan(CommentVoter.Action.UPDATE, comment)
comment.content = call.receiveText()
repo.edit(comment)
call.respond(HttpStatusCode.OK, comment)
}
}

View File

@@ -0,0 +1,42 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.component.comment.generic.CommentRepository
import fr.dcproject.component.comment.generic.CommentVoter
import fr.ktorVoter.assertCanAll
import io.ktor.application.*
import io.ktor.http.*
import io.ktor.locations.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.util.*
import java.util.*
@KtorExperimentalLocationsAPI
@Location("/comments/{comment}/children")
class CommentChildrenRequest(
val comment: UUID,
page: Int = 1,
limit: Int = 50,
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
}
@KtorExperimentalAPI
@KtorExperimentalLocationsAPI
fun Route.getChildrenComments(repo: CommentRepository) {
get<CommentChildrenRequest> {
val comments =
repo.findByParent(
it.comment,
it.page,
it.limit
)
assertCanAll(CommentVoter.Action.VIEW, comments.result)
call.respond(HttpStatusCode.OK, comments)
}
}

View File

@@ -0,0 +1,29 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.component.comment.generic.CommentRef
import fr.dcproject.component.comment.generic.CommentRepository
import fr.dcproject.component.comment.generic.CommentVoter
import fr.ktorVoter.assertCan
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.locations.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.util.*
@KtorExperimentalLocationsAPI
@Location("/comments/{comment}")
class CommentRequest(val comment: CommentRef)
@KtorExperimentalAPI
@KtorExperimentalLocationsAPI
fun Route.getOneComment(repo: CommentRepository) {
get<CommentRequest> {
val comment = repo.findById(it.comment.id) ?: NotFoundException("Comment ${it.comment.id} not found")
assertCan(CommentVoter.Action.VIEW, comment)
call.respond(HttpStatusCode.OK, comment)
}
}

View File

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

View File

@@ -0,0 +1,52 @@
package fr.dcproject.repository
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.component.comment.generic.CommentForView
import fr.dcproject.component.comment.generic.CommentRepositoryAbs
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.entity.TargetI
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.UuidEntityI
import java.util.*
class CommentConstitutionRepository(requester: Requester) : CommentRepositoryAbs<ConstitutionRef>(requester) {
override fun findById(id: UUID): CommentForView<ConstitutionRef, CitizenRef>? {
return requester
.getFunction("find_comment_by_id")
.selectOne(mapOf("id" to id))
}
override fun findByCitizen(
citizen: CitizenI,
page: Int,
limit: Int
): Paginated<CommentForView<ConstitutionRef, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_citizen")
.select(
page, limit,
"created_by_id" to citizen.id,
"reference" to TargetI.getReference(ConstitutionRef::class)
)
}
}
override fun findByTarget(
target: UuidEntityI,
page: Int,
limit: Int,
sort: CommentArticleRepository.Sort
): Paginated<CommentForView<ConstitutionRef, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_target")
.select(
page, limit,
"target_id" to target.id,
"sort" to sort.sql
)
}
}
}

View File

@@ -3,6 +3,7 @@ package fr.dcproject.repository
import com.fasterxml.jackson.core.type.TypeReference
import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.comment.generic.CommentForView
import fr.dcproject.entity.*
import fr.dcproject.entity.Constitution
import fr.postgresjson.connexion.Paginated

View File

@@ -1,90 +0,0 @@
package fr.dcproject.routes
import fr.dcproject.citizen
import fr.dcproject.entity.CommentForUpdate
import fr.dcproject.entity.CommentRef
import fr.dcproject.routes.CommentPaths.CreateCommentRequest.Content
import fr.dcproject.security.voter.CommentVoter.Action.*
import fr.ktorVoter.assertCan
import fr.ktorVoter.assertCanAll
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.locations.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.util.*
import java.util.*
import fr.dcproject.repository.CommentGeneric as CommentRepository
@KtorExperimentalLocationsAPI
object CommentPaths {
@Location("/comments/{comment}")
class CommentRequest(val comment: CommentRef)
@Location("/comments/{comment}/children")
class CommentChildrenRequest(
val comment: UUID,
page: Int = 1,
limit: Int = 50,
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("/comments/{comment}/children")
class CreateCommentRequest(val comment: CommentRef) {
class Content(val content: String)
}
}
@KtorExperimentalAPI
@KtorExperimentalLocationsAPI
fun Route.comment(repo: CommentRepository) {
get<CommentPaths.CommentRequest> {
val comment = repo.findById(it.comment.id)!!
assertCan(VIEW, comment)
call.respond(HttpStatusCode.OK, comment)
}
get<CommentPaths.CommentChildrenRequest> {
val comments =
repo.findByParent(
it.comment,
it.page,
it.limit
)
assertCanAll(VIEW, comments.result)
call.respond(HttpStatusCode.OK, comments)
}
post<CommentPaths.CreateCommentRequest> {
val parent = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
val newComment = CommentForUpdate(
content = call.receive<Content>().content,
createdBy = citizen,
parent = parent
)
assertCan(CREATE, newComment)
repo.comment(newComment)
call.respond(HttpStatusCode.Created, newComment)
}
put<CommentPaths.CommentRequest> {
val comment = repo.findById(it.comment.id)!!
assertCan(UPDATE, comment)
comment.content = call.receiveText()
repo.edit(comment)
call.respond(HttpStatusCode.OK, comment)
}
}

View File

@@ -4,10 +4,11 @@ import fr.dcproject.citizen
import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.citizen.Citizen
import fr.dcproject.entity.CommentForUpdate
import fr.dcproject.repository.CommentArticle.Sort
import fr.dcproject.security.voter.CommentVoter.Action.CREATE
import fr.dcproject.security.voter.CommentVoter.Action.VIEW
import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.component.comment.article.CommentArticleRepository.Sort
import fr.dcproject.component.comment.generic.CommentForUpdate
import fr.dcproject.component.comment.generic.CommentVoter.Action.CREATE
import fr.dcproject.component.comment.generic.CommentVoter.Action.VIEW
import fr.ktorVoter.assertCan
import fr.ktorVoter.assertCanAll
import io.ktor.application.*
@@ -16,7 +17,6 @@ import io.ktor.locations.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import fr.dcproject.repository.CommentArticle as CommentArticleRepository
@KtorExperimentalLocationsAPI
object CommentArticlePaths {

View File

@@ -2,10 +2,11 @@ package fr.dcproject.routes
import fr.dcproject.citizen
import fr.dcproject.component.citizen.Citizen
import fr.dcproject.entity.CommentForUpdate
import fr.dcproject.component.comment.generic.CommentForUpdate
import fr.dcproject.component.comment.generic.CommentVoter.Action.CREATE
import fr.dcproject.component.comment.generic.CommentVoter.Action.VIEW
import fr.dcproject.entity.ConstitutionRef
import fr.dcproject.security.voter.CommentVoter.Action.CREATE
import fr.dcproject.security.voter.CommentVoter.Action.VIEW
import fr.dcproject.repository.CommentConstitutionRepository
import fr.ktorVoter.assertCan
import fr.ktorVoter.assertCanAll
import io.ktor.application.*
@@ -14,7 +15,6 @@ import io.ktor.locations.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import fr.dcproject.repository.CommentConstitution as CommentConstitutionRepository
@KtorExperimentalLocationsAPI
object CommentConstitutionPaths {

View File

@@ -3,8 +3,8 @@ package fr.dcproject.routes
import fr.dcproject.citizen
import fr.dcproject.component.article.ArticleForView
import fr.dcproject.component.citizen.Citizen
import fr.dcproject.component.comment.generic.CommentRepository
import fr.dcproject.entity.VoteForUpdate
import fr.dcproject.repository.CommentGeneric
import fr.dcproject.repository.VoteComment
import fr.dcproject.routes.VoteArticlePaths.ArticleVoteRequest
import fr.dcproject.routes.VoteArticlePaths.CommentVoteRequest
@@ -49,7 +49,7 @@ object VoteArticlePaths {
}
@KtorExperimentalLocationsAPI
fun Route.voteArticle(repo: VoteArticleRepository, voteCommentRepo: VoteComment, commentRepo: CommentGeneric) {
fun Route.voteArticle(repo: VoteArticleRepository, voteCommentRepo: VoteComment, commentRepo: CommentRepository) {
put<ArticleVoteRequest> {
val content = call.receive<ArticleVoteRequest.Content>()
val vote = VoteForUpdate(

View File

@@ -1,6 +1,7 @@
package fr.dcproject.security.voter
import fr.dcproject.entity.CommentForView
import fr.dcproject.component.comment.generic.CommentForView
import fr.dcproject.component.comment.generic.CommentVoter
import fr.dcproject.entity.ConstitutionSimple
import fr.dcproject.entity.UserI
import fr.dcproject.user