Valider les resource entrente #91

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

View File

@@ -38,7 +38,6 @@ object FindArticles {
fun validate() = Validation<ArticlesRequest> { fun validate() = Validation<ArticlesRequest> {
ArticlesRequest::page { ArticlesRequest::page {
minimum(1) minimum(1)
maximum(100)
} }
ArticlesRequest::limit { ArticlesRequest::limit {
minimum(1) minimum(1)

View File

@@ -1,5 +1,6 @@
package fr.dcproject.component.article.routes package fr.dcproject.component.article.routes
import fr.dcproject.application.http.badRequestIfNotValid
import fr.dcproject.common.security.assert import fr.dcproject.common.security.assert
import fr.dcproject.common.utils.receiveOrBadRequest import fr.dcproject.common.utils.receiveOrBadRequest
import fr.dcproject.component.article.ArticleAccessControl import fr.dcproject.component.article.ArticleAccessControl
@@ -12,6 +13,11 @@ import fr.dcproject.component.auth.mustBeAuth
import fr.dcproject.component.notification.ArticleUpdateNotification import fr.dcproject.component.notification.ArticleUpdateNotification
import fr.dcproject.component.notification.Publisher import fr.dcproject.component.notification.Publisher
import fr.dcproject.component.workgroup.database.WorkgroupRef import fr.dcproject.component.workgroup.database.WorkgroupRef
import io.konform.validation.Validation
import io.konform.validation.jsonschema.maxItems
import io.konform.validation.jsonschema.maxLength
import io.konform.validation.jsonschema.minItems
import io.konform.validation.jsonschema.minLength
import io.ktor.application.ApplicationCall import io.ktor.application.ApplicationCall
import io.ktor.application.call import io.ktor.application.call
import io.ktor.locations.KtorExperimentalLocationsAPI import io.ktor.locations.KtorExperimentalLocationsAPI
@@ -35,11 +41,31 @@ object UpsertArticle {
val draft: Boolean = false, val draft: Boolean = false,
val versionId: UUID, val versionId: UUID,
val workgroup: WorkgroupRef? = null, val workgroup: WorkgroupRef? = null,
) ) {
fun validate() = Validation<Input> {
Input::title {
minLength(5)
maxLength(80)
}
Input::content {
minLength(50)
maxLength(6000)
}
Input::description {
minLength(50)
maxLength(6000)
}
Input::tags {
minItems(0)
maxItems(15)
}
}.validate(this)
}
} }
fun Route.upsertArticle(repo: ArticleRepository, publisher: Publisher, ac: ArticleAccessControl) { fun Route.upsertArticle(repo: ArticleRepository, publisher: Publisher, ac: ArticleAccessControl) {
suspend fun ApplicationCall.convertRequestToEntity(): ArticleForUpdate = receiveOrBadRequest<Input>().run { suspend fun ApplicationCall.convertRequestToEntity(): ArticleForUpdate = receiveOrBadRequest<Input>().run {
validate().badRequestIfNotValid()
ArticleForUpdate( ArticleForUpdate(
id = id ?: UUID.randomUUID(), id = id ?: UUID.randomUUID(),
title = title, title = title,

View File

@@ -71,16 +71,21 @@ paths:
Limit power of press Limit power of press
content: content:
type: string type: string
minLength: 50
maxLength: 6000
example: example:
Lorem upsum... Lorem upsum...
description: description:
type: string type: string
minLength: 50
maxLength: 6000
example: example:
I think is the bether choice I think is the bether choice
tags: tags:
type: array type: array
items: items:
type: string type: string
maxItems: 15
default: [ ] default: [ ]
example: [ power, press ] example: [ power, press ]
anonymous: anonymous:
@@ -112,6 +117,12 @@ paths:
format: uuid format: uuid
versionNumber: versionNumber:
type: integer type: integer
400:
description: BadReqest
content:
application/json:
schema:
$ref: '#/components/schemas/400'
401: 401:
$ref: '#/components/responses/401' $ref: '#/components/responses/401'
403: 403:

View File

@@ -145,8 +145,8 @@ class `Article routes` : BaseTest() {
"versionId": "09c418b6-63ba-448b-b38b-502b41cd500e", "versionId": "09c418b6-63ba-448b-b38b-502b41cd500e",
"title": "title2", "title": "title2",
"anonymous": false, "anonymous": false,
"content": "content2", "content": "Sed malesuada ante et sem congue, scelerisque feugiat lorem viverra.",
"description": "description2", "description": "Sed vulputate, ligula id porta posuere, sapien lorem mattis arcu, sit amet luctus erat orci sed tellus.",
"tags": [ "tags": [
"green" "green"
] ]
@@ -161,6 +161,7 @@ class `Article routes` : BaseTest() {
} }
@Test @Test
@Tag("Forbidden")
fun `I cannot create an article if I'm not connected`() { fun `I cannot create an article if I'm not connected`() {
withIntegrationApplication { withIntegrationApplication {
`When I send a POST request`("/articles") { `When I send a POST request`("/articles") {
@@ -170,8 +171,8 @@ class `Article routes` : BaseTest() {
"versionId": "e3c7ce42-241c-4caf-9a59-aba4e466440e", "versionId": "e3c7ce42-241c-4caf-9a59-aba4e466440e",
"title": "title2", "title": "title2",
"anonymous": false, "anonymous": false,
"content": "content2", "content": "Sed malesuada ante et sem congue, scelerisque feugiat lorem viverra.",
"description": "description2", "description": "Sed vulputate, ligula id porta posuere, sapien lorem mattis arcu, sit amet luctus erat orci sed tellus.",
"tags": [ "tags": [
"green" "green"
] ]
@@ -185,4 +186,35 @@ class `Article routes` : BaseTest() {
} }
} }
} }
@Test
@Tag("BadRequest")
fun `I cannot create an article with wrong request`() {
withIntegrationApplication {
`Given I have citizen`("John", "Doe")
`When I send a POST request`("/articles", Validate.NONE) {
`authenticated as`("John", "Doe")
`with body`(
"""
{
"versionId": "09c418b6-63ba-448b-b38b-502b41cd500e",
"title": "title2",
"anonymous": false,
"content": "content2",
"description": "description2",
"tags": [
"green"
]
}
"""
)
} `Then the response should be` BadRequest and {
`And the response should not be null`()
`And the response should contain`("$.invalidParams[0].name", ".content")
`And the response should contain`("$.invalidParams[0].reason", "must have at least 50 characters")
`And the response should contain`("$.invalidParams[1].name", ".description")
`And the response should contain`("$.invalidParams[1].reason", "must have at least 50 characters")
}
}
}
} }

View File

@@ -14,6 +14,7 @@ import io.ktor.server.testing.TestApplicationRequest
import io.ktor.server.testing.setBody import io.ktor.server.testing.setBody
enum class Validate(override val bit: Long) : BitMaskI { enum class Validate(override val bit: Long) : BitMaskI {
NONE(0),
REQUEST_BODY(1), REQUEST_BODY(1),
REQUEST_PARAM(2), REQUEST_PARAM(2),
REQUEST_HEADER(4), REQUEST_HEADER(4),