io.javalin.validation.BaseValidator.kt Maven / Gradle / Ivy
/*
* Javalin - https://javalin.io
* Copyright 2017 David Åse
* Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE
*/
package io.javalin.validation
import io.javalin.json.JsonMapper
import io.javalin.util.JavalinLogger
import io.javalin.util.javalinLazy
import kotlin.LazyThreadSafetyMode.NONE
typealias Check = (T) -> Boolean
data class Rule(val fieldName: String, val check: Check, val error: ValidationError)
data class ValidationError(val message: String, val args: Map = mapOf(), var value: Any? = null)
class ValidationException(val errors: Map>>) : RuntimeException()
data class StringSource(
val stringValue: String?,
val clazz: Class,
val jsonMapper: JsonMapper? = null
)
open class BaseValidator(val fieldName: String, protected var typedValue: T?, protected val stringSource: StringSource?) {
internal val rules = mutableListOf>()
constructor(stringValue: String?, clazz: Class, fieldName: String, jsonMapper: JsonMapper? = null) :
this(fieldName, null, StringSource(stringValue, clazz, jsonMapper))
private val errors by javalinLazy {
if (stringSource != null) {
if (this is BodyValidator) {
try {
typedValue = stringSource.jsonMapper!!.fromJsonString(stringSource.stringValue!!, stringSource.clazz)
} catch (e: Exception) {
JavalinLogger.info("Couldn't deserialize body to ${stringSource.clazz.simpleName}", e)
return@javalinLazy mapOf(REQUEST_BODY to listOf(ValidationError("DESERIALIZATION_FAILED", value = stringSource.stringValue)))
}
} else if (this is NullableValidator || this is Validator) {
try {
typedValue = JavalinValidation.convertValue(stringSource.clazz, stringSource.stringValue)
} catch (e: Exception) {
JavalinLogger.info("Parameter '$fieldName' with value '${stringSource.stringValue}' is not a valid ${stringSource.clazz.simpleName}")
return@javalinLazy mapOf(fieldName to listOf(ValidationError("TYPE_CONVERSION_FAILED", value = stringSource.stringValue)))
}
if (this !is NullableValidator && typedValue == null) { // only check typedValue - null might map to 0, which could be valid?
return@javalinLazy mapOf(fieldName to listOf(ValidationError("NULLCHECK_FAILED", value = stringSource.stringValue)))
}
}
}
/** after this point [typedValue] replaces [stringValue] */
val errors = mutableMapOf>>()
rules.filter { !it.check(typedValue) }.forEach { failedRule ->
// if it's a BodyValidator, the same validator can have rules with different field names
errors.computeIfAbsent(failedRule.fieldName) { mutableListOf() }
errors[failedRule.fieldName]!!.add(failedRule.error.also { it.value = typedValue })
}
errors.mapValues { it.value.toList() }.toMap() // make immutable
}
protected fun addRule(fieldName: String, check: Check, error: String): BaseValidator {
rules.add(Rule(fieldName, check, ValidationError(error)))
return this
}
protected fun addRule(fieldName: String, check: Check, error: ValidationError): BaseValidator {
rules.add(Rule(fieldName, check, error))
return this
}
open fun get(): T? = getOrThrow { ValidationException(it) }
open fun getOrThrow(exceptionFunction: (Map>>) -> Exception): T? = when {
errors.isEmpty() -> typedValue
else -> throw exceptionFunction(errors as Map>>)
}
fun errors(): Map>> = errors
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy