Is there a way I can extract those validate blocks...
# getting-started
g
Is there a way I can extract those validate blocks into a reusable function?
Copy code
fun Application.configureValidator() {
    install(RequestValidation) {
        validate<ClientUserDto> {
            val validationResult = it.validate(it)
            if (validationResult is Valid)
                ValidationResult.Valid
            else
                ValidationResult.Invalid(
                    validationResult.errors.map { error ->  "${error.dataPath} ${error.message}" }
                )
        }

        validate<ClientListDto> {
            val validationResult = it.validate(it)
            if (validationResult is Valid)
                ValidationResult.Valid
            else
                ValidationResult.Invalid(
                    validationResult.errors.map { error ->  "${error.dataPath} ${error.message}" }
                )
        }

        validate<ClientListItemDto> {
            val validationResult = it.validate(it)
            if (validationResult is Valid)
                ValidationResult.Valid
            else
                ValidationResult.Invalid(
                    validationResult.errors.map { error ->  "${error.dataPath} ${error.message}" }
                )
        }
    }
}
r
yes, as long as those
validate
methods are from some interface
eg, my guess as how the rest of your code looks:
Copy code
data class ValidationErrorReason(val dataPath: String, val message: String)
sealed interface MyValidationResult {
    object Valid : MyValidationResult
    data class Invalid(val errors: List<ValidationErrorReason>) : MyValidationResult
}

interface Validatable<T> {
    fun validate(arg: T): MyValidationResult
}

data class ClientUserDto(val name: String) : Validatable<ClientUserDto> {
    override fun validate(arg: ClientUserDto) = TODO()
}

data class ClientListDto(val name: String) : Validatable<ClientListDto> {
    override fun validate(arg: ClientListDto) = TODO()
}

data class ClientListItemDto(val name: String) : Validatable<ClientListItemDto> {
    override fun validate(arg: ClientListItemDto) = TODO()
}
special attention to the interface:
Copy code
interface Validatable<T> {
    fun validate(arg: T): MyValidationResult
}
now you can write a function:
Copy code
inline fun <reified T : Validatable<T>> RequestValidationConfig.validateValidatable() =
    validate<T> {
        val validationResult = it.validate(it)
        if (validationResult is MyValidationResult.Invalid)
            ValidationResult.Invalid(
                validationResult.errors.map { error -> "${error.dataPath} ${error.message}" }
            )
        else
            ValidationResult.Valid
    }
(I reversed the if, as kotlin did not want to infer that
errors
exists for some reason) and then you write:
Copy code
install(RequestValidation) {
    validateValidatable<ClientUserDto>()
    validateValidatable<ClientListDto>()
    validateValidatable<ClientListItemDto>()
}
in your
configureValidator
function
why this works:
install(RequestValidation)
requires a lambda of type
RequestValidationConfig.() -> Unit
We can take advantage of that receiver so we can call
validate
on it and write
Copy code
fun RequestValidationConfig.validateValidatable() =
    validate<...> { }
and since you want to do it for various types, you need reified generics (as
validate
needs them) and you bind them to some type so you can call your method
it.validate(it)
(yes, my method & interface names are stupid, please come up with better ones...) Also unsure why exactly do you have
it.validate(it)
method? Shouldn't it be just
it.validate()
?
g
Yes it should just be it.validate