Skip to content

Commit 3bdf9d2

Browse files
author
Oleg
committed
Add allOf assertion
1 parent 0dbc3c1 commit 3bdf9d2

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

src/commonMain/kotlin/smirnov/oleg/json/schema/internal/SchemaLoader.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import smirnov.oleg.json.schema.internal.factories.array.ItemsAssertionFactory
1313
import smirnov.oleg.json.schema.internal.factories.array.MaxItemsAssertionFactory
1414
import smirnov.oleg.json.schema.internal.factories.array.MinItemsAssertionFactory
1515
import smirnov.oleg.json.schema.internal.factories.array.UniqueItemsAssertionFactory
16+
import smirnov.oleg.json.schema.internal.factories.condition.AllOfAssertionFactory
1617
import smirnov.oleg.json.schema.internal.factories.condition.IfThenElseAssertionFactory
1718
import smirnov.oleg.json.schema.internal.factories.general.ConstAssertionFactory
1819
import smirnov.oleg.json.schema.internal.factories.general.EnumAssertionFactory
@@ -56,6 +57,7 @@ private val factories: List<AssertionFactory> = listOf(
5657
PropertyNamesAssertionFactory,
5758
DependenciesAssertionFactory,
5859
IfThenElseAssertionFactory,
60+
AllOfAssertionFactory,
5961
)
6062

6163
class SchemaLoader {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package smirnov.oleg.json.schema.internal.factories.condition
2+
3+
import kotlinx.serialization.json.JsonArray
4+
import kotlinx.serialization.json.JsonElement
5+
import smirnov.oleg.json.schema.ErrorCollector
6+
import smirnov.oleg.json.schema.internal.AssertionContext
7+
import smirnov.oleg.json.schema.internal.JsonSchemaAssertion
8+
import smirnov.oleg.json.schema.internal.LoadingContext
9+
import smirnov.oleg.json.schema.internal.factories.AbstractAssertionFactory
10+
11+
@Suppress("unused")
12+
internal object AllOfAssertionFactory : AbstractAssertionFactory("allOf") {
13+
override fun createFromProperty(element: JsonElement, context: LoadingContext): JsonSchemaAssertion {
14+
require(element is JsonArray) { "$property must be an array" }
15+
require(element.isNotEmpty()) { "$property must have at least one element" }
16+
require(element.all(context::isJsonSchema)) { "each element in $property must be a valid JSON schema" }
17+
18+
val assertions: List<JsonSchemaAssertion> = element.mapIndexed { index, item ->
19+
context.at(index).schemaFrom(item)
20+
}
21+
22+
return AllOfAssertion(assertions)
23+
}
24+
}
25+
26+
private class AllOfAssertion(
27+
private val assertions: List<JsonSchemaAssertion>,
28+
) : JsonSchemaAssertion {
29+
override fun validate(element: JsonElement, context: AssertionContext, errorCollector: ErrorCollector): Boolean {
30+
var valid = true
31+
32+
assertions.forEach {
33+
val res = it.validate(element, context, errorCollector)
34+
valid = valid and res
35+
}
36+
37+
return valid
38+
}
39+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package smirnov.oleg.json.schema.assertions.condition
2+
3+
import io.kotest.assertions.asClue
4+
import io.kotest.core.spec.style.FunSpec
5+
import io.kotest.matchers.collections.shouldContainExactly
6+
import io.kotest.matchers.collections.shouldHaveSize
7+
import io.kotest.matchers.shouldBe
8+
import kotlinx.serialization.json.JsonPrimitive
9+
import smirnov.oleg.json.pointer.JsonPointer
10+
import smirnov.oleg.json.schema.JsonSchema
11+
import smirnov.oleg.json.schema.KEY
12+
import smirnov.oleg.json.schema.ValidationError
13+
14+
@Suppress("unused")
15+
class JsonSchemaAllOfValidationTest : FunSpec() {
16+
init {
17+
JsonSchema.fromDescription(
18+
"""
19+
{
20+
"${KEY}schema": "http://json-schema.org/draft-07/schema#",
21+
"allOf": [
22+
{
23+
"type": "string"
24+
},
25+
{
26+
"minLength": 3
27+
},
28+
{
29+
"maxLength": 6
30+
}
31+
]
32+
}
33+
""".trimIndent()
34+
).also { schema ->
35+
test("passes all assertions") {
36+
val element = JsonPrimitive("test")
37+
38+
val errors = mutableListOf<ValidationError>()
39+
val valid = schema.validate(element, errors::add)
40+
41+
element.asClue {
42+
valid shouldBe true
43+
errors shouldHaveSize 0
44+
}
45+
}
46+
47+
test("fails first assertion") {
48+
val element = JsonPrimitive(42)
49+
50+
val errors = mutableListOf<ValidationError>()
51+
val valid = schema.validate(element, errors::add)
52+
53+
element.asClue {
54+
valid shouldBe false
55+
errors.shouldContainExactly(
56+
ValidationError(
57+
schemaPath = JsonPointer("/allOf/0/type"),
58+
objectPath = JsonPointer.ROOT,
59+
message = "element is not a string",
60+
)
61+
)
62+
}
63+
}
64+
65+
test("fails second assertion") {
66+
val element = JsonPrimitive("te")
67+
68+
val errors = mutableListOf<ValidationError>()
69+
val valid = schema.validate(element, errors::add)
70+
71+
element.asClue {
72+
valid shouldBe false
73+
errors.shouldContainExactly(
74+
ValidationError(
75+
schemaPath = JsonPointer("/allOf/1/minLength"),
76+
objectPath = JsonPointer.ROOT,
77+
message = "string length (2) must be greater or equal to 3",
78+
)
79+
)
80+
}
81+
}
82+
83+
test("fails last assertion") {
84+
val element = JsonPrimitive("test123")
85+
86+
val errors = mutableListOf<ValidationError>()
87+
val valid = schema.validate(element, errors::add)
88+
89+
element.asClue {
90+
valid shouldBe false
91+
errors.shouldContainExactly(
92+
ValidationError(
93+
schemaPath = JsonPointer("/allOf/2/maxLength"),
94+
objectPath = JsonPointer.ROOT,
95+
message = "string length (7) must be less or equal to 6",
96+
)
97+
)
98+
}
99+
}
100+
}
101+
}
102+
}

0 commit comments

Comments
 (0)