Move all file in fr.dcproject.

This commit is contained in:
2021-02-11 01:37:29 +01:00
parent c85401aa86
commit 066b01e86f
148 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
package fr.dcproject.component.comment
import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.component.comment.constitution.CommentConstitutionRepository
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.CommentRepository
import org.koin.dsl.module
val commentKoinModule = module {
single { CommentRepository(get()) }
single { CommentArticleRepository(get()) }
single { CommentConstitutionRepository(get()) }
single { CommentAccessControl() }
}

View File

@@ -0,0 +1,62 @@
package fr.dcproject.component.comment.article
import fr.dcproject.common.entity.TargetI
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.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.UuidEntityI
import java.util.UUID
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

@@ -0,0 +1,47 @@
package fr.dcproject.component.comment.article.routes
import fr.dcproject.common.security.assert
import fr.dcproject.common.utils.receiveOrBadRequest
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.component.comment.article.routes.CreateCommentArticle.PostArticleCommentRequest.Input
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.CommentForUpdate
import io.ktor.application.ApplicationCall
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.post
import io.ktor.response.respond
import io.ktor.routing.Route
import java.util.UUID
@KtorExperimentalLocationsAPI
object CreateCommentArticle {
@Location("/articles/{article}/comments")
class PostArticleCommentRequest(article: UUID) {
val article = ArticleRef(article)
class Input(val content: String)
}
suspend fun PostArticleCommentRequest.getComment(call: ApplicationCall) = call.receiveOrBadRequest<Input>().run {
CommentForUpdate(
target = article,
createdBy = call.citizen,
content = content
)
}
fun Route.createCommentArticle(repo: CommentArticleRepository, ac: CommentAccessControl) {
post<PostArticleCommentRequest> {
it.getComment(call).let { comment ->
ac.assert { canCreate(comment, citizenOrNull) }
repo.comment(comment)
call.respond(HttpStatusCode.Created, comment)
}
}
}
}

View File

@@ -0,0 +1,42 @@
package fr.dcproject.component.comment.article.routes
import fr.dcproject.common.security.assert
import fr.dcproject.component.article.ArticleRef
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.routes.PaginatedRequest
import fr.dcproject.routes.PaginatedRequestI
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.response.respond
import io.ktor.routing.Route
import java.util.UUID
@KtorExperimentalLocationsAPI
object GetArticleComments {
@Location("/articles/{article}/comments")
class ArticleCommentsRequest(
article: UUID,
page: Int = 1,
limit: Int = 50,
val search: String? = null,
sort: String = CommentArticleRepository.Sort.CREATED_AT.sql
) : PaginatedRequestI by PaginatedRequest(page, limit) {
val article = ArticleRef(article)
val sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.fromString(sort) ?: CommentArticleRepository.Sort.CREATED_AT
}
fun Route.getArticleComments(repo: CommentArticleRepository, ac: CommentAccessControl) {
get<ArticleCommentsRequest> {
val comment = repo.findByTarget(it.article, it.page, it.limit, it.sort)
if (comment.result.isNotEmpty()) {
ac.assert { canView(comment.result, citizenOrNull) }
}
call.respond(HttpStatusCode.OK, comment)
}
}
}

View File

@@ -0,0 +1,31 @@
package fr.dcproject.component.comment.article.routes
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.dcproject.component.comment.generic.CommentAccessControl
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 java.util.UUID
@KtorExperimentalLocationsAPI
object GetCitizenArticleComments {
@Location("/citizens/{citizen}/comments/articles")
class CitizenCommentArticleRequest(citizen: UUID) {
val citizen = CitizenRef(citizen)
}
fun Route.getCitizenArticleComments(repo: CommentArticleRepository, ac: CommentAccessControl) {
get<CitizenCommentArticleRequest> {
repo.findByCitizen(it.citizen).let { comments ->
ac.assert { canView(comments.result, citizenOrNull) }
call.respond(comments)
}
}
}
}

View File

@@ -0,0 +1,18 @@
package fr.dcproject.component.comment.article.routes
import fr.dcproject.component.comment.article.routes.CreateCommentArticle.createCommentArticle
import fr.dcproject.component.comment.article.routes.GetArticleComments.getArticleComments
import fr.dcproject.component.comment.article.routes.GetCitizenArticleComments.getCitizenArticleComments
import io.ktor.auth.authenticate
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.routing.Routing
import org.koin.ktor.ext.get
@KtorExperimentalLocationsAPI
fun Routing.installCommentArticleRoutes() {
authenticate(optional = true) {
getArticleComments(get(), get())
createCommentArticle(get(), get())
getCitizenArticleComments(get(), get())
}
}

View File

@@ -0,0 +1,54 @@
package fr.dcproject.component.comment.constitution
import fr.dcproject.common.entity.TargetI
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.component.constitution.ConstitutionRef
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.UuidEntityI
import java.util.UUID
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

@@ -0,0 +1,41 @@
package fr.dcproject.component.comment.constitution.routes
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.comment.constitution.CommentConstitutionRepository
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.CommentForUpdate
import fr.dcproject.component.constitution.ConstitutionRef
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.post
import io.ktor.request.receiveText
import io.ktor.response.respond
import io.ktor.routing.Route
import java.util.UUID
@KtorExperimentalLocationsAPI
object CreateConstitutionComment {
@Location("/constitutions/{constitution}/comments")
class CreateConstitutionCommentRequest(constitution: UUID) {
val constitution = ConstitutionRef(constitution)
}
fun Route.createConstitutionComment(repo: CommentConstitutionRepository, ac: CommentAccessControl) {
post<CreateConstitutionCommentRequest> {
val content = call.receiveText()
val comment = CommentForUpdate(
target = it.constitution,
createdBy = citizen,
content = content
)
ac.assert { canCreate(comment, citizenOrNull) }
repo.comment(comment)
call.respond(HttpStatusCode.Created, comment)
}
}
}

View File

@@ -0,0 +1,30 @@
package fr.dcproject.component.comment.constitution.routes
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.comment.constitution.CommentConstitutionRepository
import fr.dcproject.component.comment.generic.CommentAccessControl
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 java.util.UUID
@KtorExperimentalLocationsAPI
object GetCitizenCommentConstitution {
@Location("/citizens/{citizen}/comments/constitutions")
class GetCitizenCommentConstitutionRequest(citizen: UUID) {
val citizen = CitizenRef(citizen)
}
fun Route.getCitizenCommentConstitution(repo: CommentConstitutionRepository, ac: CommentAccessControl) {
get<GetCitizenCommentConstitutionRequest> {
val comments = repo.findByCitizen(it.citizen)
ac.assert { canView(comments.result, citizenOrNull) }
call.respond(comments)
}
}
}

View File

@@ -0,0 +1,31 @@
package fr.dcproject.component.comment.constitution.routes
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.comment.constitution.CommentConstitutionRepository
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.constitution.ConstitutionRef
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.response.respond
import io.ktor.routing.Route
import java.util.UUID
@KtorExperimentalLocationsAPI
object GetConstitutionComment {
@Location("/constitutions/{constitution}/comments")
class GetConstitutionCommentRequest(constitution: UUID) {
val constitution = ConstitutionRef(constitution)
}
fun Route.getConstitutionComment(repo: CommentConstitutionRepository, ac: CommentAccessControl) {
get<GetConstitutionCommentRequest> {
val comments = repo.findByTarget(it.constitution)
ac.assert { canView(comments.result, citizenOrNull) }
call.respond(HttpStatusCode.OK, comments)
}
}
}

View File

@@ -0,0 +1,18 @@
package fr.dcproject.component.comment.constitution.routes
import fr.dcproject.component.comment.constitution.routes.CreateConstitutionComment.createConstitutionComment
import fr.dcproject.component.comment.constitution.routes.GetCitizenCommentConstitution.getCitizenCommentConstitution
import fr.dcproject.component.comment.constitution.routes.GetConstitutionComment.getConstitutionComment
import io.ktor.auth.authenticate
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.routing.Routing
import org.koin.ktor.ext.get
@KtorExperimentalLocationsAPI
fun Routing.installCommentConstitutionRoutes() {
authenticate(optional = true) {
createConstitutionComment(get(), get())
getCitizenCommentConstitution(get(), get())
getConstitutionComment(get(), get())
}
}

View File

@@ -0,0 +1,95 @@
package fr.dcproject.component.comment.generic
import fr.dcproject.common.entity.EntityI
import fr.dcproject.common.entity.ExtraI
import fr.dcproject.common.entity.HasTarget
import fr.dcproject.common.entity.TargetI
import fr.dcproject.common.entity.TargetRef
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.vote.entity.Votable
import fr.dcproject.component.vote.entity.VotableImp
import fr.postgresjson.entity.EntityCreatedAt
import fr.postgresjson.entity.EntityCreatedAtImp
import fr.postgresjson.entity.EntityCreatedBy
import fr.postgresjson.entity.EntityCreatedByImp
import fr.postgresjson.entity.EntityDeletedAt
import fr.postgresjson.entity.EntityDeletedAtImp
import fr.postgresjson.entity.EntityUpdatedAt
import fr.postgresjson.entity.EntityUpdatedAtImp
import org.joda.time.DateTime
import java.util.UUID
class CommentForView<T : TargetI, C : CitizenRef>(
id: UUID = UUID.randomUUID(),
override val createdBy: C,
override val target: T,
override var content: String,
override val parent: CommentParent<T>? = null,
val childrenCount: Int? = null,
override val deletedAt: DateTime? = null
) : ExtraI<T, C>,
CommentWithParentI<T>,
CommentForUpdate<T, C>(id, createdBy, target, content, parent, deletedAt),
CommentWithTargetI<T>,
EntityCreatedBy<C> by EntityCreatedByImp(createdBy),
EntityUpdatedAt by EntityUpdatedAtImp(),
EntityDeletedAt by EntityDeletedAtImp(),
Votable by VotableImp(),
TargetI {
constructor(
createdBy: C,
parent: CommentParent<T>,
content: String
) : this(
createdBy = createdBy,
parent = parent,
target = parent.target,
content = content
)
}
open class CommentForUpdate<T : TargetI, C : CitizenRef>(
override val id: UUID = UUID.randomUUID(),
override val createdBy: C,
override val target: T,
open var content: String,
override val parent: CommentParent<T>? = null,
override val deletedAt: DateTime? = null
) : CommentParent<T>(id, deletedAt, target),
CommentWithParentI<T>,
ExtraI<T, C>,
CommentWithTargetI<T>,
EntityCreatedAt by EntityCreatedAtImp(),
EntityCreatedBy<C>,
EntityDeletedAt,
TargetI {
constructor(
createdBy: C,
parent: CommentParent<T>,
content: String
) : this(
createdBy = createdBy,
parent = parent,
target = parent.target,
content = content
)
}
open class CommentParent<T : TargetI>(
override val id: UUID,
override val deletedAt: DateTime?,
override val target: T
) : CommentRef(id),
CommentParentI<T>
interface CommentParentI<T : TargetI> : CommentI, EntityDeletedAt, CommentWithTargetI<T>
interface CommentWithTargetI<T : TargetI> : CommentI, TargetI, HasTarget<T>
interface CommentWithParentI<T : TargetI> {
val parent: CommentParent<T>?
}
open class CommentRef(id: UUID = UUID.randomUUID()) : CommentI, TargetRef(id)
interface CommentI : EntityI

View File

@@ -0,0 +1,41 @@
package fr.dcproject.component.comment.generic
import fr.dcproject.common.entity.HasTarget
import fr.dcproject.common.security.AccessControl
import fr.dcproject.common.security.AccessResponse
import fr.dcproject.component.citizen.CitizenI
import fr.postgresjson.entity.EntityCreatedBy
import fr.postgresjson.entity.EntityDeletedAt
class CommentAccessControl : AccessControl() {
fun <S> canView(subjects: List<S>, citizen: CitizenI?): AccessResponse
where S : CommentI,
S : EntityDeletedAt = canAll(subjects) { canView(it, citizen) }
fun <S> canView(subject: S, citizen: CitizenI?): AccessResponse
where S : CommentI,
S : EntityDeletedAt = when {
subject.isDeleted() -> denied("Your cannot view a deleted comment", "comment.view.deleted")
else -> granted()
}
fun <S, CR : CitizenI> canCreate(subject: S, citizen: CitizenI?): AccessResponse
where S : CommentI,
S : EntityCreatedBy<CR>,
S : CommentWithParentI<*>,
S : HasTarget<*> = when {
citizen == null -> denied("You must be connected to create user", "comment.create.notConnected")
subject.createdBy.id != citizen.id -> denied("You cannot create a comment with other user than yours", "comment.create.wrongUser")
subject.parent?.isDeleted() ?: false -> denied("You cannot create a comment on deleted parent", "comment.create.deletedParent")
subject.target.let { it is EntityDeletedAt && it.isDeleted() } -> denied("You cannot create a comment on deleted target", "comment.create.deletedTarget")
else -> granted()
}
fun <S, CR : CitizenI> canUpdate(subject: S, citizen: CitizenI?): AccessResponse
where S : CommentI,
S : EntityCreatedBy<CR> = when {
citizen == null -> denied("You must be connected to update comment", "comment.update.notConnected")
citizen.id != subject.createdBy.id -> denied("You cannot update another user of yours", "comment.update.notYours")
else -> granted()
}
}

View File

@@ -0,0 +1,127 @@
package fr.dcproject.component.comment.generic
import fr.dcproject.common.entity.TargetI
import fr.dcproject.common.entity.TargetRef
import fr.dcproject.component.citizen.CitizenI
import fr.dcproject.component.citizen.CitizenRef
import fr.dcproject.component.comment.article.CommentArticleRepository
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.entity.UuidEntityI
import fr.postgresjson.repository.RepositoryI
import java.util.UUID
abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Requester) : RepositoryI {
abstract fun findById(id: UUID): CommentForView<T, CitizenRef>?
abstract fun findByCitizen(
citizen: CitizenI,
page: Int = 1,
limit: Int = 50
): Paginated<CommentForView<T, CitizenRef>>
open fun findByParent(
parent: CommentForView<T, CitizenRef>,
page: Int = 1,
limit: Int = 50
): Paginated<CommentForView<T, CitizenRef>> {
return findByParent(parent.id, page, limit)
}
open fun findByParent(
parentId: UUID,
page: Int = 1,
limit: Int = 50
): Paginated<CommentForView<T, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_parent")
.select(
page,
limit,
"parent_id" to parentId
)
}
}
open fun findByTarget(
target: UuidEntityI,
page: Int = 1,
limit: Int = 50,
sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.CREATED_AT
): Paginated<CommentForView<T, CitizenRef>> {
return findByTarget(target.id, page, limit, sort)
}
open fun findByTarget(
targetId: UUID,
page: Int = 1,
limit: Int = 50,
sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.CREATED_AT
): Paginated<CommentForView<T, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_target")
.select(
page,
limit,
"target_id" to targetId,
"sort" to sort.sql
)
}
}
fun <I : TargetI, C : CitizenRef> comment(comment: CommentForUpdate<I, C>) {
requester
.getFunction("comment")
.sendQuery(
"reference" to comment.target.reference,
"resource" to comment
)
}
fun <I : T> edit(comment: CommentForUpdate<I, CitizenRef>) {
requester
.getFunction("edit_comment")
.sendQuery(
"id" to comment.id,
"content" to comment.content
)
}
}
class CommentRepository(requester: Requester) : CommentRepositoryAbs<TargetRef>(requester) {
override fun findById(id: UUID): CommentForView<TargetRef, CitizenRef>? {
return requester
.getFunction("find_comment_by_id")
.selectOne(mapOf("id" to id))
}
override fun findByCitizen(
citizen: CitizenI,
page: Int,
limit: Int
): Paginated<CommentForView<TargetRef, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_citizen")
.select(
page,
limit,
"created_by_id" to citizen.id
)
}
}
override fun findByParent(
parentId: UUID,
page: Int,
limit: Int
): Paginated<CommentForView<TargetRef, CitizenRef>> {
return requester.run {
getFunction("find_comments_by_parent")
.select(
page,
limit,
"parent_id" to parentId
)
}
}
}

View File

@@ -0,0 +1,44 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.common.security.assert
import fr.dcproject.common.utils.receiveOrBadRequest
import fr.dcproject.component.auth.citizen
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.CommentForUpdate
import fr.dcproject.component.comment.generic.CommentRef
import fr.dcproject.component.comment.generic.CommentRepository
import io.ktor.application.call
import io.ktor.features.NotFoundException
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.post
import io.ktor.response.respond
import io.ktor.routing.Route
import java.util.UUID
@KtorExperimentalLocationsAPI
object CreateCommentChildren {
@Location("/comments/{comment}/children")
class CreateCommentChildrenRequest(comment: UUID) {
val comment = CommentRef(comment)
class Input(val content: String)
}
fun Route.createCommentChildren(repo: CommentRepository, ac: CommentAccessControl) {
post<CreateCommentChildrenRequest> {
val parent = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
val newComment = CommentForUpdate(
content = call.receiveOrBadRequest<CreateCommentChildrenRequest.Input>().content,
createdBy = citizen,
parent = parent
)
ac.assert { canCreate(newComment, citizenOrNull) }
repo.comment(newComment)
call.respond(HttpStatusCode.Created, newComment)
}
}
}

View File

@@ -0,0 +1,37 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.CommentRef
import fr.dcproject.component.comment.generic.CommentRepository
import io.ktor.application.call
import io.ktor.features.NotFoundException
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.locations.Location
import io.ktor.locations.put
import io.ktor.request.receiveText
import io.ktor.response.respond
import io.ktor.routing.Route
import java.util.UUID
@KtorExperimentalLocationsAPI
object EditComment {
@Location("/comments/{comment}")
class EditCommentRequest(comment: UUID) {
val comment = CommentRef(comment)
}
fun Route.editComment(repo: CommentRepository, ac: CommentAccessControl) {
put<EditCommentRequest> {
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
ac.assert { canUpdate(comment, citizenOrNull) }
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.common.security.assert
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.CommentRepository
import fr.dcproject.routes.PaginatedRequest
import fr.dcproject.routes.PaginatedRequestI
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.response.respond
import io.ktor.routing.Route
import java.util.UUID
@KtorExperimentalLocationsAPI
object GetCommentChildren {
@Location("/comments/{comment}/children")
class CommentChildrenRequest(
val comment: UUID,
page: Int = 1,
limit: Int = 50,
val search: String? = null
) : PaginatedRequestI by PaginatedRequest(page, limit)
fun Route.getChildrenComments(repo: CommentRepository, ac: CommentAccessControl) {
get<CommentChildrenRequest> {
val comments =
repo.findByParent(
it.comment,
it.page,
it.limit
)
ac.assert { canView(comments.result, citizenOrNull) }
call.respond(HttpStatusCode.OK, comments)
}
}
}

View File

@@ -0,0 +1,33 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.common.security.assert
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.CommentRef
import fr.dcproject.component.comment.generic.CommentRepository
import io.ktor.application.call
import io.ktor.features.NotFoundException
import io.ktor.http.HttpStatusCode
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 java.util.UUID
@KtorExperimentalLocationsAPI
object GetOneComment {
@Location("/comments/{comment}")
class CommentRequest(comment: UUID) {
val comment = CommentRef(comment)
}
fun Route.getOneComment(repo: CommentRepository, ac: CommentAccessControl) {
get<CommentRequest> {
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment ${it.comment.id} not found")
ac.assert { canView(comment, citizenOrNull) }
call.respond(HttpStatusCode.OK, comment)
}
}
}

View File

@@ -0,0 +1,20 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.component.comment.generic.routes.CreateCommentChildren.createCommentChildren
import fr.dcproject.component.comment.generic.routes.EditComment.editComment
import fr.dcproject.component.comment.generic.routes.GetCommentChildren.getChildrenComments
import fr.dcproject.component.comment.generic.routes.GetOneComment.getOneComment
import io.ktor.auth.authenticate
import io.ktor.locations.KtorExperimentalLocationsAPI
import io.ktor.routing.Routing
import org.koin.ktor.ext.get
@KtorExperimentalLocationsAPI
fun Routing.installCommentRoutes() {
authenticate(optional = true) {
editComment(get(), get())
getOneComment(get(), get())
createCommentChildren(get(), get())
getChildrenComments(get(), get())
}
}