diff --git a/src/main/kotlin/fr/dcproject/component/article/routes/FindArticles.kt b/src/main/kotlin/fr/dcproject/component/article/routes/FindArticles.kt index 2d8bd9a..44d930d 100644 --- a/src/main/kotlin/fr/dcproject/component/article/routes/FindArticles.kt +++ b/src/main/kotlin/fr/dcproject/component/article/routes/FindArticles.kt @@ -38,7 +38,6 @@ object FindArticles { fun validate() = Validation { ArticlesRequest::page { minimum(1) - maximum(100) } ArticlesRequest::limit { minimum(1) diff --git a/src/main/kotlin/fr/dcproject/component/article/routes/UpsertArticle.kt b/src/main/kotlin/fr/dcproject/component/article/routes/UpsertArticle.kt index 20beaef..57fce8a 100644 --- a/src/main/kotlin/fr/dcproject/component/article/routes/UpsertArticle.kt +++ b/src/main/kotlin/fr/dcproject/component/article/routes/UpsertArticle.kt @@ -1,5 +1,6 @@ package fr.dcproject.component.article.routes +import fr.dcproject.application.http.badRequestIfNotValid import fr.dcproject.common.security.assert import fr.dcproject.common.utils.receiveOrBadRequest 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.Publisher 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.call import io.ktor.locations.KtorExperimentalLocationsAPI @@ -35,11 +41,31 @@ object UpsertArticle { val draft: Boolean = false, val versionId: UUID, val workgroup: WorkgroupRef? = null, - ) + ) { + fun validate() = Validation { + 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) { suspend fun ApplicationCall.convertRequestToEntity(): ArticleForUpdate = receiveOrBadRequest().run { + validate().badRequestIfNotValid() ArticleForUpdate( id = id ?: UUID.randomUUID(), title = title, diff --git a/src/main/resources/openapi.yaml b/src/main/resources/openapi.yaml index 4d8d410..17d5216 100644 --- a/src/main/resources/openapi.yaml +++ b/src/main/resources/openapi.yaml @@ -71,16 +71,21 @@ paths: Limit power of press content: type: string + minLength: 50 + maxLength: 6000 example: Lorem upsum... description: type: string + minLength: 50 + maxLength: 6000 example: I think is the bether choice tags: type: array items: type: string + maxItems: 15 default: [ ] example: [ power, press ] anonymous: @@ -112,6 +117,12 @@ paths: format: uuid versionNumber: type: integer + 400: + description: BadReqest + content: + application/json: + schema: + $ref: '#/components/schemas/400' 401: $ref: '#/components/responses/401' 403: diff --git a/src/test/kotlin/integration/Article routes.kt b/src/test/kotlin/integration/Article routes.kt index 5b12bf0..fa0cc82 100644 --- a/src/test/kotlin/integration/Article routes.kt +++ b/src/test/kotlin/integration/Article routes.kt @@ -145,8 +145,8 @@ class `Article routes` : BaseTest() { "versionId": "09c418b6-63ba-448b-b38b-502b41cd500e", "title": "title2", "anonymous": false, - "content": "content2", - "description": "description2", + "content": "Sed malesuada ante et sem congue, scelerisque feugiat lorem viverra.", + "description": "Sed vulputate, ligula id porta posuere, sapien lorem mattis arcu, sit amet luctus erat orci sed tellus.", "tags": [ "green" ] @@ -161,6 +161,7 @@ class `Article routes` : BaseTest() { } @Test + @Tag("Forbidden") fun `I cannot create an article if I'm not connected`() { withIntegrationApplication { `When I send a POST request`("/articles") { @@ -170,8 +171,8 @@ class `Article routes` : BaseTest() { "versionId": "e3c7ce42-241c-4caf-9a59-aba4e466440e", "title": "title2", "anonymous": false, - "content": "content2", - "description": "description2", + "content": "Sed malesuada ante et sem congue, scelerisque feugiat lorem viverra.", + "description": "Sed vulputate, ligula id porta posuere, sapien lorem mattis arcu, sit amet luctus erat orci sed tellus.", "tags": [ "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") + } + } + } } diff --git a/src/test/kotlin/integration/steps/when/request.kt b/src/test/kotlin/integration/steps/when/request.kt index a64a448..ca428c8 100644 --- a/src/test/kotlin/integration/steps/when/request.kt +++ b/src/test/kotlin/integration/steps/when/request.kt @@ -14,6 +14,7 @@ import io.ktor.server.testing.TestApplicationRequest import io.ktor.server.testing.setBody enum class Validate(override val bit: Long) : BitMaskI { + NONE(0), REQUEST_BODY(1), REQUEST_PARAM(2), REQUEST_HEADER(4),