Valider les resource entrente #91

Merged
flecomte merged 46 commits from 21-valid-input into master 2021-04-16 03:27:11 +02:00
4 changed files with 52 additions and 43 deletions
Showing only changes of commit 2ef9f65f2c - Show all commits

View File

@@ -0,0 +1,35 @@
package fr.dcproject.application.http
import fr.dcproject.application.http.HttpErrorBadRequest.InvalidParam
import io.konform.validation.ValidationResult
import io.ktor.http.HttpStatusCode
class BadRequestException(val httpError: HttpErrorBadRequest) : Exception()
class HttpErrorBadRequest(
statusCode: HttpStatusCode,
val title: String = statusCode.description,
val invalidParams: List<InvalidParam>,
) {
val statusCode: Int = statusCode.value
data class InvalidParam(
val name: String,
val reason: String
)
}
fun ValidationResult<*>.toOutput() = HttpErrorBadRequest(
HttpStatusCode.BadRequest,
invalidParams = this.errors.map {
InvalidParam(
it.dataPath,
it.message
)
}
)
fun ValidationResult<*>.badRequestIfNotValid() {
if (errors.size > 0) {
throw BadRequestException(toOutput())
}
}

View File

@@ -4,14 +4,11 @@ import com.github.jasync.sql.db.postgresql.exceptions.GenericDatabaseException
import fr.dcproject.common.security.AccessDeniedException import fr.dcproject.common.security.AccessDeniedException
import fr.dcproject.component.auth.ForbiddenException import fr.dcproject.component.auth.ForbiddenException
import fr.dcproject.component.auth.user import fr.dcproject.component.auth.user
import io.konform.validation.ValidationResult
import io.ktor.application.ApplicationCall
import io.ktor.application.call import io.ktor.application.call
import io.ktor.features.NotFoundException import io.ktor.features.NotFoundException
import io.ktor.features.StatusPages import io.ktor.features.StatusPages
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.response.respond import io.ktor.response.respond
import io.ktor.util.pipeline.PipelineContext
import java.util.concurrent.CompletionException import java.util.concurrent.CompletionException
class HttpError( class HttpError(
@@ -20,8 +17,6 @@ class HttpError(
val type: String? = null, val type: String? = null,
val title: String = cause?.message ?: statusCode.description, val title: String = cause?.message ?: statusCode.description,
val detail: String? = null, val detail: String? = null,
val invalidParams: List<InvalidParam>? = null,
val stackTrace: String? = cause?.stackTraceToString()
) { ) {
val statusCode: Int = statusCode.value val statusCode: Int = statusCode.value
data class InvalidParam( data class InvalidParam(
@@ -30,30 +25,6 @@ class HttpError(
) )
} }
fun ValidationResult<*>.toOutput(): HttpError {
return HttpError(
HttpStatusCode.BadRequest,
invalidParams = this.errors.map {
HttpError.InvalidParam(
it.dataPath,
it.message
)
}
)
}
suspend fun PipelineContext<*, ApplicationCall>.respondIfNotValid(validationResult: ValidationResult<*>): HttpError? {
if (validationResult.errors.size > 0) {
val out = validationResult.toOutput()
this.call.respond(
HttpStatusCode.BadRequest,
out
)
return out
}
return null
}
fun statusPagesInstallation(): StatusPages.Configuration.() -> Unit = { fun statusPagesInstallation(): StatusPages.Configuration.() -> Unit = {
exception<CompletionException> { e -> exception<CompletionException> { e ->
val parent = e.cause?.cause val parent = e.cause?.cause
@@ -106,4 +77,7 @@ fun statusPagesInstallation(): StatusPages.Configuration.() -> Unit = {
call.respond(HttpStatusCode.Forbidden, it) call.respond(HttpStatusCode.Forbidden, it)
} }
} }
exception<BadRequestException> { e ->
call.respond(HttpStatusCode.BadRequest, e.httpError)
}
} }

View File

@@ -1,6 +1,6 @@
package fr.dcproject.component.article.routes package fr.dcproject.component.article.routes
import fr.dcproject.application.http.respondIfNotValid 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.common.validation.isUuid import fr.dcproject.common.validation.isUuid
@@ -74,7 +74,7 @@ object FindArticles {
fun Route.findArticles(repo: ArticleRepository, ac: ArticleAccessControl) { fun Route.findArticles(repo: ArticleRepository, ac: ArticleAccessControl) {
get<ArticlesRequest> { get<ArticlesRequest> {
respondIfNotValid(it.validate())?.apply { return@get } it.validate().badRequestIfNotValid()
repo.findArticles(it) repo.findArticles(it)
.apply { ac.assert { canView(result, citizenOrNull) } } .apply { ac.assert { canView(result, citizenOrNull) } }

View File

@@ -134,6 +134,12 @@ paths:
tags: tags:
- article - article
operationId: getArticle operationId: getArticle
parameters:
- $ref: '#/components/parameters/page'
- $ref: '#/components/parameters/limit'
- $ref: '#/components/parameters/sort'
- $ref: '#/components/parameters/direction'
- $ref: '#/components/parameters/search'
responses: responses:
200: 200:
description: The Article objects description: The Article objects
@@ -149,6 +155,12 @@ paths:
tags: tags:
- article - article
operationId: getArticleVersions operationId: getArticleVersions
parameters:
- $ref: '#/components/parameters/page'
- $ref: '#/components/parameters/limit'
- $ref: '#/components/parameters/sort'
- $ref: '#/components/parameters/direction'
- $ref: '#/components/parameters/search'
responses: responses:
200: 200:
description: The versions of Article description: The versions of Article
@@ -2224,20 +2236,8 @@ components:
properties: properties:
statusCode: statusCode:
type: integer type: integer
type:
type: string
nullable: true
title: title:
type: string type: string
detail:
type: string
nullable: true
cause:
type: string
nullable: true
stackTrace:
type: string
nullable: true
invalidParams: invalidParams:
type: array type: array
items: items: