Valider les resource entrente #91

Merged
flecomte merged 46 commits from 21-valid-input into master 2021-04-16 03:27:11 +02:00
7 changed files with 57 additions and 24 deletions
Showing only changes of commit f5c1aa29e8 - Show all commits

View File

@@ -41,7 +41,7 @@ class CommentArticleRepository(requester: Requester) : CommentRepositoryAbs<Arti
target: EntityI,
page: Int,
limit: Int,
sort: Sort
sort: String
): Paginated<CommentForView<ArticleForView, CitizenCreatorI>> {
return requester
.getFunction("find_comments_by_target")
@@ -49,18 +49,7 @@ class CommentArticleRepository(requester: Requester) : CommentRepositoryAbs<Arti
page,
limit,
"target_id" to target.id,
"sort" to sort.sql
"sort" to sort
) as Paginated<CommentForView<ArticleForView, CitizenCreatorI>>
}
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,5 +1,6 @@
package fr.dcproject.component.comment.article.routes
import fr.dcproject.application.http.badRequestIfNotValid
import fr.dcproject.common.response.toOutput
import fr.dcproject.common.security.assert
import fr.dcproject.component.article.database.ArticleRef
@@ -9,6 +10,10 @@ import fr.dcproject.component.comment.generic.CommentAccessControl
import fr.dcproject.component.comment.toOutput
import fr.dcproject.routes.PaginatedRequest
import fr.dcproject.routes.PaginatedRequestI
import io.konform.validation.Validation
import io.konform.validation.jsonschema.enum
import io.konform.validation.jsonschema.maximum
import io.konform.validation.jsonschema.minimum
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.locations.KtorExperimentalLocationsAPI
@@ -26,14 +31,31 @@ object GetArticleComments {
page: Int = 1,
limit: Int = 50,
val search: String? = null,
sort: String = CommentArticleRepository.Sort.CREATED_AT.sql
val sort: String = "createdAt"
) : PaginatedRequestI by PaginatedRequest(page, limit) {
val article = ArticleRef(article)
val sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.fromString(sort) ?: CommentArticleRepository.Sort.CREATED_AT
fun validate() = Validation<ArticleCommentsRequest> {
ArticleCommentsRequest::page {
minimum(1)
}
ArticleCommentsRequest::limit {
minimum(1)
maximum(50)
}
ArticleCommentsRequest::sort ifPresent {
enum(
"votes",
"createdAt",
)
}
}.validate(this)
}
fun Route.getArticleComments(repo: CommentArticleRepository, ac: CommentAccessControl) {
get<ArticleCommentsRequest> {
it.validate().badRequestIfNotValid()
val comments = repo.findByTarget(it.article, it.page, it.limit, it.sort)
if (comments.result.isNotEmpty()) {
ac.assert { canView(comments.result, citizenOrNull) }

View File

@@ -5,7 +5,6 @@ import fr.dcproject.common.entity.TargetI
import fr.dcproject.component.citizen.database.CitizenCreator
import fr.dcproject.component.citizen.database.CitizenCreatorI
import fr.dcproject.component.citizen.database.CitizenI
import fr.dcproject.component.comment.article.database.CommentArticleRepository
import fr.dcproject.component.comment.generic.database.CommentForView
import fr.dcproject.component.comment.generic.database.CommentRepositoryAbs
import fr.dcproject.component.constitution.database.ConstitutionRef
@@ -41,7 +40,7 @@ class CommentConstitutionRepository(requester: Requester) : CommentRepositoryAbs
target: EntityI,
page: Int,
limit: Int,
sort: CommentArticleRepository.Sort
sort: String
): Paginated<CommentForView<ConstitutionRef, CitizenCreatorI>> {
return requester.run {
getFunction("find_comments_by_target")
@@ -49,7 +48,7 @@ class CommentConstitutionRepository(requester: Requester) : CommentRepositoryAbs
page,
limit,
"target_id" to target.id,
"sort" to sort.sql
"sort" to sort
)
as Paginated<CommentForView<ConstitutionRef, CitizenCreatorI>>
}

View File

@@ -6,7 +6,6 @@ import fr.dcproject.common.entity.TargetRef
import fr.dcproject.component.citizen.database.CitizenCreator
import fr.dcproject.component.citizen.database.CitizenCreatorI
import fr.dcproject.component.citizen.database.CitizenI
import fr.dcproject.component.comment.article.database.CommentArticleRepository
import fr.postgresjson.connexion.Paginated
import fr.postgresjson.connexion.Requester
import fr.postgresjson.repository.RepositoryI
@@ -49,7 +48,7 @@ abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Request
target: EntityI,
page: Int = 1,
limit: Int = 50,
sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.CREATED_AT
sort: String = "createdAt"
): Paginated<CommentForView<T, CitizenCreatorI>> {
return findByTarget(target.id, page, limit, sort)
}
@@ -58,7 +57,7 @@ abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Request
targetId: UUID,
page: Int = 1,
limit: Int = 50,
sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.CREATED_AT
sort: String = "createdAt"
): Paginated<CommentForView<T, CitizenCreatorI>> {
return requester.run {
getFunction("find_comments_by_target")
@@ -66,7 +65,7 @@ abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Request
page,
limit,
"target_id" to targetId,
"sort" to sort.sql
"sort" to sort
)
as Paginated<CommentForView<T, CitizenCreatorI>>
}

