Skip to content

Commit

Permalink
adds dynamicPath to Const, False, MaxLength, PropertyNames, If-Then-E…
Browse files Browse the repository at this point in the history
…lse schema validation failures
  • Loading branch information
erosb committed Dec 8, 2024
1 parent cd1f033 commit 25ed6a5
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 19 deletions.
3 changes: 2 additions & 1 deletion src/main/kotlin/com/github/erosb/jsonsKema/Const.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ internal val constLoader: KeywordLoader = { ctx -> ConstSchema(ctx.keywordValue,

data class ConstValidationFailure(
override val schema: ConstSchema,
override val instance: IJsonValue
override val instance: IJsonValue,
val dynamicPath: JsonPointer
) : ValidationFailure(
"actual instance is not the same as expected constant value",
schema,
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/com/github/erosb/jsonsKema/False.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ data class FalseSchema(override val location: SourceLocation) : Schema(location)

data class FalseValidationFailure(
override val schema: FalseSchema,
override val instance: IJsonValue
override val instance: IJsonValue,
val dynamicPath: JsonPointer
) : ValidationFailure("false schema always fails", schema, instance, Keyword.FALSE)

3 changes: 2 additions & 1 deletion src/main/kotlin/com/github/erosb/jsonsKema/MaxLength.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ internal val maxLengthLoader: KeywordLoader = { ctx ->

data class MaxLengthValidationFailure(
override val schema: MaxLengthSchema,
override val instance: IJsonString
override val instance: IJsonString,
val dynamicPath: JsonPointer
) : ValidationFailure(
"actual string length ${instance.value.length} exceeds maxLength ${schema.maxLength}",
schema,
Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/com/github/erosb/jsonsKema/PropertyNames.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ internal val propertyNamesLoader: KeywordLoader = {ctx ->
data class PropertyNamesValidationFailure(
override val schema: PropertyNamesSchema,
override val instance: IJsonObj,
val causesByProperties: Map<IJsonString, ValidationFailure>
val causesByProperties: Map<IJsonString, ValidationFailure>,
val dynamicPath: JsonPointer
) : ValidationFailure(
message = "",
schema = schema,
Expand Down
33 changes: 19 additions & 14 deletions src/main/kotlin/com/github/erosb/jsonsKema/Validator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,12 @@ private class DefaultValidator(
}
}

override fun visitConstSchema(schema: ConstSchema): ValidationFailure? {
override fun visitConstSchema(schema: ConstSchema): ValidationFailure? = inPathSegment(Keyword.CONST) {
val isValid = schema.constant == instance
return if (isValid) {
if (isValid) {
null
} else {
ConstValidationFailure(schema, instance)
ConstValidationFailure(schema, instance, dynamicPath())
}
}

Expand Down Expand Up @@ -342,8 +342,8 @@ private class DefaultValidator(
}
}

override fun visitPropertyNamesSchema(propertyNamesSchema: PropertyNamesSchema): ValidationFailure? {
return instance.maybeObject { obj ->
override fun visitPropertyNamesSchema(propertyNamesSchema: PropertyNamesSchema): ValidationFailure? = inPathSegment(Keyword.PROPERTY_NAMES) {
instance.maybeObject { obj ->
val failures: Map<IJsonString, ValidationFailure> = obj.properties.keys.map {
it to withOtherInstance(it) {
propertyNamesSchema.propertyNamesSchema.accept(this)
Expand All @@ -353,7 +353,7 @@ private class DefaultValidator(
.map { it as Pair<IJsonString, ValidationFailure> }
.toMap()
return@maybeObject if (failures.isNotEmpty()) {
PropertyNamesValidationFailure(schema = propertyNamesSchema, instance = obj, causesByProperties = failures)
PropertyNamesValidationFailure(schema = propertyNamesSchema, instance = obj, causesByProperties = failures, dynamicPath())
} else {
null
}
Expand Down Expand Up @@ -395,11 +395,11 @@ private class DefaultValidator(
endResult
}

override fun visitMaxLengthSchema(schema: MaxLengthSchema): ValidationFailure? {
return instance.maybeString {
override fun visitMaxLengthSchema(schema: MaxLengthSchema): ValidationFailure? = inPathSegment(Keyword.MAX_LENGTH) {
instance.maybeString {
val length = it.value.codePointCount(0, it.value.length)
if (length > schema.maxLength) {
MaxLengthValidationFailure(schema, it)
MaxLengthValidationFailure(schema, it, dynamicPath())
} else {
null
}
Expand Down Expand Up @@ -511,8 +511,9 @@ private class DefaultValidator(
}
}

override fun visitFalseSchema(schema: FalseSchema): ValidationFailure =
FalseValidationFailure(schema, instance)
override fun visitFalseSchema(schema: FalseSchema): ValidationFailure? = inPathSegment(Keyword.FALSE) {
FalseValidationFailure(schema, instance, dynamicPath())
}

override fun visitUniqueItemsSchema(schema: UniqueItemsSchema): ValidationFailure? = inPathSegment(Keyword.UNIQUE_ITEMS) {
if (schema.unique) {
Expand Down Expand Up @@ -631,11 +632,15 @@ private class DefaultValidator(
}

override fun visitIfThenElseSchema(schema: IfThenElseSchema): ValidationFailure? {
val ifFailure = schema.ifSchema.accept(this)
val ifFailure = inPathSegment(Keyword.IF) { schema.ifSchema.accept(this) }
return if (ifFailure == null) {
schema.thenSchema?.accept(this)
inPathSegment(Keyword.THEN) {
schema.thenSchema?.accept(this)
}
} else {
schema.elseSchema?.accept(this)
inPathSegment(Keyword.ELSE) {
schema.elseSchema?.accept(this)
}
}
}

Expand Down
19 changes: 19 additions & 0 deletions src/test/kotlin/com/github/erosb/jsonsKema/IfThenElseSchemaTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,23 @@ class IfThenElseSchemaTest {
val subject = IfThenElseSchema(ifSchema, thenSchema, null, UnknownSource)
assertEquals(listOf(ifSchema, thenSchema), subject.subschemas())
}

@Test
fun `dynamic path for then`() {
val subject = IfThenElseSchema(ifSchema, thenSchema, elseSchema, UnknownSource)

val actual = Validator.forSchema(subject).validate(JsonParser("null")()) as FalseValidationFailure

assertEquals(JsonPointer("then", "false"), actual.dynamicPath)
}


@Test
fun `dynamic path for else`() {
val subject = IfThenElseSchema(FalseSchema(UnknownSource), thenSchema, elseSchema, UnknownSource)

val actual = Validator.forSchema(subject).validate(JsonParser("4")()) as ConstValidationFailure

assertEquals(JsonPointer("else", "const"), actual.dynamicPath)
}
}
23 changes: 23 additions & 0 deletions src/test/kotlin/com/github/erosb/jsonsKema/PropertyNamesTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.github.erosb.jsonsKema

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class PropertyNamesTest {

@Test
fun test() {
val schema = SchemaLoader(JsonParser("""
{
"propertyNames": {"maxLength": 2}
}
""".trimIndent())())()

val actual = Validator.forSchema(schema).validate(JsonParser("""
{"aaa":"x"}
""".trimIndent())()) as PropertyNamesValidationFailure
assertEquals("#/propertyNames", actual.dynamicPath.toString())
val cause = actual.causesByProperties.values.first() as MaxLengthValidationFailure
assertEquals("#/propertyNames/maxLength", cause.dynamicPath.toString())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class ValidationFailureTest {
@Test
fun flattenRecursive() {
val falseSubschema = FalseSchema(UnknownSource)
val falseFailure = FalseValidationFailure(falseSubschema, JsonNull(UnknownSource))
val falseFailure = FalseValidationFailure(falseSubschema, JsonNull(UnknownSource), JsonPointer("false"))
val subject = AggregatingValidationFailure(
CompositeSchema(
subschemas = setOf(
Expand Down

0 comments on commit 25ed6a5

Please sign in to comment.