validata

Kotlin validation library


Download

Validata

This version of the document is for version 0.1+.

Key points

  • Type-safe DSL
  • Zero dependencies
  • Highly customizable

Quickstart

Intall

Gradle

Add the Validata bintray repository to your build.gradle

repositories {
    maven {
        url "https://dl.bintray.com/njanma/validata"
    }
}

Add dependency

compile "io.validata:validata-core:0.1.0"

Maven

Add the Validata bintray repository to your Maven settings

<repository>
    <id>bintray-njanma-validata</id>
    <name>bintray</name>
    <url>https://dl.bintray.com/njanma/validata</url>
</repository>

Add dependency

<dependency>
    <groupId>io.validata</groupId>
    <artifactId>validata-core</artifactId>
    <version>0.1.0</version>
</dependency>

Examples

This section contains a few examples.

Suppose you have a complex data class:

data class UserProfile(val id: Long,
                       val name: String,
                       val age: Int?,
                       val email: String,
                       val registration: RegistrationData)
                       
enum class Device {
    PC, MOBILE, TV
}

data class RegistrationData(val issued: Instant, val device: Device, val deviceUUID: UUID)                       

and service for working with him:

class Service {
    fun checkUserProfile(userProfile: UserProfile): Boolean = true
    fun checkAge(age: Int): Boolean = age >= 18
    fun checkName(name: String): Boolean = true
    fun checkDevice(device: Device): Boolean = true
    fun checkUUID(uuid: UUID): Boolean = true
    fun findByEmail(email: String): UserProfile? =
            UserProfile(1, "realGena", 3, "gena999@mail.com",
                    RegistrationData(LocalDateTime.now().minusDays(1).toInstant(ZoneOffset.UTC), Device.MOBILE, UUID.randomUUID()))

    fun findByDeviceUUID(uuid: UUID): UserProfile? = null
    fun findRegistrationData(email: String): RegistrationData =
            RegistrationData(LocalDateTime.now().minusDays(1).toInstant(ZoneOffset.UTC), Device.PC, UUID.randomUUID())
}

Using Validata you can quickly write some validation rules, for example:

val service = Service()

val profileEmailValidator = Validator<UserProfile, String> {
        UserProfile::email {
            notBlank("email should be not blank")
            minLength(99)
            maxLength(200)
        }
        UserProfile::registration {
            service::checkDevice shouldBe false
        }
        UserProfile::age ifPresent {
            moreThan(18)
        }
}

and apply it to your data

val userProfile = UserProfile(1, "gena", 2, "incorrectEmail.com", RegistrationData(Instant.EPOCH, Device.TV, UUID.randomUUID()))
val validated: ValidationResult<String> = userProfileValidation(userProfile)

ValidationResult can be one of these types: Valid or Invalid<E>. Invalid<E> result has a sequence of errors that were defined earlier.

If present

val userProfileValidation = Validator<UserProfile, String> {
    UserProfile::age ifPresent {
        moreThan(18, "age should be more than 18")
    }
}

Without ifPresent it won’t be compile, because the field age can be nullable.

Checked by

val userRegistrationValidator = Validator<RegistrationData, String> {
    RegistrationData::device {
        service::checkDevice shouldBe false
    }
}
val userProfileValidation = Validator<UserProfile, String> {
    UserProfile::email{
        service::findRegistrationData checkedBy userRegistrationValidator
    }
}    

Matcher

val profileEmailValidator = Validator<UserProfile, String> {
    UserProfile::email should hasLength(1)
}    

Custom matcher

val haveRealRegistrationDate = Matcher<RegistrationData, String> {
    MatcherResult(it.issued.isBefore(Instant.now()), "issued date should be in past!", "issued date should be in future")
}

val userProfileValidation = Validator<UserProfile, String> {
    UserProfile::email{
        service::findRegistrationData shouldNot haveRealRegistrationDate
    }
}

Complex example

val haveRealRegistrationDate = Matcher<RegistrationData, String> {
    MatcherResult(it.issued.isBefore(Instant.now()), "issued date should be in past!", "issued date should be in future")
}
val userRegistrationValidator = Validator<RegistrationData, String> {
    RegistrationData::device {
        service::checkDevice.shouldBe(false, { "should be false but was returned: $it" })
    }
}
val userCheckMsg = "user check invalid"
val profileEmailValidator = Validator<UserProfile, String> {
    UserProfile::email{
        notBlank("email should be not blank")
        minLength(99, "email should has length more than 999", "email should has length less than 999")
        maxLength(200)
    }
    UserProfile::email should hasLength(1).transform("email should has length 1", "email shouldn't has length 1")
    UserProfile::age.shouldNotBe(3, { userCheckMsg }) {
        println("user check invalid")

    }
    UserProfile::email.checkedIf(true) {
        minLength(999, "error on checkedIf", "")
    }
}

val userProfileValidation = Validator<UserProfile, String> {
    UserProfile::email{
        service::findRegistrationData checkedBy userRegistrationValidator
        service::findRegistrationData shouldNot haveRealRegistrationDate
        service::findByEmail ifPresent {
            checkedBy(profileEmailValidator)
        }
    }

    UserProfile::age ifPresent {
        moreThan(18, "age should be more than 18")
        service::checkAge.shouldBe(true, { "age should be > 18" })
    }
    UserProfile::registration should haveRealRegistrationDate
    UserProfile::registration checkedBy Validator {
        RegistrationData::deviceUUID {
            service::checkUUID.shouldBe(false, "name not allowed")
        }
    }
}

val userProfile = UserProfile(1, "gena", 2, "test@mail.com", RegistrationData(Instant.EPOCH, Device.TV, UUID.randomUUID()))
val validated: ValidationResult<String> = userProfileValidation(userProfile)