-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cross-field validation #29
Comments
What do you think about using EDIT: this doesn't work as I expected - import java.time.LocalDate
import java.time.LocalDateTime
import io.konform.validation.Validation
data class User(
val validFrom: LocalDateTime,
val validUntil: LocalDateTime?,
) {
companion object {
/** validate two fields on User */
private val validateUserDates = Validation<User> {
addConstraint("before/after") {
it.validUntil?.isAfter(it.validFrom) ?: false
}
}
/** public validator, combining private validators */
val validateUser = Validation<User> {
User::validUntil ifPresent {
run(validateUserDates)
}
}
}
}
fun main() {
val time = LocalDate.of(2020, 1, 1).atStartOfDay()
val userValid = User(time, time.plusDays(10))
println(User.validateUser(userValid))
// Valid(value=User(validFrom=2020-01-01T00:00, validUntil=2020-01-11T00:00))
val userInvalid = User(time, time.plusDays(-10))
println(User.validateUser(userInvalid))
// Invalid(errors=[ValidationError(dataPath=, message=before/after)])
// EDIT: the `ifPresent` didn't work as I expected
val userNoUntil = User(time, null)
println(User.validateUser(userNoUntil))
// Invalid(errors=[ValidationError(dataPath=, message=before/after)])
} The downside is that the error message isn't dynamic. It would be nice to have a |
I'm a little surprised this feature is missing. I keep running into this, and it feels like the most missed feature in good ol' JSR303. Example I have right now: validate a postal code depending on the country. Basically, it should be possible to select validations to apply to an object dynamically from the objects runtime state, not just statically. Assuming custom validations: val validateAddress = Validation<Address> {
Address::countryCode {
validIsoCountryCode()
}
Address::postalCode dynamicValidation { it: Address ->
when (it.countryCode) {
"US" -> validUsPostalCode()
"DE" -> validGermanPostalCode()
else -> // default validation of some kind
}
}
} |
I'm not quite sure, why val validateAddress = Validation<Address> { it: Address ->
Address::countryCode {
validIsoCountryCode()
}
Address::postalCode {
when (it.countryCode) {
"US" -> validUsPostalCode()
"DE" -> validGermanPostalCode()
else -> // default validation of some kind
}
}
} Is there a reason behind building static validators (caching validators by PropKey?)? |
doesn't the following snippet address your scenario?
|
Hey @minisaw I'm not @cerker but the limitation of what you suggested is that the error is associated with the top level value, not the field (i.e. the dataPath will be @nlochschmidt do you have any intention to support cross-field validation in konform? I tried looking through the issues for more context on this feature request.
|
0.7.0 with #86 will improve the situation by making it possible, but it's not quite where I want it yet. val validateAddress = Validation<Address> {
validate("postalCode", { it.countryCode to it.postalCode }) {
val postCodeValidationForCountry = when(it.first) {
"US" -> validateusPostalCode
"DE" -> validGermanPostalCode()
else -> // default validation of some kind
}
validate(countryCode, { it.second }) {
run(postCodeValidationForCountry)
}
}
} |
I believe #171 adequately addresses this. See the new README and tests for examples. Here's how this version solves #29 (comment) Validation<Address> {
Address::postalCode dynamic { address ->
when (address.countryCode) {
"US" -> pattern("[0-9]{5}")
else -> pattern("[A-Z]+")
}
}
} |
Is it possible to validate one field in relation to others?
For example, my user has a validFrom: LocalDateTime and a validUntil: LocalDateTime? field. How can I validate that validUntil is after validFrom?
I started writing a custom validator:
but how can I pass the other value?
The text was updated successfully, but these errors were encountered: