Add validation on route CreateComments & EditComment

rename POST /comments/{comment}/children
method edit and create comment of repository return edited/created comment
This commit is contained in:
2021-04-10 01:16:09 +02:00
parent 27e405c585
commit 8223dd21bb
11 changed files with 307 additions and 107 deletions

View File

@@ -63,12 +63,14 @@ open class CommentForUpdate<T : TargetI, C : CitizenI>(
constructor(
createdBy: C,
parent: CommentParent<T>,
content: String
content: String,
id: UUID? = null,
) : this(
createdBy = createdBy,
parent = parent,
target = parent.target,
content = content
content = content,
id = id ?: UUID.randomUUID(),
)
}

View File

@@ -58,35 +58,29 @@ abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Request
page: Int = 1,
limit: Int = 50,
sort: String = "createdAt"
): Paginated<CommentForView<T, CitizenCreatorI>> {
return requester.run {
getFunction("find_comments_by_target")
.select<CommentForView<T, CitizenCreator>>(
page,
limit,
"target_id" to targetId,
"sort" to sort
)
as Paginated<CommentForView<T, CitizenCreatorI>>
}
}
): Paginated<CommentForView<T, CitizenCreatorI>> = requester
.getFunction("find_comments_by_target")
.select<CommentForView<T, CitizenCreator>>(
page,
limit,
"target_id" to targetId,
"sort" to sort
) as Paginated<CommentForView<T, CitizenCreatorI>>
fun <I : TargetI, C : CitizenCreatorI> comment(comment: CommentForUpdate<I, C>) {
requester
.getFunction("comment")
.sendQuery(
"reference" to comment.target.reference,
"resource" to comment
)
}
fun <I : TargetI, C : CitizenCreatorI> comment(comment: CommentForUpdate<I, C>): CommentForView<TargetRef, CitizenCreator> = requester
.getFunction("comment")
.selectOne(
"reference" to comment.target.reference,
"resource" to comment
)!!
fun <I : T> edit(comment: CommentForUpdate<I, CitizenCreatorI>) {
requester
fun <I : T> edit(comment: CommentForUpdate<I, CitizenCreatorI>): CommentForView<TargetRef, CitizenCreator> {
return requester
.getFunction("edit_comment")
.sendQuery(
.selectOne(
"id" to comment.id,
"content" to comment.content
)
)!!
}
}

View File

@@ -0,0 +1,63 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.application.http.badRequestIfNotValid
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.auth.mustBeAuth
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.database.CommentForUpdate
import fr.dcproject.component.comment.generic.database.CommentRef
import fr.dcproject.component.comment.generic.database.CommentRepository
import fr.dcproject.component.comment.toOutput
import io.konform.validation.Validation
import io.konform.validation.jsonschema.maxLength
import io.konform.validation.jsonschema.minLength
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 CreateComment {
@Location("/comments/{comment}")
class CreateCommentRequest(comment: UUID) {
val comment = CommentRef(comment)
class Input(val content: String) {
fun validate() = Validation<Input> {
Input::content {
minLength(20)
maxLength(6000)
}
}.validate(this)
}
}
fun Route.createCommentChildren(repo: CommentRepository, ac: CommentAccessControl) {
post<CreateCommentRequest> {
mustBeAuth()
call.receiveOrBadRequest<CreateCommentRequest.Input>()
.apply { validate().badRequestIfNotValid() }
.run {
val parent = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
CommentForUpdate(
content = content,
createdBy = citizen,
target = parent.target,
parent = parent,
)
}.let { newComment ->
ac.assert { canCreate(newComment, citizenOrNull) }
repo.comment(newComment)
call.respond(HttpStatusCode.Created, newComment.toOutput())
}
}
}
}

View File

@@ -1,47 +0,0 @@
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.auth.mustBeAuth
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.database.CommentForUpdate
import fr.dcproject.component.comment.generic.database.CommentRef
import fr.dcproject.component.comment.generic.database.CommentRepository
import fr.dcproject.component.comment.toOutput
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> {
mustBeAuth()
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.toOutput())
}
}
}

View File

