Skip to content
This repository has been archived by the owner on Mar 9, 2024. It is now read-only.

Commit

Permalink
Added whenContains for collection properties
Browse files Browse the repository at this point in the history
  • Loading branch information
cosmin-marginean committed Jan 28, 2024
1 parent 7fd0500 commit 00e1a32
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 36 deletions.
9 changes: 9 additions & 0 deletions src/main/kotlin/io/validk/Validation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ class Validation<T>(
}
}

fun <E> KProperty1<T, Collection<E>>.whenContains(value: E, block: Validation<T>.(T) -> Unit) {
val property = this
dynamicValidations.add {
if (property.get(it).contains(value)) {
block(it)
}
}
}

infix fun <R> KProperty1<T, R?>.ifNotNull(block: Validation<R>.() -> Unit) {
val property = this
dynamicValidations.add {
Expand Down
4 changes: 2 additions & 2 deletions src/main/kotlin/io/validk/constraints.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ fun <R : Number> Validation<R>.maxExclusive(maxValue: Number) = addConstraint("m
it.toDouble() < maxValue.toDouble()
}

fun Validation<String>.notEmpty() = addConstraint("cannot be empty") {
fun Validation<String>.notEmptyString() = addConstraint("cannot be empty") {
it.isNotEmpty()
}

Expand Down Expand Up @@ -48,7 +48,7 @@ fun Validation<String>.matches(pattern: String) = matches(pattern.toRegex())

fun Validation<String>.email() = matches(REGEX_EMAIL) message "must be a valid email"

fun Validation<out Collection<*>>.notEmptyCollection() = addConstraint("cannot be empty") {
fun Validation<out Collection<*>>.notEmpty() = addConstraint("cannot be empty") {
it.isNotEmpty()
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/io/validk/CollectionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import io.kotest.matchers.shouldNotBe
class CollectionTest : StringSpec({

"empty, min/max size" {
Validation<List<String>> { notEmptyCollection() }.validate(emptyList()) shouldNotBe null
Validation<List<String>> { notEmptyCollection() }.validate(listOf("AA")) shouldBe null
Validation<List<String>> { notEmpty() }.validate(emptyList()) shouldNotBe null
Validation<List<String>> { notEmpty() }.validate(listOf("AA")) shouldBe null

Validation<List<String>> { minSize(2) }.validate(listOf("AA")) shouldNotBe null
Validation<List<String>> { minSize(2) }.validate(listOf("AA", "BB")) shouldBe null
Expand Down
2 changes: 1 addition & 1 deletion src/test/kotlin/io/validk/ComplexNestingTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ComplexNestingTest : StringSpec({
Employee::roles each {
Role::name { enum("DIRECTOR", "EMPLOYEE") }
Role::types {
notEmptyCollection()
notEmpty()
}
}
Employee::address {
Expand Down
73 changes: 46 additions & 27 deletions src/test/kotlin/io/validk/ConditionalValidation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import io.kotest.matchers.shouldBe

class ConditionalValidation : StringSpec({

"validate fields based on the value of the value of another field" {
"validate fields based on the value of another field" {
val validation = Validation<Entity> {
Entity::entityType { enum<EntityType>() }

Expand All @@ -17,36 +17,26 @@ class ConditionalValidation : StringSpec({
}

"COMPANY" -> Entity::registeredOffice { minLength(5) }

}
}
}

validation.validate(Entity("PERSON", "", "Passport number 0123456789", 12)) shouldBe errors(
ValidationError(
"age",
"must be at least 18"
)
ValidationError("age", "must be at least 18")
)
validation.validate(Entity("COMPANY", "", "", 12)) shouldBe errors(
ValidationError(
"registeredOffice",
"must be at least 5 characters"
)
ValidationError("registeredOffice", "must be at least 5 characters")
)

validation.validate(Entity("PERSON", "", "Passport number 0123456789", 24)) shouldBe null
validation.validate(Entity("COMPANY", "London", "", 1)) shouldBe null

validation.validate(Entity("NOTHING", "London", "", 1)) shouldBe errors(
ValidationError(
"entityType",
"must be one of: COMPANY, PERSON"
)
ValidationError("entityType", "must be one of: COMPANY, PERSON")
)
}

"validate fields based on the value of the value of another field with whenIs" {
"validate fields based on the value of another field with whenIs" {
val validation = Validation {
Entity::entityType { enum<EntityType>() }

Expand All @@ -61,26 +51,50 @@ class ConditionalValidation : StringSpec({
}

validation.validate(Entity("PERSON", "", "Passport number 0123456789", 12)) shouldBe errors(
ValidationError(
"age",
"must be at least 18"
)
ValidationError("age", "must be at least 18")
)
validation.validate(Entity("COMPANY", "", "", 12)) shouldBe errors(
ValidationError(
"registeredOffice",
"must be at least 5 characters"
)
ValidationError("registeredOffice", "must be at least 5 characters")
)

validation.validate(Entity("PERSON", "", "Passport number 0123456789", 24)) shouldBe null
validation.validate(Entity("COMPANY", "London", "", 1)) shouldBe null

validation.validate(Entity("NOTHING", "London", "", 1)) shouldBe errors(
ValidationError(
"entityType",
"must be one of: COMPANY, PERSON"
)
ValidationError("entityType", "must be one of: COMPANY, PERSON")
)
}


"validate when collection contains" {
val validation = Validation {
EntityWithCollectionField::roles.whenContains("ADMIN") {
EntityWithCollectionField::adminEmail { notBlank() message "Email admin is required when roles contains ADMIN" }
}
}

validation.validate(EntityWithCollectionField(setOf("USER", "HR"), "")) shouldBe null
validation.validate(EntityWithCollectionField(setOf("USER", "ADMIN"), "")) shouldBe errors(
ValidationError("adminEmail", "Email admin is required when roles contains ADMIN")
)
}

"validate when collection contains while also having collection validation" {
val validation = Validation {
EntityWithCollectionField::roles {
notEmpty() message "Roles required"
}
EntityWithCollectionField::roles.whenContains("ADMIN") {
EntityWithCollectionField::adminEmail { notBlank() message "Email admin is required when roles contains ADMIN" }
}
}

validation.validate(EntityWithCollectionField(emptySet(), "")) shouldBe errors(
ValidationError("roles", "Roles required")
)
validation.validate(EntityWithCollectionField(setOf("USER", "HR"), "")) shouldBe null
validation.validate(EntityWithCollectionField(setOf("USER", "ADMIN"), "")) shouldBe errors(
ValidationError("adminEmail", "Email admin is required when roles contains ADMIN")
)
}
}) {
Expand All @@ -92,6 +106,11 @@ class ConditionalValidation : StringSpec({
val age: Int
)

private data class EntityWithCollectionField(
val roles: Set<String>,
val adminEmail: String
)

private enum class EntityType {
COMPANY,
PERSON
Expand Down
6 changes: 3 additions & 3 deletions src/test/kotlin/io/validk/StringConstraintsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import io.kotest.matchers.shouldNotBe
class StringConstraintsTest : StringSpec({

"notEmpty" {
Validation { notEmpty() }.validate("abc") shouldBe null
Validation { notEmpty() }.validate(" ") shouldBe null
Validation { notEmpty() }.validate("") shouldNotBe null
Validation { this.notEmptyString() }.validate("abc") shouldBe null
Validation { this.notEmptyString() }.validate(" ") shouldBe null
Validation { this.notEmptyString() }.validate("") shouldNotBe null
}

"notBlank" {
Expand Down
2 changes: 1 addition & 1 deletion version.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#Sat Apr 22 19:36:33 BST 2023
version = 1.0.1
version = 1.0.2

0 comments on commit 00e1a32

Please sign in to comment.