View File

@@ -544,6 +544,12 @@ paths:
type: array
items:
$ref: '#/components/schemas/CommentResponse'
400:
description: BadReqest
content:
application/json:
schema:
$ref: '#/components/schemas/400'
post:
security:
- JWTAuth: [ ]

View File

@@ -26,7 +26,7 @@ begin
else null
end desc,
case sort
when 'created_at' then com.created_at::text
when 'createdAt' then com.created_at::text
else null
end desc,
com.created_at desc

View File

@@ -3,6 +3,7 @@ package integration
import fr.dcproject.component.citizen.database.CitizenI.Name
import integration.steps.`when`.Validate.ALL
import integration.steps.`when`.Validate.REQUEST_BODY
import integration.steps.`when`.Validate.REQUEST_PARAM
import integration.steps.`when`.`When I send a GET request`
import integration.steps.`when`.`When I send a POST request`
import integration.steps.`when`.`When I send a PUT request`
@@ -78,7 +79,7 @@ class `Comment articles routes` : BaseTest() {
`Given I have citizen`("Enrico", "Fermi")
`Given I have article`(id = "6166c078-ca97-4366-b0aa-2a5cd558c78a")
`Given I have comment on article`(article = "6166c078-ca97-4366-b0aa-2a5cd558c78a", createdBy = Name("Enrico", "Fermi"))
`When I send a GET request`("/articles/6166c078-ca97-4366-b0aa-2a5cd558c78a/comments") {
`When I send a GET request`("/articles/6166c078-ca97-4366-b0aa-2a5cd558c78a/comments?page=1&limit=40&sort=votes") {
`authenticated as`("Enrico", "Fermi")
} `Then the response should be` OK and {
`And the response should not be null`()
@@ -87,6 +88,23 @@ class `Comment articles routes` : BaseTest() {
}
}
@Test
@Tag("BadRequest")
fun `I cannot get all comment on article with wrong parameters`() {
withIntegrationApplication {
`Given I have citizen`("Enrico", "Fermi")
`Given I have article`(id = "6166c078-ca97-4366-b0aa-2a5cd558c78a")
`Given I have comment on article`(article = "6166c078-ca97-4366-b0aa-2a5cd558c78a", createdBy = Name("Enrico", "Fermi"))
`When I send a GET request`("/articles/6166c078-ca97-4366-b0aa-2a5cd558c78a/comments?page=1&limit=40&sort=wrong", ALL - REQUEST_PARAM) {
`authenticated as`("Enrico", "Fermi")
} `Then the response should be` BadRequest and {
`And the response should not be null`()
`And the response should contain`("$.invalidParams[*].name", ".sort")
`And the response should contain`("$.invalidParams[*].reason", "must be one of: 'votes', 'createdAt'")
}
}
}
/* TODO add votes */
@Test
fun `I can get all comment on article sorted by votes`() {