Valider les resource entrente #91
@@ -41,7 +41,7 @@ class CommentArticleRepository(requester: Requester) : CommentRepositoryAbs<Arti
|
|||||||
target: EntityI,
|
target: EntityI,
|
||||||
page: Int,
|
page: Int,
|
||||||
limit: Int,
|
limit: Int,
|
||||||
sort: Sort
|
sort: String
|
||||||
): Paginated<CommentForView<ArticleForView, CitizenCreatorI>> {
|
): Paginated<CommentForView<ArticleForView, CitizenCreatorI>> {
|
||||||
return requester
|
return requester
|
||||||
.getFunction("find_comments_by_target")
|
.getFunction("find_comments_by_target")
|
||||||
@@ -49,18 +49,7 @@ class CommentArticleRepository(requester: Requester) : CommentRepositoryAbs<Arti
|
|||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
"target_id" to target.id,
|
"target_id" to target.id,
|
||||||
"sort" to sort.sql
|
"sort" to sort
|
||||||
) as Paginated<CommentForView<ArticleForView, CitizenCreatorI>>
|
) 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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package fr.dcproject.component.comment.article.routes
|
package fr.dcproject.component.comment.article.routes
|
||||||
|
|
||||||
|
import fr.dcproject.application.http.badRequestIfNotValid
|
||||||
import fr.dcproject.common.response.toOutput
|
import fr.dcproject.common.response.toOutput
|
||||||
import fr.dcproject.common.security.assert
|
import fr.dcproject.common.security.assert
|
||||||
import fr.dcproject.component.article.database.ArticleRef
|
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.component.comment.toOutput
|
||||||
import fr.dcproject.routes.PaginatedRequest
|
import fr.dcproject.routes.PaginatedRequest
|
||||||
import fr.dcproject.routes.PaginatedRequestI
|
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.application.call
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.locations.KtorExperimentalLocationsAPI
|
import io.ktor.locations.KtorExperimentalLocationsAPI
|
||||||
@@ -26,14 +31,31 @@ object GetArticleComments {
|
|||||||
page: Int = 1,
|
page: Int = 1,
|
||||||
limit: Int = 50,
|
limit: Int = 50,
|
||||||
val search: String? = null,
|
val search: String? = null,
|
||||||
sort: String = CommentArticleRepository.Sort.CREATED_AT.sql
|
val sort: String = "createdAt"
|
||||||
) : PaginatedRequestI by PaginatedRequest(page, limit) {
|
) : PaginatedRequestI by PaginatedRequest(page, limit) {
|
||||||
val article = ArticleRef(article)
|
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) {
|
fun Route.getArticleComments(repo: CommentArticleRepository, ac: CommentAccessControl) {
|
||||||
get<ArticleCommentsRequest> {
|
get<ArticleCommentsRequest> {
|
||||||
|
it.validate().badRequestIfNotValid()
|
||||||
|
|
||||||
val comments = repo.findByTarget(it.article, it.page, it.limit, it.sort)
|
val comments = repo.findByTarget(it.article, it.page, it.limit, it.sort)
|
||||||
if (comments.result.isNotEmpty()) {
|
if (comments.result.isNotEmpty()) {
|
||||||
ac.assert { canView(comments.result, citizenOrNull) }
|
ac.assert { canView(comments.result, citizenOrNull) }
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import fr.dcproject.common.entity.TargetI
|
|||||||
import fr.dcproject.component.citizen.database.CitizenCreator
|
import fr.dcproject.component.citizen.database.CitizenCreator
|
||||||
import fr.dcproject.component.citizen.database.CitizenCreatorI
|
import fr.dcproject.component.citizen.database.CitizenCreatorI
|
||||||
import fr.dcproject.component.citizen.database.CitizenI
|
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.CommentForView
|
||||||
import fr.dcproject.component.comment.generic.database.CommentRepositoryAbs
|
import fr.dcproject.component.comment.generic.database.CommentRepositoryAbs
|
||||||
import fr.dcproject.component.constitution.database.ConstitutionRef
|
import fr.dcproject.component.constitution.database.ConstitutionRef
|
||||||
@@ -41,7 +40,7 @@ class CommentConstitutionRepository(requester: Requester) : CommentRepositoryAbs
|
|||||||
target: EntityI,
|
target: EntityI,
|
||||||
page: Int,
|
page: Int,
|
||||||
limit: Int,
|
limit: Int,
|
||||||
sort: CommentArticleRepository.Sort
|
sort: String
|
||||||
): Paginated<CommentForView<ConstitutionRef, CitizenCreatorI>> {
|
): Paginated<CommentForView<ConstitutionRef, CitizenCreatorI>> {
|
||||||
return requester.run {
|
return requester.run {
|
||||||
getFunction("find_comments_by_target")
|
getFunction("find_comments_by_target")
|
||||||
@@ -49,7 +48,7 @@ class CommentConstitutionRepository(requester: Requester) : CommentRepositoryAbs
|
|||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
"target_id" to target.id,
|
"target_id" to target.id,
|
||||||
"sort" to sort.sql
|
"sort" to sort
|
||||||
)
|
)
|
||||||
as Paginated<CommentForView<ConstitutionRef, CitizenCreatorI>>
|
as Paginated<CommentForView<ConstitutionRef, CitizenCreatorI>>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import fr.dcproject.common.entity.TargetRef
|
|||||||
import fr.dcproject.component.citizen.database.CitizenCreator
|
import fr.dcproject.component.citizen.database.CitizenCreator
|
||||||
import fr.dcproject.component.citizen.database.CitizenCreatorI
|
import fr.dcproject.component.citizen.database.CitizenCreatorI
|
||||||
import fr.dcproject.component.citizen.database.CitizenI
|
import fr.dcproject.component.citizen.database.CitizenI
|
||||||
import fr.dcproject.component.comment.article.database.CommentArticleRepository
|
|
||||||
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
|
||||||
@@ -49,7 +48,7 @@ abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Request
|
|||||||
target: EntityI,
|
target: EntityI,
|
||||||
page: Int = 1,
|
page: Int = 1,
|
||||||
limit: Int = 50,
|
limit: Int = 50,
|
||||||
sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.CREATED_AT
|
sort: String = "createdAt"
|
||||||
): Paginated<CommentForView<T, CitizenCreatorI>> {
|
): Paginated<CommentForView<T, CitizenCreatorI>> {
|
||||||
return findByTarget(target.id, page, limit, sort)
|
return findByTarget(target.id, page, limit, sort)
|
||||||
}
|
}
|
||||||
@@ -58,7 +57,7 @@ abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Request
|
|||||||
targetId: UUID,
|
targetId: UUID,
|
||||||
page: Int = 1,
|
page: Int = 1,
|
||||||
limit: Int = 50,
|
limit: Int = 50,
|
||||||
sort: CommentArticleRepository.Sort = CommentArticleRepository.Sort.CREATED_AT
|
sort: String = "createdAt"
|
||||||
): Paginated<CommentForView<T, CitizenCreatorI>> {
|
): Paginated<CommentForView<T, CitizenCreatorI>> {
|
||||||
return requester.run {
|
return requester.run {
|
||||||
getFunction("find_comments_by_target")
|
getFunction("find_comments_by_target")
|
||||||
@@ -66,7 +65,7 @@ abstract class CommentRepositoryAbs<T : TargetI>(override var requester: Request
|
|||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
"target_id" to targetId,
|
"target_id" to targetId,
|
||||||
"sort" to sort.sql
|
"sort" to sort
|
||||||
)
|
)
|
||||||
as Paginated<CommentForView<T, CitizenCreatorI>>
|
as Paginated<CommentForView<T, CitizenCreatorI>>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -544,6 +544,12 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/CommentResponse'
|
$ref: '#/components/schemas/CommentResponse'
|
||||||
|
400:
|
||||||
|
description: BadReqest
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/400'
|
||||||
post:
|
post:
|
||||||
security:
|
security:
|
||||||
- JWTAuth: [ ]
|
- JWTAuth: [ ]
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ begin
|
|||||||
else null
|
else null
|
||||||
end desc,
|
end desc,
|
||||||
case sort
|
case sort
|
||||||
when 'created_at' then com.created_at::text
|
when 'createdAt' then com.created_at::text
|
||||||
else null
|
else null
|
||||||
end desc,
|
end desc,
|
||||||
com.created_at desc
|
com.created_at desc
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package integration
|
|||||||
import fr.dcproject.component.citizen.database.CitizenI.Name
|
import fr.dcproject.component.citizen.database.CitizenI.Name
|
||||||
import integration.steps.`when`.Validate.ALL
|
import integration.steps.`when`.Validate.ALL
|
||||||
import integration.steps.`when`.Validate.REQUEST_BODY
|
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 GET request`
|
||||||
import integration.steps.`when`.`When I send a POST request`
|
import integration.steps.`when`.`When I send a POST request`
|
||||||
import integration.steps.`when`.`When I send a PUT 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 citizen`("Enrico", "Fermi")
|
||||||
`Given I have article`(id = "6166c078-ca97-4366-b0aa-2a5cd558c78a")
|
`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"))
|
`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")
|
`authenticated as`("Enrico", "Fermi")
|
||||||
} `Then the response should be` OK and {
|
} `Then the response should be` OK and {
|
||||||
`And the response should not be null`()
|
`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 */
|
/* TODO add votes */
|
||||||
@Test
|
@Test
|
||||||
fun `I can get all comment on article sorted by votes`() {
|
fun `I can get all comment on article sorted by votes`() {
|
||||||
|
|||||||
Reference in New Issue
Block a user