Validation

value class Validation<D, T, M>(validate: (Inspector<D>, T) -> List<M>)

Encapsulates the logic for validating a given data-model with metadata.

The validation logic itself is expressed by a function that must be passed as validate parameter. This function takes the actual model-data D as well as the metadata T in order to create a List of validation messages M. This value class simply wraps the provided validate function in order to make it invocable without any ceremony.

It appears to be a good practice to put the implementation of the passed validate function right next to your data classes in the commonMain section of your Kotlin multiplatform project. This way you can write the validation logic once and use them on the JS and JVM side.

For example:

data class Person(val name: String, val birthday: LocalDate) {
companion object {
// define validator inside of its corresponding domain type
val validate: Validator<Person, LocalDate, SomeMessage> = validation { inspector, today ->
inspector.map(Person.name()).let { nameInspector ->
if(nameInspector.data.isBlank())
add(SomeMessage(nameInspector.path, "Name must not be blank"))
}
inspector.map(Person.birthday()).let { birthdayInspector ->
if(birthdayInspector.data today)
add(SomeMessage(birthdayInspector, path, "Birthday must not be in the future"))
}
}
}
}

You can also compose validators:

data class Person(val name: String, val birthday: LocalDate) {
// take from example above!
}

data class User(val nickname: String, val person: Person) {
data class UserMetaData(val nicknameRepo: NicknameRepository, val today: LocalDate)

companion object {
val validate: Validator<User, UserMetaData, SomeMessage> = validation { inspector, meta ->
inspector.map(User.nickname()).let { nicknameInspector ->
if(meta.nicknameRepo.exists(nicknameInspector.data))
add(SomeMessage(nicknameInspector.path, "Nickname is already in use"))
}
// use validator of `Person` type by just calling the validator and passing the mapped inspector
// and of course the appropriate meta-data!
addAll(Person.validate(inspector.map(User.person()), meta.today))
}
}
}

Parameters

D

data-model to validate

T

metadata which perhaps is needed in validation process

Constructors

Link copied to clipboard
constructor(validate: (Inspector<D>, T) -> List<M>)

Functions

Link copied to clipboard
operator fun invoke(data: D, metadata: T): List<M>
operator fun invoke(inspector: Inspector<D>, metadata: T): List<M>
Link copied to clipboard
operator fun <D, M> Validation<D, Unit, M>.invoke(data: D): List<M>
operator fun <D, M> Validation<D, Unit, M>.invoke(inspector: Inspector<D>): List<M>

Convenience execution function for Validation for the case, that the metadata is Unit. In those cases the metadata parameter can be omitted.