@@ -1,14 +1,18 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.common.response.toOutput
import fr.dcproject.application.http.badRequestIfNotValid
import fr.dcproject.common.security.assert
import fr.dcproject.common.utils.receiveOrBadRequest
import fr.dcproject.component.auth.citizenOrNull
import fr.dcproject.component.auth.mustBeAuth
import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.generic.database.CommentForUpdate
import fr.dcproject.component.comment.generic.database.CommentRef
import fr.dcproject.component.comment.generic.database.CommentRepository
import fr.dcproject.component.comment.toOutput
import io.konform.validation.Validation
import io.konform.validation.jsonschema.maxLength
import io.konform.validation.jsonschema.minLength
import io.ktor.application.call
import io.ktor.features.NotFoundException
import io.ktor.http.HttpStatusCode
@@ -24,22 +28,40 @@ object EditComment {
@Location("/comments/{comment}")
class EditCommentRequest(comment: UUID) {
val comment = CommentRef(comment)
class Input(val content: String)
class Input(val content: String) {
fun validate() = Validation<Input> {
Input::content {
minLength(20)
maxLength(6000)
}
}.validate(this)
}
}
fun Route.editComment(repo: CommentRepository, ac: CommentAccessControl) {
put<EditCommentRequest> {
mustBeAuth()
val comment = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
ac.assert { canUpdate(comment, citizenOrNull) }
val commentOld = repo.findById(it.comment.id) ?: throw NotFoundException("Comment not found")
ac.assert { canUpdate(commentOld, citizenOrNull) }
comment.content = call.receiveOrBadRequest<EditCommentRequest.Input>().content
repo.edit(comment)
call.respond(
HttpStatusCode.OK,
comment.toOutput()
)
call.receiveOrBadRequest<EditCommentRequest.Input>()
.apply { validate().badRequestIfNotValid() }
.run {
CommentForUpdate(
id = commentOld.id,
createdBy = commentOld.createdBy,
target = commentOld.target,
parent = commentOld.parent,
content = content,
)
}
.let { repo.edit(it) }
.let {
call.respond(
HttpStatusCode.OK,
it.toOutput()
)
}
}
}
}

View File

@@ -1,6 +1,6 @@
package fr.dcproject.component.comment.generic.routes
import fr.dcproject.component.comment.generic.routes.CreateCommentChildren.createCommentChildren
import fr.dcproject.component.comment.generic.routes.CreateComment.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

View File

@@ -598,6 +598,40 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CommentResponse'
post:
security:
- JWTAuth: []
summary: create comment
tags:
- comment
requestBody:
content:
application/json:
schema:
required:
- content
properties:
content:
type: string
example:
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
responses:
201:
description: Return updated comment
content:
application/json:
schema:
$ref: '#/components/schemas/CommentResponse'
400:
description: BadReqest
content:
application/json:
schema:
$ref: '#/components/schemas/400'
401:
$ref: '#/components/responses/401'
404:
description: No comment found
put:
security:
- JWTAuth: []
@@ -614,7 +648,7 @@ paths:
content:
type: string
example:
Lorem ipsum...
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
responses:
200:
description: Return updated comment
@@ -622,6 +656,12 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/CommentResponse'
400:
description: BadReqest
content:
application/json:
schema:
$ref: '#/components/schemas/400'
401:
$ref: '#/components/responses/401'
/comments/{comment}/children:

View File

@@ -1,4 +1,4 @@
create or replace function comment(reference regclass, resource json, out _id uuid)
create or replace function comment(reference regclass, inout resource json)
language plpgsql as
$$
declare
@@ -17,7 +17,8 @@ begin
else
raise exception 'comment with target as "%", is not implemented', reference::text;
end if;
_id = _new_id;
select find_comment_by_id(_new_id) into resource;
end;
$$;

View File

@@ -1,9 +1,11 @@
create or replace function edit_comment(_id uuid, _content text) returns void
create or replace function edit_comment(_id uuid, _content text, out resource json)
language plpgsql as
$$
begin
update comment c set
"content" = _content
where c.id = _id;
select find_comment_by_id(_id) into resource;
end;
$$;