konform-kt / konform

Portable validations for Kotlin
https://www.konform.io
MIT License
651 stars 39 forks source link

Make validations context aware #62

Open geertmulders opened 1 year ago

geertmulders commented 1 year ago

Currently when you want to do a database call or check a value against some set of data, your only option is to wrap the "context" in the builder, e.g.

val allowedValues: Set<String> = calculateAllowedValues()
val validation = Validation<String> {
    addConstraint("This value is not allowed!") { allowedValues.contains(it) }
}
validation("wim")

The result is that a validation is tightly coupled to its "context". This is troublesome especially when the "context" is not constant.

Prettier would be:

val validation = Validation<Set<String>, String> {
    addConstraint("This value is not allowed!") { value -> this.contains(value) }
}
val allowedValues: Set<String> = calculateAllowedValues()
validation(allowedValues, "wim")

The changes in PR #61 allow the user to use a piece of context, with almost no breaking changes for those that don't need any context (the special case, with Unit context). The only breaking change is when you define your own extension function on a ValidationBuilder.

fun ValidationBuilder<String>.myOwnConstraintBuilder() = ...
// becomes:
fun ValidationBuilder<Unit, String>.myOwnConstraintBuilder() = ...

It makes it also possible to combine validations with different contexts (see for example test method composeValidationsWithContext):

val addressValidation = Validation<AddressContext, Address> {
    Address::country {
        addConstraint("Country is not allowed") {
            this.validCountries.contains(it)
        }
    }
}

val validation = Validation<Context, Register> {
    Register::home ifPresent {
        run(addressValidation, Context::subContext)
    }
}

Please consider merging this branch.

Nek-12 commented 1 year ago

Just came here looking because of the exact same issue. Want to use another field of the object in my validation