Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce abstraction for validated elements #191

Merged
merged 24 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4c6b815
Inctoduce abstraction for validated elements
OptimumCode Aug 29, 2024
6dce06f
Update api dump
OptimumCode Aug 29, 2024
7d65e89
Move some parts to extension functions
OptimumCode Aug 30, 2024
a131dd3
Reduce number of properties needed for primitive element
OptimumCode Aug 30, 2024
c1b539e
Correct check for different instances
OptimumCode Aug 30, 2024
8390772
Update api dump
OptimumCode Aug 30, 2024
539f36a
Add comments for model classes. Simplify array interface
OptimumCode Aug 30, 2024
118f6f8
Add experimental api annotation for model classes
OptimumCode Aug 30, 2024
16802e4
Add vaildate methods to JsonSchema that accept abstract element
OptimumCode Aug 30, 2024
d8b67cd
Update api dump
OptimumCode Aug 30, 2024
e5cf619
Remove references to JsonElement from format validator
OptimumCode Aug 30, 2024
b1714aa
Add note about collecting elements to set. Use collection instead of …
OptimumCode Aug 30, 2024
46cac3a
Return content property in NumberAssertion error message
OptimumCode Aug 30, 2024
a4aed8e
Fix detekt warnings
OptimumCode Aug 30, 2024
cf9a015
Annotate external assertion classes with experimental annotation
OptimumCode Aug 31, 2024
e57aaf2
Add check that double value is finite for mutlipleOf keyword
OptimumCode Aug 31, 2024
f3339d8
Correct conditions in equality check to prepare for YAML
OptimumCode Aug 31, 2024
37f84a3
Add getValue internal function for ObjectElement
OptimumCode Aug 31, 2024
705cdb8
Revert "Correct conditions in equality check to prepare for YAML"
OptimumCode Aug 31, 2024
fc624b7
Move FIXME comment
OptimumCode Aug 31, 2024
2a19d3e
Add test for json wrappers
OptimumCode Sep 1, 2024
01f8167
Add test for missing key and contains function
OptimumCode Sep 2, 2024
ec174d8
Increment minor version - breaking changes in API
OptimumCode Sep 2, 2024
0646c19
Restore backward compatibility (for kotlin). Add deprecations
OptimumCode Sep 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 29 additions & 2 deletions json-schema-validator/api/json-schema-validator.api
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public abstract interface class io/github/optimumcode/json/schema/FormatValidato
public static final field Companion Lio/github/optimumcode/json/schema/FormatValidator$Companion;
public static fun Invalid ()Lio/github/optimumcode/json/schema/FormatValidationResult;
public static fun Valid ()Lio/github/optimumcode/json/schema/FormatValidationResult;
public abstract fun validate (Lkotlinx/serialization/json/JsonElement;)Lio/github/optimumcode/json/schema/FormatValidationResult;
public abstract fun validate (Lio/github/optimumcode/json/schema/model/AbstractElement;)Lio/github/optimumcode/json/schema/FormatValidationResult;
}

public final class io/github/optimumcode/json/schema/FormatValidator$Companion {
Expand All @@ -108,6 +108,8 @@ public final class io/github/optimumcode/json/schema/JsonSchema {
public static final fun fromDefinition (Ljava/lang/String;Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchema;
public static final fun fromJsonElement (Lkotlinx/serialization/json/JsonElement;)Lio/github/optimumcode/json/schema/JsonSchema;
public static final fun fromJsonElement (Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/SchemaType;)Lio/github/optimumcode/json/schema/JsonSchema;
public final fun validate (Lio/github/optimumcode/json/schema/model/AbstractElement;Lio/github/optimumcode/json/schema/ErrorCollector;)Z
public final fun validate (Lio/github/optimumcode/json/schema/model/AbstractElement;Lio/github/optimumcode/json/schema/OutputCollector$Provider;)Ljava/lang/Object;
public final fun validate (Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/ErrorCollector;)Z
public final fun validate (Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/OutputCollector$Provider;)Ljava/lang/Object;
}
Expand Down Expand Up @@ -337,7 +339,7 @@ public abstract interface class io/github/optimumcode/json/schema/extension/Exte
}

public abstract interface class io/github/optimumcode/json/schema/extension/ExternalAssertion {
public abstract fun validate (Lkotlinx/serialization/json/JsonElement;Lio/github/optimumcode/json/schema/extension/ExternalAssertionContext;Lio/github/optimumcode/json/schema/ErrorCollector;)Z
public abstract fun validate (Lio/github/optimumcode/json/schema/model/AbstractElement;Lio/github/optimumcode/json/schema/extension/ExternalAssertionContext;Lio/github/optimumcode/json/schema/ErrorCollector;)Z
}

public abstract interface class io/github/optimumcode/json/schema/extension/ExternalAssertionContext {
Expand All @@ -354,3 +356,28 @@ public abstract interface class io/github/optimumcode/json/schema/extension/Exte
public abstract fun getSchemaPath ()Lio/github/optimumcode/json/pointer/JsonPointer;
}

public abstract interface class io/github/optimumcode/json/schema/model/AbstractElement {
public abstract fun toString ()Ljava/lang/String;
}

public abstract interface class io/github/optimumcode/json/schema/model/ArrayElement : io/github/optimumcode/json/schema/model/AbstractElement, kotlin/sequences/Sequence {
public abstract fun get (I)Lio/github/optimumcode/json/schema/model/AbstractElement;
public abstract fun getSize ()I
}

public abstract interface class io/github/optimumcode/json/schema/model/ObjectElement : io/github/optimumcode/json/schema/model/AbstractElement, kotlin/sequences/Sequence {
public abstract fun contains (Ljava/lang/String;)Z
public abstract fun get (Ljava/lang/String;)Lio/github/optimumcode/json/schema/model/AbstractElement;
public abstract fun getKeys ()Ljava/util/Set;
public abstract fun getSize ()I
}

public abstract interface class io/github/optimumcode/json/schema/model/PrimitiveElement : io/github/optimumcode/json/schema/model/AbstractElement {
public abstract fun getContent ()Ljava/lang/String;
public abstract fun getNumber ()Ljava/lang/Number;
public abstract fun isBoolean ()Z
public abstract fun isNull ()Z
public abstract fun isNumber ()Z
public abstract fun isString ()Z
}

Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package io.github.optimumcode.json.schema

import kotlinx.serialization.json.JsonElement
import io.github.optimumcode.json.schema.model.AbstractElement
import kotlin.jvm.JvmStatic

/**
* The [FormatValidator] is used to check whether the [JsonElement] matches the expected format.
* If the [JsonElement] is not of the required type (e.g. validator expects string but the [JsonElement] is an object)
* The [FormatValidator] is used to check whether the [AbstractElement] matches the expected format.
* If the [AbstractElement] is not of the required type
* (e.g. validator expects string but the [AbstractElement] is an object)
* the validator **MUST** return [FormatValidator.Valid] result
*/
@ExperimentalApi
Expand All @@ -16,7 +17,7 @@ public interface FormatValidator {
* @param element JSON element to validate against the expected format
* @return the result of the validation
*/
public fun validate(element: JsonElement): FormatValidationResult
public fun validate(element: AbstractElement): FormatValidationResult

public companion object {
@Suppress("ktlint:standard:function-naming", "FunctionName")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import io.github.optimumcode.json.schema.internal.DefaultAssertionContext
import io.github.optimumcode.json.schema.internal.DefaultReferenceResolver
import io.github.optimumcode.json.schema.internal.IsolatedLoader
import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion
import io.github.optimumcode.json.schema.internal.wrapper.wrap
import io.github.optimumcode.json.schema.model.AbstractElement
import kotlinx.serialization.json.JsonElement
import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmStatic
Expand All @@ -28,6 +30,31 @@ public class JsonSchema internal constructor(
public fun validate(
value: JsonElement,
errorCollector: ErrorCollector,
): Boolean = validate(value.wrap(), errorCollector)

/**
* Validates [value] against this [JsonSchema].
* The provided [outputCollectorProvider] will be used to create [OutputCollector]
* which collects the validation result.
*
* @return validation result depending on [outputCollectorProvider]
*/
public fun <T> validate(
value: JsonElement,
outputCollectorProvider: OutputCollector.Provider<T>,
): T = validate(value.wrap(), outputCollectorProvider)

/**
* Validates [value] against this [JsonSchema].
* If the [value] is valid against the schema the function returns `true`.
* Otherwise, it returns `false`.
*
* All reported errors will be reported to [ErrorCollector.onError]
*/
@ExperimentalApi
public fun validate(
value: AbstractElement,
errorCollector: ErrorCollector,
): Boolean {
val context = DefaultAssertionContext(JsonPointer.ROOT, referenceResolver)
return DelegateOutputCollector(errorCollector).use {
Expand All @@ -42,8 +69,9 @@ public class JsonSchema internal constructor(
*
* @return validation result depending on [outputCollectorProvider]
*/
@ExperimentalApi
public fun <T> validate(
value: JsonElement,
value: AbstractElement,
outputCollectorProvider: OutputCollector.Provider<T>,
): T {
val context = DefaultAssertionContext(JsonPointer.ROOT, referenceResolver)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.github.optimumcode.json.schema.extension

import io.github.optimumcode.json.schema.AnnotationKey
import io.github.optimumcode.json.schema.ExperimentalApi

@ExperimentalApi
public interface ExternalAnnotationCollector {
/**
* Adds annotation with provided [key]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package io.github.optimumcode.json.schema.extension

import io.github.optimumcode.json.schema.ErrorCollector
import kotlinx.serialization.json.JsonElement
import io.github.optimumcode.json.schema.ExperimentalApi
import io.github.optimumcode.json.schema.model.AbstractElement

/**
* This interface allows you to implement your own schema assertion.
* This interface **does not** allow implementing custom applicators.
* Only simple assertions (like: _format_, _type_) can be implemented.
*/
@ExperimentalApi
public interface ExternalAssertion {
/**
* Validates passes [element].
Expand All @@ -25,7 +27,7 @@ public interface ExternalAssertion {
* @return `true` if element is valid against assertion. Otherwise, returns `false`
*/
public fun validate(
element: JsonElement,
element: AbstractElement,
context: ExternalAssertionContext,
errorCollector: ErrorCollector,
): Boolean
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.github.optimumcode.json.schema.extension

import io.github.optimumcode.json.pointer.JsonPointer
import io.github.optimumcode.json.schema.ExperimentalApi

@ExperimentalApi
public interface ExternalAssertionContext {
/**
* A JSON pointer to the currently validating element in the object that is being validated
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.github.optimumcode.json.schema.extension

import io.github.optimumcode.json.schema.ExperimentalApi
import kotlinx.serialization.json.JsonElement

@ExperimentalApi
public interface ExternalAssertionFactory {
/**
* A keyword that is associated with the [ExternalAssertion] created by this factory.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package io.github.optimumcode.json.schema.extension

import io.github.optimumcode.json.pointer.JsonPointer
import io.github.optimumcode.json.schema.ExperimentalApi

@ExperimentalApi
public interface ExternalLoadingContext {
/**
* A JSON pointer to the current position in schema associated with currently processing element
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ package io.github.optimumcode.json.schema.internal
import io.github.optimumcode.json.pointer.JsonPointer
import io.github.optimumcode.json.schema.OutputCollector
import io.github.optimumcode.json.schema.ValidationError
import kotlinx.serialization.json.JsonElement
import io.github.optimumcode.json.schema.model.AbstractElement

internal class FalseSchemaAssertion(
private val path: JsonPointer,
) : JsonSchemaAssertion {
override fun validate(
element: JsonElement,
element: AbstractElement,
context: AssertionContext,
errorCollector: OutputCollector<*>,
): Boolean {
Expand All @@ -28,7 +28,7 @@ internal class FalseSchemaAssertion(

internal object TrueSchemaAssertion : JsonSchemaAssertion {
override fun validate(
element: JsonElement,
element: AbstractElement,
context: AssertionContext,
errorCollector: OutputCollector<*>,
): Boolean {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.github.optimumcode.json.schema.internal

import io.github.optimumcode.json.schema.OutputCollector
import kotlinx.serialization.json.JsonElement
import io.github.optimumcode.json.schema.model.AbstractElement

internal interface JsonSchemaAssertion {
/**
Expand All @@ -20,7 +20,7 @@ internal interface JsonSchemaAssertion {
* @return `true` if element is valid against assertion. Otherwise, returns `false`
*/
fun validate(
element: JsonElement,
element: AbstractElement,
context: AssertionContext,
errorCollector: OutputCollector<*>,
): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.github.optimumcode.json.schema.internal
import com.eygraber.uri.Uri
import io.github.optimumcode.json.pointer.JsonPointer
import io.github.optimumcode.json.schema.OutputCollector
import kotlinx.serialization.json.JsonElement
import io.github.optimumcode.json.schema.model.AbstractElement

internal class JsonSchemaRoot(
private val scopeId: Uri,
Expand All @@ -12,7 +12,7 @@ internal class JsonSchemaRoot(
private val canBeReferencedRecursively: Boolean,
) : JsonSchemaAssertion {
override fun validate(
element: JsonElement,
element: AbstractElement,
context: AssertionContext,
errorCollector: OutputCollector<*>,
): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import io.github.optimumcode.json.pointer.plus
import io.github.optimumcode.json.pointer.relative
import io.github.optimumcode.json.schema.AbsoluteLocation
import io.github.optimumcode.json.schema.OutputCollector
import kotlinx.serialization.json.JsonElement
import io.github.optimumcode.json.schema.model.AbstractElement

internal class RecursiveRefSchemaAssertion(
private val basePath: JsonPointer,
private val refId: RefId,
) : JsonSchemaAssertion {
override fun validate(
element: JsonElement,
element: AbstractElement,
context: AssertionContext,
errorCollector: OutputCollector<*>,
): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import io.github.optimumcode.json.pointer.plus
import io.github.optimumcode.json.pointer.relative
import io.github.optimumcode.json.schema.AbsoluteLocation
import io.github.optimumcode.json.schema.OutputCollector
import kotlinx.serialization.json.JsonElement
import io.github.optimumcode.json.schema.model.AbstractElement

internal class RefSchemaAssertion(
private val basePath: JsonPointer,
Expand All @@ -17,7 +17,7 @@ internal class RefSchemaAssertion(
private lateinit var refAbsolutePath: Uri

override fun validate(
element: JsonElement,
element: AbstractElement,
context: AssertionContext,
errorCollector: OutputCollector<*>,
): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import io.github.optimumcode.json.schema.extension.ExternalAssertionFactory
import io.github.optimumcode.json.schema.internal.AssertionContext
import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion
import io.github.optimumcode.json.schema.internal.LoadingContext
import io.github.optimumcode.json.schema.model.AbstractElement
import kotlinx.serialization.json.JsonElement

internal class ExternalAssertionFactoryAdapter(
Expand All @@ -25,7 +26,7 @@ private class ExternalAssertionAdapter(
private val externalAssertion: ExternalAssertion,
) : JsonSchemaAssertion {
override fun validate(
element: JsonElement,
element: AbstractElement,
context: AssertionContext,
errorCollector: OutputCollector<*>,
): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import io.github.optimumcode.json.schema.AnnotationKey
import io.github.optimumcode.json.schema.OutputCollector
import io.github.optimumcode.json.schema.internal.AssertionContext
import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import io.github.optimumcode.json.schema.model.AbstractElement
import io.github.optimumcode.json.schema.model.ArrayElement
import io.github.optimumcode.json.schema.model.lastIndex

internal class AdditionalItemsAssertion(
private val location: JsonPointer,
Expand All @@ -16,12 +17,12 @@ internal class AdditionalItemsAssertion(
private val returnIfNoIndex: Boolean,
) : JsonSchemaAssertion {
override fun validate(
element: JsonElement,
element: AbstractElement,
context: AssertionContext,
errorCollector: OutputCollector<*>,
): Boolean {
return errorCollector.updateKeywordLocation(location).use {
if (element !is JsonArray) {
if (element !is ArrayElement) {
return@use true
}
val lastProcessedIndex: Int =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@ import io.github.optimumcode.json.schema.AnnotationKey
import io.github.optimumcode.json.schema.OutputCollector
import io.github.optimumcode.json.schema.internal.AssertionContext
import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import io.github.optimumcode.json.schema.model.AbstractElement
import io.github.optimumcode.json.schema.model.ArrayElement
import io.github.optimumcode.json.schema.model.lastIndex

internal class AllItemsAssertion(
private val location: JsonPointer,
private val itemAssertion: JsonSchemaAssertion,
private val annotationKey: AnnotationKey<Int>,
) : JsonSchemaAssertion {
override fun validate(
element: JsonElement,
element: AbstractElement,
context: AssertionContext,
errorCollector: OutputCollector<*>,
): Boolean {
return errorCollector.updateKeywordLocation(location).use {
if (element !is JsonArray) {
if (element !is ArrayElement) {
return@use true
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import io.github.optimumcode.json.schema.OutputCollector
import io.github.optimumcode.json.schema.ValidationError
import io.github.optimumcode.json.schema.internal.AssertionContext
import io.github.optimumcode.json.schema.internal.JsonSchemaAssertion
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import io.github.optimumcode.json.schema.model.AbstractElement
import io.github.optimumcode.json.schema.model.ArrayElement

internal class ArrayLengthAssertion(
private val path: JsonPointer,
Expand All @@ -15,12 +15,12 @@ internal class ArrayLengthAssertion(
private val check: (Int, Int) -> Boolean,
) : JsonSchemaAssertion {
override fun validate(
element: JsonElement,
element: AbstractElement,
context: AssertionContext,
errorCollector: OutputCollector<*>,
): Boolean {
return errorCollector.updateKeywordLocation(path).use {
if (element !is JsonArray) {
if (element !is ArrayElement) {
return@use true
}
if (check(element.size, length)) {
Expand Down
Loading
Loading