
io.github.cdimascio.openapi.Validate.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openapi-spring-webflux-validator Show documentation
Show all versions of openapi-spring-webflux-validator Show documentation
Validate webflux functional endpoints given a Swagger v2 specification
The newest version!
package io.github.cdimascio.openapi
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import org.springframework.http.HttpStatus
import org.springframework.web.reactive.function.BodyExtractors
import org.springframework.web.reactive.function.server.ServerRequest
import org.springframework.web.reactive.function.server.ServerResponse
import org.springframework.web.reactive.function.server.awaitBodyOrNull
import reactor.core.publisher.Mono
operator fun Regex.contains(text: CharSequence): Boolean = this.matches(text)
/**
* Represents an error when validating a request against the
* Swagger 2 or OpenApi 3 specification
*/
data class ValidationError(val request: ServerRequest, val code: Int, val message: String)
/**
* Handler for validation errors
*/
typealias ErrorHandler = (request: ServerRequest, status: HttpStatus, List) -> T
/**
* Factory for ObjectMapper.
*/
typealias ObjectMapperFactory = () -> ObjectMapper
/**
* Validates requests against a Swagger 2 or OpenAPI 3 specification.
*/
class Validate internal constructor(
swaggerJsonPath: String,
errorHandler: ErrorHandler,
private val objectMapperFactory: ObjectMapperFactory) {
/**
* The validate instance
*/
companion object Instance {
private val defaultErrorHandler: ErrorHandler =
{ request, status, messages -> ValidationError(request, status.value(), messages[0]) }
private val defaultObjectMapperFactory: ObjectMapperFactory = { jacksonObjectMapper() }
/**
* Configures the Validator with the Swagger 2 or OpenApi specification located at [openApiSwaggerPath]
* The Swagger 2 or OpenApi 3 specification file may be represented as YAML or JSON.
*/
fun configure(openApiSwaggerPath: String) = configure(openApiSwaggerPath, defaultErrorHandler)
/**
* Configures the Validator with the Swagger 2 or OpenApi specification located at [openApiSwaggerPath]
* and a custom [errorHandler]. The specification file may be represented as YAML or JSON.
*/
fun configure(openApiSwaggerPath: String,
errorHandler: ErrorHandler) = configure(openApiSwaggerPath, defaultObjectMapperFactory, errorHandler)
/**
* Configures the Validator with the Swagger 2 or OpenApi specification located at [openApiSwaggerPath], using
* custom [objectMapperFactory] and a custom [errorHandler]. The specification file may be represented as YAML or JSON.
*/
fun configure(openApiSwaggerPath: String,
objectMapperFactory: ObjectMapperFactory,
errorHandler: ErrorHandler) = Validate(openApiSwaggerPath, errorHandler, objectMapperFactory)
}
private val validator = Validator(swaggerJsonPath, errorHandler)
/**
* The [request] to validate
*/
fun request(request: ServerRequest) = Request(request, objectMapperFactory)
/**
* Validates the [request]. If validation succeeds, the [handler] function is called to return a response
*/
fun request(request: ServerRequest, handler: () -> Mono) = validator.validate(request) ?: handler()
/**
* Validates the [request]. If validation succeeds, the [handler] function is called to return a response.
* It's a suspended alternative to a [request] method.
*/
suspend fun requestAndAwait(request: ServerRequest, handler: suspend () -> ServerResponse): ServerResponse =
validator.validateAndAwait(request) ?: handler()
inner class Request(val request: ServerRequest, val objectMapperFactory: ObjectMapperFactory) {
/**
* Validates a request with body of type [bodyType]. If validation succeeds, the [handler]
* is called to return a response
*/
fun withBody(bodyType: Class, handler: (T) -> Mono): Mono {
return withBody(bodyType, readJsonValue(bodyType), handler)
}
/**
* Validates a request with body of type [bodyType]. If validation succeeds, the [readValue] function
* is used to transform the string body to [bodyType], and the [handler]
* is called to return a response
*/
fun withBody(bodyType: Class,
readValue: (String) -> T,
handler: (T) -> Mono): Mono {
return BodyValidator(request, bodyType, objectMapperFactory).validate(handler, readValue)
}
/**
* Reified inline version of the [Request.withBody] function.
* @param T type of the body.
* @param handler handler function.
* @return ServerResponse as a result of the call.
*/
inline fun withBody(noinline handler: (T) -> Mono): Mono =
this.withBody(T::class.java, handler = handler)
/**
* Validates a request with body of type [bodyType] . If validation succeeds, the [handler]
* is called to return a response.
* It's a suspended alternative to a [withBody] method.
*/
suspend fun awaitBody(bodyType: Class,
readValue: (String) -> T = readJsonValue(bodyType),
handler: suspend (T) -> ServerResponse): ServerResponse {
return BodyValidator(request, bodyType, objectMapperFactory).validateAndAwait(handler, readValue)
}
private fun readJsonValue(bodyType: Class): (String) -> T = { json ->
objectMapperFactory().readValue(json, bodyType)
}
}
/**
* Creates a new BodyValidator to validate a [request] of type [bodyType] using [objectMapperFactory].
*/
inner class BodyValidator(val request: ServerRequest, val bodyType: Class, val objectMapperFactory: ObjectMapperFactory) {
/**
* Validates the body and calls [handler] if the validation succeeds
*/
fun validate(handler: (T) -> Mono, readValue: (String) -> T): Mono {
val json = request.body(BodyExtractors.toMono(String::class.java)).switchIfEmpty(Mono.just(""))
return json.flatMap { validator.validate(request, it) ?: handler(readValue(it)) }
}
/**
* Validates the body and calls [handler] if the validation succeeds.
* It's a suspended alternative to a [validate] method.
*/
suspend fun validateAndAwait(handler: suspend (T) -> ServerResponse, readValue: (String) -> T): ServerResponse {
val json = request.awaitBodyOrNull() ?: ""
return json.let { validator.validateAndAwait(request, it) ?: handler(readValue(it)) }
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy