Valider les resource entrente #91
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) } }
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user