diff --git a/README.md b/README.md index eea5814c6..5769bcdd8 100644 --- a/README.md +++ b/README.md @@ -60,34 +60,26 @@ In this case this workload is using the Draft 4 specification and largely tests If performance is an important consideration, the specific sample workloads should be benchmarked, as there are different performance characteristics when certain keywords are used. For instance the use of the `unevaluatedProperties` or `unevaluatedItems` keyword will trigger annotation collection in the related validators, such as the `properties` or `items` validators, and annotation collection will adversely affect performance. -##### NetworkNT 1.3.1 +##### NetworkNT 1.4.1 ``` -Benchmark Mode Cnt Score Error Units -NetworkntBenchmark.testValidate thrpt 10 6776.693 ± 115.309 ops/s -NetworkntBenchmark.testValidate:·gc.alloc.rate thrpt 10 971.191 ± 16.420 MB/sec -NetworkntBenchmark.testValidate:·gc.alloc.rate.norm thrpt 10 165318.816 ± 0.459 B/op -NetworkntBenchmark.testValidate:·gc.churn.G1_Eden_Space thrpt 10 968.894 ± 51.234 MB/sec -NetworkntBenchmark.testValidate:·gc.churn.G1_Eden_Space.norm thrpt 10 164933.962 ± 8636.203 B/op -NetworkntBenchmark.testValidate:·gc.churn.G1_Survivor_Space thrpt 10 0.002 ± 0.001 MB/sec -NetworkntBenchmark.testValidate:·gc.churn.G1_Survivor_Space.norm thrpt 10 0.274 ± 0.218 B/op -NetworkntBenchmark.testValidate:·gc.count thrpt 10 89.000 counts -NetworkntBenchmark.testValidate:·gc.time thrpt 10 99.000 ms +Benchmark Mode Cnt Score Error Units +NetworkntBenchmark.testValidate thrpt 10 8352.126 ± 61.870 ops/s +NetworkntBenchmark.testValidate:gc.alloc.rate thrpt 10 721.296 ± 5.342 MB/sec +NetworkntBenchmark.testValidate:gc.alloc.rate.norm thrpt 10 90560.013 ± 0.001 B/op +NetworkntBenchmark.testValidate:gc.count thrpt 10 61.000 counts +NetworkntBenchmark.testValidate:gc.time thrpt 10 68.000 ms ``` ###### Everit 1.14.1 ``` -Benchmark Mode Cnt Score Error Units -EveritBenchmark.testValidate thrpt 10 3719.192 ± 125.592 ops/s -EveritBenchmark.testValidate:·gc.alloc.rate thrpt 10 1448.208 ± 74.746 MB/sec -EveritBenchmark.testValidate:·gc.alloc.rate.norm thrpt 10 449621.927 ± 7400.825 B/op -EveritBenchmark.testValidate:·gc.churn.G1_Eden_Space thrpt 10 1446.397 ± 79.919 MB/sec -EveritBenchmark.testValidate:·gc.churn.G1_Eden_Space.norm thrpt 10 449159.799 ± 18614.931 B/op -EveritBenchmark.testValidate:·gc.churn.G1_Survivor_Space thrpt 10 0.001 ± 0.001 MB/sec -EveritBenchmark.testValidate:·gc.churn.G1_Survivor_Space.norm thrpt 10 0.364 ± 0.391 B/op -EveritBenchmark.testValidate:·gc.count thrpt 10 133.000 counts -EveritBenchmark.testValidate:·gc.time thrpt 10 148.000 ms +Benchmark Mode Cnt Score Error Units +EveritBenchmark.testValidate thrpt 10 3775.453 ± 44.023 ops/s +EveritBenchmark.testValidate:gc.alloc.rate thrpt 10 1667.345 ± 19.437 MB/sec +EveritBenchmark.testValidate:gc.alloc.rate.norm thrpt 10 463104.030 ± 0.003 B/op +EveritBenchmark.testValidate:gc.count thrpt 10 140.000 counts +EveritBenchmark.testValidate:gc.time thrpt 10 158.000 ms ``` #### Functionality @@ -96,7 +88,7 @@ This implementation is tested against the [JSON Schema Test Suite](https://githu | Implementations | Overall | DRAFT_03 | DRAFT_04 | DRAFT_06 | DRAFT_07 | DRAFT_2019_09 | DRAFT_2020_12 | |-----------------|-------------------------------------------------------------------------|-------------------------------------------------------------------|---------------------------------------------------------------------|--------------------------------------------------------------------|------------------------------------------------------------------------|----------------------------------------------------------------------|------------------------------------------------------------------------| -| NetworkNt | pass: r:4703 (100.0%) o:2369 (100.0%)
fail: r:0 (0.0%) o:1 (0.0%) | | pass: r:600 (100.0%) o:251 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:796 (100.0%) o:318 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:880 (100.0%) o:541 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1201 (100.0%) o:625 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1226 (100.0%) o:634 (99.8%)
fail: r:0 (0.0%) o:1 (0.2%) | +| NetworkNt | pass: r:4803 (100.0%) o:2372 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | | pass: r:610 (100.0%) o:251 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:822 (100.0%) o:318 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:906 (100.0%) o:541 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1220 (100.0%) o:625 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1245 (100.0%) o:637 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | * Note that this uses the `JoniRegularExpressionFactory` for the `pattern` and `format` `regex` tests. @@ -230,7 +222,7 @@ This package is available on Maven central. com.networknt json-schema-validator - 1.4.0 + 1.4.1 ``` @@ -238,7 +230,7 @@ This package is available on Maven central. ```java dependencies { - implementation(group: 'com.networknt', name: 'json-schema-validator', version: '1.4.0'); + implementation(group: 'com.networknt', name: 'json-schema-validator', version: '1.4.1'); } ``` @@ -263,13 +255,12 @@ JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag. builder.schemaMappers(schemaMappers -> schemaMappers.mapPrefix("https://www.example.org/", "classpath:schema/")) ); -SchemaValidatorsConfig config = new SchemaValidatorsConfig(); -// By default JSON Path is used for reporting the instance location and evaluation path -config.setPathType(PathType.JSON_POINTER); +SchemaValidatorsConfig.Builder builder = SchemaValidatorsConfig.builder(); // By default the JDK regular expression implementation which is not ECMA 262 compliant is used -// Note that setting this requires including optional depedencies -// config.setRegularExpressionFactory(GraalJSRegularExpressionFactory.getInstance()); -// config.setRegularExpressionFactory(JoniRegularExpressionFactory.getInstance()); +// Note that setting this requires including optional dependencies +// builder.regularExpressionFactory(GraalJSRegularExpressionFactory.getInstance()); +// builder.regularExpressionFactory(JoniRegularExpressionFactory.getInstance()); +SchemaValidatorsConfig config = builder.build(); // Due to the mapping the schema will be retrieved from the classpath at classpath:schema/example-main.json. // If the schema data does not specify an $id the absolute IRI of the schema location will be used as the $id. @@ -299,13 +290,12 @@ Note that the meta-schemas for Draft 4, Draft 6, Draft 7, Draft 2019-09 and Draf ```java JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag.V202012); -SchemaValidatorsConfig config = new SchemaValidatorsConfig(); -// By default JSON Path is used for reporting the instance location and evaluation path -config.setPathType(PathType.JSON_POINTER); +SchemaValidatorsConfig.Builder builder = SchemaValidatorsConfig.builder(); // By default the JDK regular expression implementation which is not ECMA 262 compliant is used -// Note that setting this requires including optional depedencies -// config.setRegularExpressionFactory(GraalJSRegularExpressionFactory.getInstance()); -// config.setRegularExpressionFactory(JoniRegularExpressionFactory.getInstance()); +// Note that setting this requires including optional dependencies +// builder.regularExpressionFactory(GraalJSRegularExpressionFactory.getInstance()); +// builder.regularExpressionFactory(JoniRegularExpressionFactory.getInstance()); +SchemaValidatorsConfig config = builder.build(); // Due to the mapping the meta-schema will be retrieved from the classpath at classpath:draft/2020-12/schema. JsonSchema schema = jsonSchemaFactory.getSchema(SchemaLocation.of(SchemaId.V202012), config); @@ -320,7 +310,7 @@ String input = "{\r\n" + "}"; Set assertions = schema.validate(input, InputFormat.JSON, executionContext -> { // By default since Draft 2019-09 the format keyword only generates annotations and not assertions - executionContext.getConfig().setFormatAssertionsEnabled(true); + executionContext.getExecutionConfig().setFormatAssertionsEnabled(true); }); ``` ### Results and output formats @@ -354,6 +344,7 @@ Assertions contains the following additional information | Property | The property name that caused the validation error for example for the `required` keyword. Note that this is not part of the instance location as that points to the instance node. | Schema Node | The `JsonNode` pointed to by the Schema Location. This is the schema data that caused the input data to fail. It is possible to get the location information by configuring the `JsonSchemaFactory` with a `JsonNodeReader` that uses the `LocationJsonNodeFactoryFactory` and using `JsonNodes.tokenLocationOf(schemaNode)`. | Instance Node | The `JsonNode` pointed to by the Instance Location. This is the input data that failed validation. It is possible to get the location information by configuring the `JsonSchemaFactory` with a `JsonNodeReader` that uses the `LocationJsonNodeFactoryFactory` and using `JsonNodes.tokenLocationOf(instanceNode)`. +| Error | The error. | Details | Additional details that can be set by custom keyword validator implementations. This is not used by the library. Annotations contains the following additional information @@ -383,8 +374,7 @@ String inputData = "{\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.jsonNodeReader(JsonNodeReader.builder().locationAware().build())); -SchemaValidatorsConfig config = new SchemaValidatorsConfig(); -config.setPathType(PathType.JSON_POINTER); +SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, InputFormat.JSON, config); Set messages = schema.validate(inputData, InputFormat.JSON, executionContext -> { executionContext.getExecutionConfig().setFormatAssertionsEnabled(true); @@ -433,9 +423,7 @@ The following example shows how to generate the hierarchical output format with ```java JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); -SchemaValidatorsConfig config = new SchemaValidatorsConfig(); -config.setPathType(PathType.JSON_POINTER); -config.setFormatAssertionsEnabled(true); +SchemaValidatorsConfig config = SchemaValidatorsConfig().builder().formatAssertionsEnabled(true).build(); JsonSchema schema = factory.getSchema(SchemaLocation.of("https://json-schema.org/schemas/example"), config); OutputUnit outputUnit = schema.validate(inputData, InputFormat.JSON, OutputFormat.HIERARCHICAL, executionContext -> { @@ -527,17 +515,27 @@ The following is sample output from the Hierarchical format. | Name | Description | Default Value |---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------- -| `pathType` | The path type to use for reporting the instance location and evaluation path. Set to `PathType.JSON_POINTER` to use JSON Pointer. | `PathType.DEFAULT` +| `applyDefaultsStrategy` | The strategy for applying defaults when walking when missing or null nodes are encountered. | `ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY` +| `cacheRefs` | Whether the schemas loaded from refs will be cached and reused for subsequent runs. Setting this to `false` will affect performance but may be neccessary to prevent high memory usage for the cache if multiple nested applicators like `anyOf`, `oneOf` and `allOf` are used. | `true` +| `discriminatorKeywordEnabled` | Whether the `discriminator` keyword is handled according to OpenAPI 3. | `false` +| `errorMessageKeyword` | The keyword to use for custom error messages in the schema. If not set this features is disabled. This is typically set to `errorMessage` or `message`. | `null` | `executionContextCustomizer` | This can be used to customize the `ExecutionContext` generated by the `JsonSchema` for each validation run. | `null` -| `schemaIdValidator` | This is used to customize how the `$id` values are validated. Note that the default implementation allows non-empty fragments where no base IRI is specified and also allows non-absolute IRI `$id` values in the root schema. | `JsonSchemaIdValidator.DEFAULT` +| `failFast` | Whether to return failure immediately when an assertion is generated. | `false` +| `formatAssertionsEnabled` | The default is to generate format assertions from Draft 4 to Draft 7 and to only generate annotations from Draft 2019-09. Setting to `true` or `false` will override the default behavior. | `null` +| `javaSemantics` | Whether java semantics is used for the `type` keyword. | `false` +| `locale` | The locale to use for generating messages in the `ValidationMessage`. | `Locale.getDefault()` +| `losslessNarrowing` | Whether lossless narrowing is used for the `type` keyword. | `false` | `messageSource` | This is used to retrieve the locale specific messages. | `DefaultMessageSource.getInstance()` +| `nullableKeywordEnabled` | Whether the `nullable` keyword is handled according to OpenAPI 3.0. This affects the `enum` and `type` keywords. | `false` +| `pathType` | The path type to use for reporting the instance location and evaluation path. Set to `PathType.JSON_PATH` to use JSON Path. | `PathType.JSON_POINTER` | `preloadJsonSchema` | Whether the schema will be preloaded before processing any input. This will use memory but the execution of the validation will be faster. | `true` | `preloadJsonSchemaRefMaxNestingDepth` | The max depth of the evaluation path to preload when preloading refs. | `40` -| `cacheRefs` | Whether the schemas loaded from refs will be cached and reused for subsequent runs. Setting this to `false` will affect performance but may be neccessary to prevent high memory usage for the cache if multiple nested applicators like `anyOf`, `oneOf` and `allOf` are used. | `true` -| `locale` | The locale to use for generating messages in the `ValidationMessage`. | `Locale.getDefault()` -| `failFast` | Whether to return failure immediately when an assertion is generated. | `false` -| `formatAssertionsEnabled` | The default is to generate format assertions from Draft 4 to Draft 7 and to only generate annotations from Draft 2019-09. Setting to `true` or `false` will override the default behavior. | `null` +| `readOnly` | Whether schema is read only. This affects the `readOnly` keyword. | `null` | `regularExpressionFactory` | The factory to use to create regular expressions for instance `JoniRegularExpressionFactory` or `GraalJSRegularExpressionFactory`. This requires the dependency to be manually added to the project or a `ClassNotFoundException` will be thrown. | `JDKRegularExpressionFactory.getInstance()` +| `schemaIdValidator` | This is used to customize how the `$id` values are validated. Note that the default implementation allows non-empty fragments where no base IRI is specified and also allows non-absolute IRI `$id` values in the root schema. | `JsonSchemaIdValidator.DEFAULT` +| `strict` | This is set whether keywords are strict in their validation. What this does depends on the individual validators. | +| `typeLoose` | Whether types are interpreted in a loose manner. If set to true, a single value can be interpreted as a size 1 array. Strings may also be interpreted as number, integer or boolean. | `false` +| `writeOnly` | Whether schema is write only. This affects the `writeOnly` keyword. | `null` ## Performance Considerations @@ -545,7 +543,7 @@ When the library creates a schema from the schema factory, it creates a distinct When the schema is created, the library will by default automatically preload all the validators needed and resolve references. This can be disabled with the `preloadJsonSchema` option in the `SchemaValidatorsConfig`. At this point, no exceptions will be thrown if a reference cannot be resolved. If there are references that are cyclic, only the first cycle will be preloaded. If you wish to ensure that remote references can all be resolved, the `initializeValidators` method needs to be called on the `JsonSchema` which will throw an exception if there are references that cannot be resolved. -Instances for `JsonSchemaFactory` and the `JsonSchema` created from it are designed to be thread-safe provided its configuration is not modified and should be cached and reused. Not reusing the `JsonSchema` means that the schema data needs to be repeated parsed with validator instances created and references resolved. When references are resolved, the validators created will be cached. For schemas that have deeply nested references, the memory needed for the validators may be very high, in which case the caching may need to be disabled using the `cacheRefs` option in the `JsonSchemaValidatorsConfig`. Disabling this will mean the validators from the references need to be re-created for each validation run which will impact performance. +Instances for `JsonSchemaFactory` and the `JsonSchema` created from it are designed to be thread-safe provided its configuration is not modified and should be cached and reused. Not reusing the `JsonSchema` means that the schema data needs to be repeated parsed with validator instances created and references resolved. When references are resolved, the validators created will be cached. For schemas that have deeply nested references, the memory needed for the validators may be very high, in which case the caching may need to be disabled using the `cacheRefs` option in the `SchemaValidatorsConfig`. Disabling this will mean the validators from the references need to be re-created for each validation run which will impact performance. Collecting annotations will adversely affect validation performance. @@ -576,7 +574,7 @@ This does not mean that using a schema with a later draft specification will aut ## [Regular Expressions](doc/ecma-262.md) -## [Custom Message](doc/cust-msg.md) +## [Custom Error Messages](doc/cust-msg.md) ## [Multiple Language](doc/multiple-language.md) diff --git a/doc/compatibility.md b/doc/compatibility.md index 410f894a8..3292bf4ba 100644 --- a/doc/compatibility.md +++ b/doc/compatibility.md @@ -19,11 +19,11 @@ The implementation supports the use of custom keywords, formats, vocabularies an There are currently no known issues with the required functionality from the specification. -The following are the tests results after running the [JSON Schema Test Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite) as at 29 Jan 2024 using version 1.3.1. As the test suite is continously updated, this can result in changes in the results subsequently. +The following are the tests results after running the [JSON Schema Test Suite](https://github.com/json-schema-org/JSON-Schema-Test-Suite) as at 18 Jun 2024 using version 1.4.1. As the test suite is continously updated, this can result in changes in the results subsequently. | Implementations | Overall | DRAFT_03 | DRAFT_04 | DRAFT_06 | DRAFT_07 | DRAFT_2019_09 | DRAFT_2020_12 | |-----------------|-------------------------------------------------------------------------|-------------------------------------------------------------------|---------------------------------------------------------------------|--------------------------------------------------------------------|------------------------------------------------------------------------|----------------------------------------------------------------------|------------------------------------------------------------------------| -| NetworkNt | pass: r:4703 (100.0%) o:2369 (100.0%)
fail: r:0 (0.0%) o:1 (0.0%) | | pass: r:600 (100.0%) o:251 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:796 (100.0%) o:318 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:880 (100.0%) o:541 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1201 (100.0%) o:625 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1226 (100.0%) o:634 (99.8%)
fail: r:0 (0.0%) o:1 (0.2%) | +| NetworkNt | pass: r:4803 (100.0%) o:2372 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | | pass: r:610 (100.0%) o:251 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:822 (100.0%) o:318 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:906 (100.0%) o:541 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1220 (100.0%) o:625 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | pass: r:1245 (100.0%) o:637 (100.0%)
fail: r:0 (0.0%) o:0 (0.0%) | ### Legend diff --git a/doc/config.md b/doc/config.md index c4ab32962..bdf1cfbd2 100644 --- a/doc/config.md +++ b/doc/config.md @@ -1,69 +1,47 @@ -### Configuration - -To control the behavior of the library, we have introduced SchemaValidatorsConfig recently. It gives users great flexibility when using the library in different contexts. - -For some users, it is just a JSON schema validator implemented mainly based on v4 with some additions from v5 to v7. - -For others, it is used as a critical component in the REST API frameworks to validate the request or response. The library was developed as part of the [light-4j](https://github.com/networknt/light-4j) framework in the beginning. - -Most of the configuration flags are used to control the difference between Swagger/OpenAPI specification and JSON schema specification as they are not the same. The future of the OpenAPI version might resolve this problem, but the release date is not set yet. +### Schema Validators Configuration + +| Name | Description | Default Value +|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------- +| `applyDefaultsStrategy` | The strategy for applying defaults when walking when missing or null nodes are encountered. | `ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY` +| `cacheRefs` | Whether the schemas loaded from refs will be cached and reused for subsequent runs. Setting this to `false` will affect performance but may be neccessary to prevent high memory usage for the cache if multiple nested applicators like `anyOf`, `oneOf` and `allOf` are used. | `true` +| `discriminatorKeywordEnabled` | Whether the `discriminator` keyword is handled according to OpenAPI 3. | `false` +| `errorMessageKeyword` | The keyword to use for custom error messages in the schema. If not set this features is disabled. This is typically set to `errorMessage` or `message`. | `null` +| `executionContextCustomizer` | This can be used to customize the `ExecutionContext` generated by the `JsonSchema` for each validation run. | `null` +| `failFast` | Whether to return failure immediately when an assertion is generated. | `false` +| `formatAssertionsEnabled` | The default is to generate format assertions from Draft 4 to Draft 7 and to only generate annotations from Draft 2019-09. Setting to `true` or `false` will override the default behavior. | `null` +| `javaSemantics` | Whether java semantics is used for the `type` keyword. | `false` +| `locale` | The locale to use for generating messages in the `ValidationMessage`. | `Locale.getDefault()` +| `losslessNarrowing` | Whether lossless narrowing is used for the `type` keyword. | `false` +| `messageSource` | This is used to retrieve the locale specific messages. | `DefaultMessageSource.getInstance()` +| `nullableKeywordEnabled` | Whether the `nullable` keyword is handled according to OpenAPI 3.0. This affects the `enum` and `type` keywords. | `false` +| `pathType` | The path type to use for reporting the instance location and evaluation path. Set to `PathType.JSON_PATH` to use JSON Path. | `PathType.JSON_POINTER` +| `preloadJsonSchema` | Whether the schema will be preloaded before processing any input. This will use memory but the execution of the validation will be faster. | `true` +| `preloadJsonSchemaRefMaxNestingDepth` | The max depth of the evaluation path to preload when preloading refs. | `40` +| `readOnly` | Whether schema is read only. This affects the `readOnly` keyword. | `null` +| `regularExpressionFactory` | The factory to use to create regular expressions for instance `JoniRegularExpressionFactory` or `GraalJSRegularExpressionFactory`. This requires the dependency to be manually added to the project or a `ClassNotFoundException` will be thrown. | `JDKRegularExpressionFactory.getInstance()` +| `schemaIdValidator` | This is used to customize how the `$id` values are validated. Note that the default implementation allows non-empty fragments where no base IRI is specified and also allows non-absolute IRI `$id` values in the root schema. | `JsonSchemaIdValidator.DEFAULT` +| `strict` | This is set whether keywords are strict in their validation. What this does depends on the individual validators. | +| `typeLoose` | Whether types are interpreted in a loose manner. If set to true, a single value can be interpreted as a size 1 array. Strings may also be interpreted as number, integer or boolean. | `false` +| `writeOnly` | Whether schema is write only. This affects the `writeOnly` keyword. | `null` #### How to use config -When you create a `JsonSchema` instance from the `JsonSchemaFactory`, you can pass an object of SchemaValidatorsConfig as the second parameter. +When you create a `JsonSchema` instance from the `JsonSchemaFactory`, you can pass an object of `SchemaValidatorsConfig` as the second parameter. ```java -SchemaValidatorsConfig config = new SchemaValidatorsConfig(); -config.setTypeLoose(false); -JsonSchema jsonSchema = JsonSchemaFactory.getInstance().getSchema(schema, config); +JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); +SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().typeLoose(false).build(); +JsonSchema schema = factory.getSchema(schema, config); ``` -#### Configurations - -* typeLoose - -When typeLoose is true, the validator will convert strings to different types to match the type defined in the schema. This is mostly used to validate the JSON request or response for headers, query parameters, path parameters, and cookies. For the HTTP protocol, these are all strings and might be defined as other types in the schema. For example, the page number might be an integer in the schema but passed as a query parameter in string. When it comes to validating arrays note that any item can also be interpreted as a size 1 array of that item so the item will be validated against the type defined for the array. - -* strictness -This is a map of keywords to whether the keyword's validators should perform a strict or permissive analysis. When strict is true, validators will perform strict checking against the schema. -This is the default behavior. When set to false, validators are free to relax some constraints but not required. Each validator has its own understanding of what constitutes strict and -permissive. - -* failFast - -When set to true, the validation process stops immediately when the first error occurs. This mostly used on microservices that is designed to [fail-fast](https://www.networknt.com/architecture/fail-fast/), or users don't want to see hundreds of errors for a big payload. Please be aware that the validator throws an exception in the case the first error occurs. To learn how to use it, please follow the [test case](https://github.com/networknt/json-schema-validator/blob/master/src/test/java/com/networknt/schema/V4JsonSchemaTest.java#L352). - -* handleNullableField - -When a field is set as nullable in the OpenAPI specification, the schema validator validates that it is nullable; however, it continues with validation against the nullable field. - -If handleNullableField is set to true && incoming field is nullable && value is field: null --> succeed - -If handleNullableField is set to false && incoming field is nullable && value is field: null --> it is up to the type validator using the SchemaValidator to handle it. - -The default value is true in the SchemaValidatorsConfig object. - -For more details, please refer to this [issue](https://github.com/networknt/json-schema-validator/issues/183). - -* javaSemantics - -When set to true, use Java-specific semantics rather than native JavaScript semantics. - -For example, if the node type is `number` per JS semantics where the value can be losslesly interpreted as `java.lang.Long`, the validator would use `integer` as the node type instead of `number`. This is useful when schema type is `integer`, since validation would fail otherwise. - -For more details, please refer to this [issue](https://github.com/networknt/json-schema-validator/issues/334). - -* losslessNarrowing - -When set to true, can interpret round doubles as integers. - -Note that setting `javaSemantics = true` will achieve the same functionality at this time. - -For more details, please refer to this [issue](https://github.com/networknt/json-schema-validator/issues/344). - -* pathType - -This defines how path expressions are defined and returned once validation is performed through `ValidationMessage` instances. This can either be set to `PathType.JSON_POINTER` for [JSONPointer](https://www.rfc-editor.org/rfc/rfc6901.html) expressions, -or to `PathType.JSON_PATH` for [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) expressions. Doing so allows you to report the path for each finding and to potentially lookup nodes -(see [here](https://github.com/networknt/json-schema-validator/blob/c41df270a71f8423c63cfaa379d2e9b3f570b73e/doc/yaml-line-numbers.md#scenario-2---validationmessage-line-locations) for an example). By default, path expressions use a -`PathType.LEGACY` format which is close to JSONPath but does not escape reserved characters. +#### Details + +| Name | Details +|---------------------------|------------------------------------------------ +| `typeLoose` | When typeLoose is true, the validator will convert strings to different types to match the type defined in the schema. This is mostly used to validate the JSON request or response for headers, query parameters, path parameters, and cookies. For the HTTP protocol, these are all strings and might be defined as other types in the schema. For example, the page number might be an integer in the schema but passed as a query parameter in string. When it comes to validating arrays note that any item can also be interpreted as a size 1 array of that item so the item will be validated against the type defined for the array. +| `strict` | This is a map of keywords to whether the keyword's validators should perform a strict or permissive analysis. When strict is true, validators will perform strict checking against the schema. This is the default behavior. When set to false, validators are free to relax some constraints but not required. Each validator has its own understanding of what constitutes strict and permissive. +| `failFast` | When set to true, the validation process stops immediately when the first error occurs. This mostly used on microservices that is designed to [fail-fast](https://www.networknt.com/architecture/fail-fast/), or users don't want to see hundreds of errors for a big payload. +| `nullableKeywordEnabled` | When a field is set as `nullable` in the OpenAPI specification, the schema validator validates that it is `nullable`; however, it continues with validation against the `nullable` field. If `nullableKeywordEnabled` is set to true && incoming field is `nullable` && value is field: null --> succeed. If `nullableKeywordEnabled` is set to false && incoming field is `nullable` && value is field: null --> it is up to the type validator using the SchemaValidator to handle it. +| `javaSemantics` | When set to true, use Java-specific semantics rather than native JavaScript semantics. For example, if the node type is `number` per JS semantics where the value can be losslesly interpreted as `java.lang.Long`, the validator would use `integer` as the node type instead of `number`. This is useful when schema type is `integer`, since validation would fail otherwise. +| `losslessNarrowing` | When set to true, can interpret round doubles as integers. Note that setting `javaSemantics = true` will achieve the same functionality at this time. +| `pathType` | This defines how path expressions are defined and returned once validation is performed through `ValidationMessage` instances. This can either be set to `PathType.JSON_POINTER` for [JSONPointer](https://www.rfc-editor.org/rfc/rfc6901.html) expressions, or to `PathType.JSON_PATH` for [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) expressions. Doing so allows you to report the path for each finding and to potentially lookup nodes (see [here](https://github.com/networknt/json-schema-validator/blob/c41df270a71f8423c63cfaa379d2e9b3f570b73e/doc/yaml-line-numbers.md#scenario-2---validationmessage-line-locations) for an example). \ No newline at end of file diff --git a/doc/cust-msg.md b/doc/cust-msg.md index c6c96137b..cde6a00fe 100644 --- a/doc/cust-msg.md +++ b/doc/cust-msg.md @@ -1,7 +1,11 @@ -# Custom Message Support -Users can add their own custom messages for schema validation using the instructions in this page. +# Custom Error Messages +Schema authors can provide their own custom messages within the schema using a specified keyword. -The json schema itself has a place for the customised message. +This is not enabled by default and the `SchemaValidatorsConfig` must be configured with the `errorMessageKeyword`. + +```java +SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().errorMessageKeyword("errorMessage").build(); +``` ## Examples ### Example 1 : @@ -19,9 +23,9 @@ The custom message can be provided outside properties for each type, as shown in "maxItems": 3 } }, - "message": { - "maxItems" : "MaxItem must be 3 only", - "type" : "Invalid type" + "errorMessage": { + "maxItems": "MaxItem must be 3 only", + "type": "Invalid type" } } ``` @@ -34,14 +38,14 @@ To keep custom messages distinct for each type, one can even give them in each p "dateTime": { "type": "string", "format": "date", - "message": { + "errorMessage": { "format": "Keep date format yyyy-mm-dd" } }, "uuid": { "type": "string", "format": "uuid", - "message": { + "errorMessage": { "format": "Input should be uuid" } } @@ -63,11 +67,11 @@ For the keywords `required` and `dependencies`, different messages can be specif } }, "required": ["foo", "bar"], - "message": { - "type" : "should be an object", + "errorMessage": { + "type": "should be an object", "required": { - "foo" : "'foo' is required", - "bar" : "'bar' is required" + "foo": "'foo' is required", + "bar": "'bar' is required" } } } @@ -87,11 +91,11 @@ The message can use arguments but note that single quotes need to be escaped as } }, "required": ["foo", "bar"], - "message": { - "type" : "should be an object", + "errorMessage": { + "type": "should be an object", "required": { - "foo" : "{0}: ''foo'' is required", - "bar" : "{0}: ''bar'' is required" + "foo": "{0}: ''foo'' is required", + "bar": "{0}: ''bar'' is required" } } } @@ -99,15 +103,9 @@ The message can use arguments but note that single quotes need to be escaped as ## Format ```json -"message": { - [validationType] : [customMessage] - } +"errorMessage": { + "[keyword]": "[customMessage]" +} ``` -Users can express custom message in the **'message'** field. -The **'validation type'** should be the key and the **'custom message'** should be the value. - -Also, we can make format the dynamic message with properties returned from [ValidationMessage.java](https://github.com/networknt/json-schema-validator/blob/master/src/main/java/com/networknt/schema/ValidationMessage.java) class such as **arguments, path e.t.c.** - - - -Take a look at the [PR1](https://github.com/networknt/json-schema-validator/pull/438) and [PR2](https://github.com/networknt/json-schema-validator/pull/632) +Users can provide custom messages in the configured keyword, typically `errorMessage` or `message` field. +The `keyword` should be the key and the `customMessage` should be the value. \ No newline at end of file diff --git a/doc/duration.md b/doc/duration.md index f19dd9621..0d3bc30d8 100644 --- a/doc/duration.md +++ b/doc/duration.md @@ -20,9 +20,8 @@ By default, the duration validator performs a strict check that the value conforms to RFC 3339. You can relax this constraint by setting strict to false. ```java -SchemaValidatorsConfig config = new SchemaValidatorsConfig(); -config.setStrict("duration", false); -JsonSchema jsonSchema = JsonSchemaFactory.getInstance().getSchema(schema, config); +SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().strict("duration", false).build(); +JsonSchema jsonSchema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schema, config); ``` The relaxed check permits: diff --git a/doc/ecma-262.md b/doc/ecma-262.md index 418686ebc..aea236e02 100644 --- a/doc/ecma-262.md +++ b/doc/ecma-262.md @@ -49,8 +49,9 @@ The following test case shows how to pass a config object to use the `GraalJS` f public class RegularExpressionTest { @Test public void testInvalidRegexValidatorECMA262() throws Exception { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setRegularExpressionFactory(GraalJSRegularExpressionFactory.getInstance()); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .regularExpressionFactory(GraalJSRegularExpressionFactory.getInstance()) + .build(); JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); JsonSchema schema = factory.getSchema("{\r\n" + " \"format\": \"regex\"\r\n" @@ -63,4 +64,26 @@ public class RegularExpressionTest { } ``` +## Performance + +The following is the relative performance of the different implementations. + +``` +Benchmark Mode Cnt Score Error Units +RegularExpressionBenchmark.graaljs thrpt 6 362696.226 ± 15811.099 ops/s +RegularExpressionBenchmark.graaljs:gc.alloc.rate thrpt 6 2584.386 ± 112.708 MB/sec +RegularExpressionBenchmark.graaljs:gc.alloc.rate.norm thrpt 6 7472.003 ± 0.001 B/op +RegularExpressionBenchmark.graaljs:gc.count thrpt 6 130.000 counts +RegularExpressionBenchmark.graaljs:gc.time thrpt 6 144.000 ms +RegularExpressionBenchmark.jdk thrpt 6 2776184.321 ± 41838.479 ops/s +RegularExpressionBenchmark.jdk:gc.alloc.rate thrpt 6 1482.565 ± 22.343 MB/sec +RegularExpressionBenchmark.jdk:gc.alloc.rate.norm thrpt 6 560.000 ± 0.001 B/op +RegularExpressionBenchmark.jdk:gc.count thrpt 6 74.000 counts +RegularExpressionBenchmark.jdk:gc.time thrpt 6 78.000 ms +RegularExpressionBenchmark.joni thrpt 6 1810229.581 ± 35230.798 ops/s +RegularExpressionBenchmark.joni:gc.alloc.rate thrpt 6 1463.887 ± 28.483 MB/sec +RegularExpressionBenchmark.joni:gc.alloc.rate.norm thrpt 6 848.003 ± 0.001 B/op +RegularExpressionBenchmark.joni:gc.count thrpt 6 73.000 counts +RegularExpressionBenchmark.joni:gc.time thrpt 6 77.000 ms +``` diff --git a/doc/quickstart.md b/doc/quickstart.md index 73c7be412..d66c07aae 100644 --- a/doc/quickstart.md +++ b/doc/quickstart.md @@ -1,16 +1,24 @@ ## Quick Start -To use the validator, we need to have the `JsonSchema` loaded and cached. +To use the validator, the `JsonSchema` first needs to be loaded. For performance it is recommended that the `JsonSchema` is cached. -For simplicity the following test loads a schema from a `String` or `JsonNode`. Note that loading a schema in this manner is not recommended as a relative `$ref` will not be properly resolved as there is no base IRI. +The following examples demonstrate loading the `JsonSchema` in the following manner. +* `SchemaLocation` with the value of the `$id` of the schema which is mapped using the `SchemaMapper` to the retrieval IRI which is on the classpath +* `SchemaLocation` with the value of the `$id` of the schema where the content of the schema is supplied using the `SchemaLoader` +* `SchemaLocation` with the value of the retrieval IRI which is on the classpath +* `String` with the content of the schema +* `JsonNode` with the content of the schema -The preferred method of loading a schema is by using a `SchemaLocation` and by configuring the appropriate `SchemaMapper` and `SchemaLoader` on the `JsonSchemaFactory`. +The preferred method of loading a schema is by using a `SchemaLocation` and by configuring the appropriate `SchemaMapper` and `SchemaLoader` on the `JsonSchemaFactory`. The `SchemaMapper` is use to map the `$id` to the retrieval IRI. The `SchemaLoader` is used to actually load the content of the schema. + +Loading a schema from a `String` or `JsonNode` is not recommended as a relative `$ref` will not be properly resolved as there is no base IRI. ```java package com.example; import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.Collections; import java.util.Set; import org.junit.jupiter.api.Test; @@ -18,13 +26,80 @@ import org.junit.jupiter.api.Test; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; -import com.networknt.schema.*; +import com.networknt.schema.SpecVersion.VersionFlag; import com.networknt.schema.serialization.JsonMapperFactory; +/** + * Sample test. + */ public class SampleTest { + @Test + void schemaFromSchemaLocationMapping() throws JsonMappingException, JsonProcessingException { + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.schemaMappers( + schemaMappers -> schemaMappers.mapPrefix("https://www.example.com/schema", "classpath:schema"))); + /* + * This should be cached for performance. + */ + JsonSchema schemaFromSchemaLocation = factory + .getSchema(SchemaLocation.of("https://www.example.com/schema/example-ref.json")); + /* + * By default all schemas are preloaded eagerly but ref resolve failures are not + * thrown. You check if there are issues with ref resolving using + * initializeValidators() + */ + schemaFromSchemaLocation.initializeValidators(); + Set errors = schemaFromSchemaLocation.validate("{\"id\": \"2\"}", InputFormat.JSON, + executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); + assertEquals(1, errors.size()); + } + + @Test + void schemaFromSchemaLocationContent() throws JsonMappingException, JsonProcessingException { + String schemaData = "{\"enum\":[1, 2, 3, 4],\"enumErrorCode\":\"Not in the list\"}"; + + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, + builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas( + Collections.singletonMap("https://www.example.com/schema/example-ref.json", schemaData)))); + /* + * This should be cached for performance. + */ + JsonSchema schemaFromSchemaLocation = factory + .getSchema(SchemaLocation.of("https://www.example.com/schema/example-ref.json")); + /* + * By default all schemas are preloaded eagerly but ref resolve failures are not + * thrown. You check if there are issues with ref resolving using + * initializeValidators() + */ + schemaFromSchemaLocation.initializeValidators(); + Set errors = schemaFromSchemaLocation.validate("{\"id\": \"2\"}", InputFormat.JSON, + executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); + assertEquals(1, errors.size()); + } + + @Test + void schemaFromClasspath() throws JsonMappingException, JsonProcessingException { + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); + /* + * This should be cached for performance. + * + * Loading from using the retrieval IRI is not recommended as it may cause + * confusing when resolving relative $ref when $id is also used. + */ + JsonSchema schemaFromClasspath = factory.getSchema(SchemaLocation.of("classpath:schema/example-ref.json")); + /* + * By default all schemas are preloaded eagerly but ref resolve failures are not + * thrown. You check if there are issues with ref resolving using + * initializeValidators() + */ + schemaFromClasspath.initializeValidators(); + Set errors = schemaFromClasspath.validate("{\"id\": \"2\"}", InputFormat.JSON, + executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); + assertEquals(1, errors.size()); + } + @Test void schemaFromString() throws JsonMappingException, JsonProcessingException { - JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4); + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); /* * This should be cached for performance. * @@ -33,13 +108,14 @@ public class SampleTest { */ JsonSchema schemaFromString = factory .getSchema("{\"enum\":[1, 2, 3, 4],\"enumErrorCode\":\"Not in the list\"}"); - Set errors = schemaFromString.validate("7", InputFormat.JSON); + Set errors = schemaFromString.validate("7", InputFormat.JSON, + executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); assertEquals(1, errors.size()); } @Test void schemaFromJsonNode() throws JsonMappingException, JsonProcessingException { - JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4); + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); JsonNode schemaNode = JsonMapperFactory.getInstance().readTree( "{\"$schema\": \"http://json-schema.org/draft-06/schema#\", \"properties\": { \"id\": {\"type\": \"number\"}}}"); /* @@ -48,7 +124,7 @@ public class SampleTest { * Loading from a JsonNode is not recommended as there is no base IRI to use for * resolving relative $ref. * - * Note that the V4 from the factory is the default version if $schema is not + * Note that the V202012 from the factory is the default version if $schema is not * specified. As $schema is specified in the data, V6 is used. */ JsonSchema schemaFromNode = factory.getSchema(schemaNode); @@ -58,7 +134,8 @@ public class SampleTest { * initializeValidators() */ schemaFromNode.initializeValidators(); - Set errors = schemaFromNode.validate("{\"id\": \"2\"}", InputFormat.JSON); + Set errors = schemaFromNode.validate("{\"id\": \"2\"}", InputFormat.JSON, + executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); assertEquals(1, errors.size()); } } diff --git a/doc/upgrading.md b/doc/upgrading.md index 506e96761..c3b05260c 100644 --- a/doc/upgrading.md +++ b/doc/upgrading.md @@ -4,6 +4,91 @@ This library can contain breaking changes in `minor` version releases. This contains information on the notable or breaking changes in each version. +### 1.4.1 + +#### Schema Validators Config + +The `SchemaValidatorsConfig` constructor has been deprecated. Use the `SchemaValidators.builder` to create an instance instead. `SchemaValidatorConfig` instances are intended to be immutable in future and those created by the builder will throw `UnsupportedOperationException` when setters are called. + +Note that there are differences in defaults from the builder vs the constructor. + +The following builder creates the same values as the constructor previously. + +```java +SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .pathType(PathType.LEGACY) + .errorMessageKeyword("message") + .nullableKeywordEnabled(true) + .build(); +``` + +The following configurations were renamed with the old ones deprecated +* `handleNullableField` -> `nullableKeywordEnabled` +* `openAPI3StyleDiscriminators` -> `discriminatorKeywordEnabled` +* `customMessageSupported` -> `errorMessageKeyword` + +The following defaults were changed in the builder vs the constructor +* `pathType` from `PathType.LEGACY` to `PathType.JSON_POINTER` +* `handleNullableField` from `true` to `false` +* `customMessageSupported` from `true` to `false` + +When using the builder custom error messages are not enabled by default and must be enabled by specifying the error message keyword to use ie. "message". + +| Deprecated Code | Replacement +|------------------------------------------------------------------------|---------------------------------------------------------------------- +| `SchemaValidatorsConfig config = new SchemaValidatorsConfig();` | `SchemaValidatorsConfig config = SchemaValidatorsConfig().builder().pathType(PathType.LEGACY).errorMessageKeyword("message").nullableKeywordEnabled(true).build();` +| `config.setEcma262Validator(true);` | `builder.regularExpressionFactory(JoniRegularExpressionFactory.getInstance());` +| `config.setHandleNullableField(true);` | `builder.nullableKeywordEnabled(true);` +| `config.setOpenAPI3StyleDiscriminators(true);` | `builder.discriminatorKeywordEnabled(true);` +| `config.setCustomMessageSupported(true);` | `builder.errorMessageKeyword("message");` + +#### Collector Context + +`JsonSchema.validateAndCollect` has been deprecated in favor of explicitly calling `loadCollectors`. + +This also deprecates the related `loadCollectors` configuration in `SchemaValidatorsConfig`. + +This makes the `CollectorContext.loadCollectors()` method public to be explicitly called instead of relying on the `SchemaValidatorsConfig`. + +Proper usage of the `validateAndCollect` method is confusing. It relies on a configuration set in `SchemaValidatorsConfig` that is configured on a per schema basis. It immediately runs `loadCollectors` if set to `true` and will never be able to run `loadCollectors` if set to `false` as the method is not `public`. + +The documentation has been updated to reflect the replacement, which is to explicitly create the `CollectorContext` to be shared and set for each execution. Finally `loadCollectors` can be called a the end if needed. + +```java +CollectorContext collectorContext = new CollectorContext(); +// This adds a custom collect keyword that sets values in the CollectorContext whenever it gets processed +JsonMetaSchema metaSchema = JsonMetaSchema.builder(JsonMetaSchema.getV202012()).keyword(new CollectKeyword()).build(); +JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.metaSchema(metaSchema)); +JsonSchema schema = factory.getSchema("{\n" + + " \"collect\": true\n" + + "}"); +for (int i = 0; i < 50; i++) { + // The shared CollectorContext is set on the ExecutionContext for every run to aggregate data from all the runs + schema.validate("1", InputFormat.JSON, executionContext -> { + executionContext.setCollectorContext(collectorContext); + }); +} +// This is called for Collector implementations to aggregate data +collectorContext.loadCollectors(); +AtomicInteger result = (AtomicInteger) collectorContext.get("collect"); +assertEquals(50, result.get()); +``` + +#### Schema Reference Caching + +Previously when schema `$ref` are encountered, the reference and all the validators it requires will always be cached and stored if needed in the future. This can potentially cause out of memory errors for schemas that use applicators like `allOf`, `anyOf`, `oneOf`. This can be configured by setting the `cacheRefs` option to `false` on `SchemaValidatorsConfig.builder()`. Note that not caching will impact performance and make it slower. + +#### Regular Expressions + +This adds GraalJS as an implementation. The Joni implementation now will throw an `Exception` if illegal escapes are used in the regular expressions. + +The preferred way of configuring the implementation is via setting the `regularExpressionFactory` on `SchemaValidatorsConfig.builder()`. + +#### Fine Grain Debug Logging + +Previously the if debug logging is enabled the validators will log fine grained logs. This now requires setting the `debugEnabled` flag in `ExecutionConfig` as the checks to determine if the logger was enabled was impacting performance. + + ### 1.4.0 This contains breaking changes diff --git a/doc/walkers.md b/doc/walkers.md index 51736e499..8cbc6c2f6 100644 --- a/doc/walkers.md +++ b/doc/walkers.md @@ -136,15 +136,15 @@ If the onWalkStart method returns WalkFlow.SKIP, the actual walk method executio Walk listeners can be added by using the SchemaValidatorsConfig class. ```java -SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); - schemaValidatorsConfig.addKeywordWalkListener(new AllKeywordListener()); - schemaValidatorsConfig.addKeywordWalkListener(ValidatorTypeCode.REF.getValue(), new RefKeywordListener()); - schemaValidatorsConfig.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), +SchemaValidatorsConfig.Builder schemaValidatorsConfig = SchemaValidatorsConfig.builder(); + schemaValidatorsConfig.keywordWalkListener(new AllKeywordListener()); + schemaValidatorsConfig.keywordWalkListener(ValidatorTypeCode.REF.getValue(), new RefKeywordListener()); + schemaValidatorsConfig.keywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new PropertiesKeywordListener()); final JsonSchemaFactory schemaFactory = JsonSchemaFactory .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).metaSchema(metaSchema) .build(); -this.jsonSchema = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig); +this.jsonSchema = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig.build()); ``` @@ -153,12 +153,12 @@ There are two kinds of walk listeners, keyword walk listeners and property walk Both property walk listeners and keyword walk listener can be modeled by using the same WalkListener interface. Following is an example of how to add a property walk listener. ```java -SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); -schemaValidatorsConfig.addPropertyWalkListener(new ExamplePropertyWalkListener()); +SchemaValidatorsConfig.Builder schemaValidatorsConfig = SchemaValidatorsConfig.builder(); +schemaValidatorsConfig.propertyWalkListener(new ExamplePropertyWalkListener()); final JsonSchemaFactory schemaFactory = JsonSchemaFactory .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).metaSchema(metaSchema) .build(); -this.jsonSchema = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig); +this.jsonSchema = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig.build()); ``` @@ -278,9 +278,9 @@ But if we apply defaults while walking, then required validation passes, and the ```java JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4); - SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); - schemaValidatorsConfig.setApplyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)); - JsonSchema jsonSchema = schemaFactory.getSchema(SchemaLocation.of("classpath:schema.json"), schemaValidatorsConfig); + SchemaValidatorsConfig.Builder schemaValidatorsConfig = SchemaValidatorsConfig.builder(); + schemaValidatorsConfig.applyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)); + JsonSchema jsonSchema = schemaFactory.getSchema(SchemaLocation.of("classpath:schema.json"), schemaValidatorsConfig.build()); JsonNode inputNode = objectMapper.readTree(getClass().getClassLoader().getResourceAsStream("data.json")); ValidationResult result = jsonSchema.walk(inputNode, true); diff --git a/doc/yaml-line-numbers.md b/doc/yaml-line-numbers.md index 2894bd638..7cb8b8959 100644 --- a/doc/yaml-line-numbers.md +++ b/doc/yaml-line-numbers.md @@ -271,9 +271,8 @@ either [JSONPath](https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/) or we will set the path expressions to be JSONPointers. This is achieved by configuring the reported path type through the `SchemaValidatorsConfig` before we read our schema: ```java - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - JsonSchema jsonSchema = JsonSchemaFactory.getInstance().getSchema(schema, config); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); + JsonSchema jsonSchema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schema, config); ``` Having set paths to be JSONPointer expressions we can use those pointers for locating the appropriate `JsonNode` instances. The following couple of methods illustrate this process: diff --git a/doc/yaml.md b/doc/yaml.md index 1c63f075c..060b9bc8d 100644 --- a/doc/yaml.md +++ b/doc/yaml.md @@ -1,25 +1,44 @@ -One of the features of this library is to validate the YAML file in addition to the JSON. In fact, the main use case for this library is to be part of the light-4j framework to validate the request/response at runtime against the OpenAPI specification file openapi.yaml. If you are not using light-4j, you need to load the YAML with https://github.com/FasterXML/jackson-dataformats-text first, and then everything is the same as JSON. +One of the features of this library is to validate the YAML file in addition to the JSON. In fact, the main use case for this library is to be part of the light-4j framework to validate the request/response at runtime against the OpenAPI specification file openapi.yaml. ### Usage -Add the dependency +By default including the library would also include the `jackson-dataformat-yaml` unless explicitly excluded. ```xml com.fasterxml.jackson.dataformat jackson-dataformat-yaml - 2.10.1 + 2.17.1 ``` -and create object mapper using yaml factory i.e `ObjectMapper objMapper =new ObjectMapper(new YAMLFactory());` +By default the object mapper from `YamlMapperFactory.getInstance()` will be used but this can configured by using a `JsonNodeReader`. -#### Example ```java -JsonSchemaFactory factory = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).objectMapper(mapper).build(); /* Using draft-07. You can choose anyother draft.*/ -JsonSchema schema = factory.getSchema(YamlOperations.class.getClassLoader().getResourceAsStream("your-schema.json")); +ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); +JsonNodeReader jsonNodeReader = JsonNodeReader.builder().yamlMapper(yamlMapper).build(); +``` -JsonNode jsonNode = mapper.readTree(YamlOperations.class.getClassLoader().getResourceAsStream("your-file.yaml")); -Set validateMsg = schema.validate(jsonNode); +#### Example +```java +String schemaData = "---\r\n" + + "\"$id\": 'https://schema/myschema'\r\n" + + "properties:\r\n" + + " startDate:\r\n" + + " format: 'date'\r\n" + + " minLength: 6\r\n" + + ""; +String inputData = "---\r\n" + + "startDate: '1'\r\n" + + ""; +ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); +JsonNodeReader jsonNodeReader = JsonNodeReader.builder().yamlMapper(yamlMapper).build(); +JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, + builder -> builder.jsonNodeReader(jsonNodeReader).build()); +SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); +JsonSchema schema = factory.getSchema(schemaData, InputFormat.YAML, config); +Set messages = schema.validate(inputData, InputFormat.YAML, executionContext -> { + executionContext.getExecutionConfig().setFormatAssertionsEnabled(true); +}); ``` diff --git a/src/main/java/com/networknt/schema/AllOfValidator.java b/src/main/java/com/networknt/schema/AllOfValidator.java index 01bc1b6d1..967dc4408 100644 --- a/src/main/java/com/networknt/schema/AllOfValidator.java +++ b/src/main/java/com/networknt/schema/AllOfValidator.java @@ -77,7 +77,7 @@ protected Set validate(ExecutionContext executionContext, Jso childSchemaErrors.union(localErrors); } - if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) { + if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled()) { final Iterator arrayElements = this.schemaNode.elements(); while (arrayElements.hasNext()) { final ObjectNode allOfEntry = (ObjectNode) arrayElements.next(); diff --git a/src/main/java/com/networknt/schema/AnyOfValidator.java b/src/main/java/com/networknt/schema/AnyOfValidator.java index 202b98db6..02440f12d 100644 --- a/src/main/java/com/networknt/schema/AnyOfValidator.java +++ b/src/main/java/com/networknt/schema/AnyOfValidator.java @@ -63,7 +63,7 @@ protected Set validate(ExecutionContext executionContext, Jso JsonNodePath instanceLocation, boolean walk) { debug(logger, executionContext, node, rootNode, instanceLocation); - if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) { + if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled()) { executionContext.enterDiscriminatorContext(new DiscriminatorContext(), instanceLocation); } SetView allErrors = null; @@ -101,13 +101,13 @@ protected Set validate(ExecutionContext executionContext, Jso numberOfValidSubSchemas++; } - if (errors.isEmpty() && (!this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) + if (errors.isEmpty() && (!this.validationContext.getConfig().isDiscriminatorKeywordEnabled()) && canShortCircuit() && canShortCircuit(executionContext)) { // Clear all errors. Note that this is checked in finally. allErrors = null; // return empty errors. return errors; - } else if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) { + } else if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled()) { DiscriminatorContext currentDiscriminatorContext = executionContext.getCurrentDiscriminatorContext(); if (currentDiscriminatorContext.isDiscriminatorMatchFound() || currentDiscriminatorContext.isDiscriminatorIgnore()) { @@ -140,7 +140,7 @@ && canShortCircuit() && canShortCircuit(executionContext)) { executionContext.setFailFast(failFast); } - if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators() + if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled() && executionContext.getCurrentDiscriminatorContext().isActive() && !executionContext.getCurrentDiscriminatorContext().isDiscriminatorIgnore()) { return Collections.singleton(message().instanceNode(node).instanceLocation(instanceLocation) @@ -150,7 +150,7 @@ && canShortCircuit() && canShortCircuit(executionContext)) { .build()); } } finally { - if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) { + if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled()) { executionContext.leaveDiscriminatorContextImmediately(instanceLocation); } } diff --git a/src/main/java/com/networknt/schema/BaseJsonValidator.java b/src/main/java/com/networknt/schema/BaseJsonValidator.java index 87e9311a7..7c3ee1833 100644 --- a/src/main/java/com/networknt/schema/BaseJsonValidator.java +++ b/src/main/java/com/networknt/schema/BaseJsonValidator.java @@ -51,8 +51,8 @@ public BaseJsonValidator(SchemaLocation schemaLocation, JsonNodePath evaluationP ValidationContext validationContext, boolean suppressSubSchemaRetrieval) { super(errorMessageType, (validationContext != null && validationContext.getConfig() != null) - ? validationContext.getConfig().isCustomMessageSupported() - : true, + ? validationContext.getConfig().getErrorMessageKeyword() + : null, (validationContext != null && validationContext.getConfig() != null) ? validationContext.getConfig().getMessageSource() : DefaultMessageSource.getInstance(), @@ -70,7 +70,7 @@ public BaseJsonValidator(SchemaLocation schemaLocation, JsonNodePath evaluationP * @param schemaNode the schema node * @param validationContext the validation context * @param errorMessageType the error message type - * @param customErrorMessagesEnabled whether custom error msessages are enabled + * @param errorMessageKeyword the error message keyword * @param messageSource the message source * @param keyword the keyword * @param parentSchema the parent schema @@ -86,7 +86,7 @@ protected BaseJsonValidator( ValidationContext validationContext, /* Below from ValidationMessageHandler */ ErrorMessageType errorMessageType, - boolean customErrorMessagesEnabled, + String errorMessageKeyword, MessageSource messageSource, Keyword keyword, JsonSchema parentSchema, @@ -94,7 +94,7 @@ protected BaseJsonValidator( JsonNodePath evaluationPath, JsonSchema evaluationParentSchema, Map errorMessage) { - super(errorMessageType, customErrorMessagesEnabled, messageSource, keyword, + super(errorMessageType, errorMessageKeyword, messageSource, keyword, parentSchema, schemaLocation, evaluationPath, evaluationParentSchema, errorMessage); this.suppressSubSchemaRetrieval = suppressSubSchemaRetrieval; this.schemaNode = schemaNode; diff --git a/src/main/java/com/networknt/schema/DefaultJsonMetaSchemaFactory.java b/src/main/java/com/networknt/schema/DefaultJsonMetaSchemaFactory.java index 8d7cc518c..342e86f6c 100644 --- a/src/main/java/com/networknt/schema/DefaultJsonMetaSchemaFactory.java +++ b/src/main/java/com/networknt/schema/DefaultJsonMetaSchemaFactory.java @@ -40,9 +40,6 @@ protected JsonMetaSchema loadMetaSchema(String iri, JsonSchemaFactory schemaFact SchemaValidatorsConfig config) { try { JsonMetaSchema result = loadMetaSchemaBuilder(iri, schemaFactory, config).build(); - if (result.getKeywords().containsKey("discriminator")) { - config.setOpenAPI3StyleDiscriminators(true); - } return result; } catch (InvalidSchemaException e) { throw e; diff --git a/src/main/java/com/networknt/schema/EnumValidator.java b/src/main/java/com/networknt/schema/EnumValidator.java index 7c77241fb..3c2cab47b 100644 --- a/src/main/java/com/networknt/schema/EnumValidator.java +++ b/src/main/java/com/networknt/schema/EnumValidator.java @@ -63,7 +63,7 @@ public EnumValidator(SchemaLocation schemaLocation, JsonNodePath evaluationPath, } // check if the parent schema declares the fields as nullable - if (validationContext.getConfig().isHandleNullableField()) { + if (validationContext.getConfig().isNullableKeywordEnabled()) { JsonNode nullable = parentSchema.getSchemaNode().get("nullable"); if (nullable != null && nullable.asBoolean()) { nodes.add(NullNode.getInstance()); diff --git a/src/main/java/com/networknt/schema/JsonMetaSchema.java b/src/main/java/com/networknt/schema/JsonMetaSchema.java index 4ae26bb4c..069309caa 100644 --- a/src/main/java/com/networknt/schema/JsonMetaSchema.java +++ b/src/main/java/com/networknt/schema/JsonMetaSchema.java @@ -478,11 +478,14 @@ public JsonValidator newValidator(ValidationContext validationContext, SchemaLoc try { Keyword kw = this.keywords.get(keyword); if (kw == null) { - if ("message".equals(keyword) && validationContext.getConfig().isCustomMessageSupported()) { + if (keyword.equals(validationContext.getConfig().getErrorMessageKeyword())) { + return null; + } + if (validationContext.getConfig().isNullableKeywordEnabled() && "nullable".equals(keyword)) { return null; } if (ValidatorTypeCode.DISCRIMINATOR.getValue().equals(keyword) - && validationContext.getConfig().isOpenAPI3StyleDiscriminators()) { + && validationContext.getConfig().isDiscriminatorKeywordEnabled()) { return ValidatorTypeCode.DISCRIMINATOR.newValidator(schemaLocation, evaluationPath, schemaNode, parentSchema, validationContext); } diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java index 1c3182f47..03459746a 100644 --- a/src/main/java/com/networknt/schema/JsonSchema.java +++ b/src/main/java/com/networknt/schema/JsonSchema.java @@ -162,7 +162,7 @@ private JsonSchema(ValidationContext validationContext, SchemaLocation schemaLoc * @param schemaNode the schema node * @param validationContext the validation context * @param errorMessageType the error message type - * @param customErrorMessagesEnabled whether custom error msessages are enabled + * @param errorMessageKeyword the error message keyword * @param messageSource the message source * @param keyword the keyword * @param parentSchema the parent schema @@ -184,7 +184,7 @@ protected JsonSchema( ValidationContext validationContext, /* Below from ValidationMessageHandler */ ErrorMessageType errorMessageType, - boolean customErrorMessagesEnabled, + String errorMessageKeyword, MessageSource messageSource, Keyword keyword, JsonSchema parentSchema, @@ -192,7 +192,7 @@ protected JsonSchema( JsonNodePath evaluationPath, JsonSchema evaluationParentSchema, Map errorMessage) { - super(suppressSubSchemaRetrieval, schemaNode, validationContext, errorMessageType, customErrorMessagesEnabled, messageSource, keyword, + super(suppressSubSchemaRetrieval, schemaNode, validationContext, errorMessageType, errorMessageKeyword, messageSource, keyword, parentSchema, schemaLocation, evaluationPath, evaluationParentSchema, errorMessage); this.validators = validators; this.validatorsLoaded = validatorsLoaded; @@ -238,12 +238,19 @@ public JsonSchema fromRef(JsonSchema refEvaluationParentSchema, JsonNodePath ref schemaNode, validationContext, /* Below from ValidationMessageHandler */ - errorMessageType, customErrorMessagesEnabled, messageSource, + errorMessageType, errorMessageKeyword, messageSource, keyword, parentSchema, schemaLocation, evaluationPath, evaluationParentSchema, errorMessage); } public JsonSchema withConfig(SchemaValidatorsConfig config) { + if (this.getValidationContext().getMetaSchema().getKeywords().containsKey("discriminator") + && !config.isDiscriminatorKeywordEnabled()) { + config = SchemaValidatorsConfig.builder(config) + .discriminatorKeywordEnabled(true) + .nullableKeywordEnabled(true) + .build(); + } if (!this.getValidationContext().getConfig().equals(config)) { ValidationContext validationContext = new ValidationContext(this.getValidationContext().getMetaSchema(), this.getValidationContext().getJsonSchemaFactory(), config, @@ -266,7 +273,7 @@ public JsonSchema withConfig(SchemaValidatorsConfig config) { validationContext, /* Below from ValidationMessageHandler */ errorMessageType, - customErrorMessagesEnabled, + errorMessageKeyword, messageSource, keyword, parentSchema, @@ -595,7 +602,7 @@ private long activeDialect() { @Override public Set validate(ExecutionContext executionContext, JsonNode jsonNode, JsonNode rootNode, JsonNodePath instanceLocation) { - if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) { + if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled()) { ObjectNode discriminator = (ObjectNode) schemaNode.get("discriminator"); if (null != discriminator && null != executionContext.getCurrentDiscriminatorContext()) { executionContext.getCurrentDiscriminatorContext().registerDiscriminator(schemaLocation, @@ -619,7 +626,7 @@ public Set validate(ExecutionContext executionContext, JsonNo } } - if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) { + if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled()) { ObjectNode discriminator = (ObjectNode) this.schemaNode.get("discriminator"); if (null != discriminator) { final DiscriminatorContext discriminatorContext = executionContext diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java index 89ddffd79..4055ded9b 100644 --- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java +++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java @@ -386,9 +386,16 @@ private JsonSchema doCreate(ValidationContext validationContext, SchemaLocation private ValidationContext withMetaSchema(ValidationContext validationContext, JsonNode schemaNode) { JsonMetaSchema metaSchema = getMetaSchema(schemaNode, validationContext.getConfig()); if (metaSchema != null && !metaSchema.getIri().equals(validationContext.getMetaSchema().getIri())) { - return new ValidationContext(metaSchema, validationContext.getJsonSchemaFactory(), - validationContext.getConfig(), validationContext.getSchemaReferences(), - validationContext.getSchemaResources(), validationContext.getDynamicAnchors()); + SchemaValidatorsConfig config = validationContext.getConfig(); + if (metaSchema.getKeywords().containsKey("discriminator") && !config.isDiscriminatorKeywordEnabled()) { + config = SchemaValidatorsConfig.builder(config) + .discriminatorKeywordEnabled(true) + .nullableKeywordEnabled(true) + .build(); + } + return new ValidationContext(metaSchema, validationContext.getJsonSchemaFactory(), config, + validationContext.getSchemaReferences(), validationContext.getSchemaResources(), + validationContext.getDynamicAnchors()); } return validationContext; } @@ -409,17 +416,21 @@ protected SchemaLocation getSchemaLocation(SchemaLocation schemaLocation) { protected ValidationContext createValidationContext(final JsonNode schemaNode, SchemaValidatorsConfig config) { final JsonMetaSchema jsonMetaSchema = getMetaSchemaOrDefault(schemaNode, config); - return new ValidationContext(jsonMetaSchema, this, config); + SchemaValidatorsConfig configResult = config; + if (jsonMetaSchema.getKeywords().containsKey("discriminator") && !config.isDiscriminatorKeywordEnabled()) { + configResult = SchemaValidatorsConfig.builder(config) + .discriminatorKeywordEnabled(true) + .nullableKeywordEnabled(true) + .build(); + } + return new ValidationContext(jsonMetaSchema, this, configResult); } - + private JsonMetaSchema getMetaSchema(final JsonNode schemaNode, SchemaValidatorsConfig config) { final JsonNode iriNode = schemaNode.get("$schema"); if (iriNode != null && iriNode.isTextual()) { JsonMetaSchema result = metaSchemas.computeIfAbsent(normalizeMetaSchemaUri(iriNode.textValue()), id -> loadMetaSchema(id, config)); - if (result.getKeywords().containsKey("discriminator")) { - config.setOpenAPI3StyleDiscriminators(true); - } return result; } return null; @@ -444,9 +455,6 @@ private JsonMetaSchema getMetaSchemaOrDefault(final JsonNode schemaNode, SchemaV public JsonMetaSchema getMetaSchema(String iri, SchemaValidatorsConfig config) { String key = normalizeMetaSchemaUri(iri); JsonMetaSchema result = metaSchemas.computeIfAbsent(key, id -> loadMetaSchema(id, config)); - if (result.getKeywords().containsKey("discriminator")) { - config.setOpenAPI3StyleDiscriminators(true); - } return result; } @@ -654,6 +662,7 @@ ObjectMapper getJsonMapper() { * @return the schema validators config */ protected SchemaValidatorsConfig createSchemaValidatorsConfig() { + // Remain as constructor until constructor is removed return new SchemaValidatorsConfig(); } diff --git a/src/main/java/com/networknt/schema/OneOfValidator.java b/src/main/java/com/networknt/schema/OneOfValidator.java index 35505b24a..d964defaa 100644 --- a/src/main/java/com/networknt/schema/OneOfValidator.java +++ b/src/main/java/com/networknt/schema/OneOfValidator.java @@ -72,7 +72,7 @@ protected Set validate(ExecutionContext executionContext, Jso boolean failFast = executionContext.isFailFast(); try { DiscriminatorValidator discriminator = null; - if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) { + if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled()) { DiscriminatorContext discriminatorContext = new DiscriminatorContext(); executionContext.enterDiscriminatorContext(discriminatorContext, instanceLocation); @@ -105,7 +105,7 @@ protected Set validate(ExecutionContext executionContext, Jso break; } - if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) { + if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled()) { // The discriminator will cause all messages other than the one with the // matching discriminator to be discarded. Note that the discriminator cannot // affect the actual validation result. @@ -152,7 +152,7 @@ protected Set validate(ExecutionContext executionContext, Jso index++; } - if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators() + if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled() && (discriminator != null || executionContext.getCurrentDiscriminatorContext().isActive()) && !executionContext.getCurrentDiscriminatorContext().isDiscriminatorMatchFound() && !executionContext.getCurrentDiscriminatorContext().isDiscriminatorIgnore()) { @@ -166,7 +166,7 @@ protected Set validate(ExecutionContext executionContext, Jso // Restore flag executionContext.setFailFast(failFast); - if (this.validationContext.getConfig().isOpenAPI3StyleDiscriminators()) { + if (this.validationContext.getConfig().isDiscriminatorKeywordEnabled()) { executionContext.leaveDiscriminatorContextImmediately(instanceLocation); } } diff --git a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java index 75e5ff87c..bb6242fa0 100644 --- a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java +++ b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java @@ -1,7 +1,7 @@ /* * Copyright (c) 2016 Network New Technologies Inc. * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache LicenseBuilder Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * @@ -34,23 +34,35 @@ import java.util.Locale; import java.util.Map; import java.util.Objects; +import java.util.function.Consumer; /** * Configuration for validators. */ public class SchemaValidatorsConfig { + // This is just a constant for listening to all Keywords. + public static final String ALL_KEYWORD_WALK_LISTENER_KEY = "com.networknt.AllKeywordWalkListener"; + public static final int DEFAULT_PRELOAD_JSON_SCHEMA_REF_MAX_NESTING_DEPTH = 40; /** - * Used to validate the acceptable $id values. + * The strategy the walker uses to sets nodes that are missing or NullNode to + * the default value, if any, and mutate the input json. */ - private JsonSchemaIdValidator schemaIdValidator = JsonSchemaIdValidator.DEFAULT; + private ApplyDefaultsStrategy applyDefaultsStrategy = ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY; /** - * when validate type, if TYPE_LOOSE = true, will try to convert string to - * different types to match the type defined in schema. + * Controls if schemas loaded from refs will be cached and reused for subsequent runs. */ - private boolean typeLoose; + private boolean cacheRefs = true; + + /** + * When set to true, "messages" provided in schema are used for forming validation errors + * else default messages are used + */ + private String errorMessageKeyword = "message"; + + private ExecutionContextCustomizer executionContextCustomizer; /** * When set to true, validator process is stop immediately when a very first @@ -59,15 +71,25 @@ public class SchemaValidatorsConfig { private boolean failFast; /** - * When set to true, walker sets nodes that are missing or NullNode to the - * default value, if any, and mutate the input json. + * Since Draft 2019-09 format assertions are not enabled by default. */ - private ApplyDefaultsStrategy applyDefaultsStrategy = ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY; + private Boolean formatAssertionsEnabled = null; /** - * Used to create {@link com.networknt.schema.regex.RegularExpression}. + * When a field is set as nullable in the OpenAPI specification, the schema + * validator validates that it is nullable however continues with validation + * against the nullable field + *

+ * If handleNullableField is set to true && incoming field is nullable && value + * is field: null --> succeed If handleNullableField is set to false && incoming + * field is nullable && value is field: null --> it is up to the type validator + * using the SchemaValidator to handle it. */ - private RegularExpressionFactory regularExpressionFactory = JDKRegularExpressionFactory.getInstance(); + private boolean nullableKeywordEnabled = true; + + private final WalkListenerRunner itemWalkListenerRunner; + + private final List itemWalkListeners; /** * When set to true, use Java-specific semantics rather than native JavaScript @@ -75,533 +97,540 @@ public class SchemaValidatorsConfig { */ private boolean javaSemantics; + private final WalkListenerRunner keywordWalkListenerRunner; + + private final Map> keywordWalkListenersMap; + + /** + * The Locale to consider when loading validation messages from the default resource bundle. + */ + private Locale locale; + /** * When set to true, can interpret round doubles as integers */ private boolean losslessNarrowing; /** - * When set to true, "messages" provided in schema are used for forming validation errors - * else default messages are used + * The message source to use for generating localised messages. */ - private boolean customMessageSupported = true; + private MessageSource messageSource; /** * When set to true, support for discriminators is enabled for validations of * oneOf, anyOf and allOf as described on GitHub. */ - private boolean openAPI3StyleDiscriminators = false; + private boolean discriminatorKeywordEnabled = false; /** - * Contains a mapping of how strict a keyword's validators should be. - * Defaults to {@literal true}. - *

- * Each validator has its own understanding of what constitutes strict - * and permissive. + * The approach used to generate paths in reported messages, logs and errors. Default is the legacy "JSONPath-like" approach. */ - private final Map strictness = new HashMap<>(0); + private PathType pathType = PathType.DEFAULT; /** - * When a field is set as nullable in the OpenAPI specification, the schema - * validator validates that it is nullable however continues with validation - * against the nullable field - *

- * If handleNullableField is set to true && incoming field is nullable && value - * is field: null --> succeed If handleNullableField is set to false && incoming - * field is nullable && value is field: null --> it is up to the type validator - * using the SchemaValidator to handle it. + * Controls if the schema will automatically be preloaded. */ - private boolean handleNullableField = true; + private boolean preloadJsonSchema = true; /** - * When set to true assumes that schema is used to validate incoming data from an API. + * Controls the max depth of the evaluation path to preload when preloading refs. */ - private Boolean readOnly = null; + private int preloadJsonSchemaRefMaxNestingDepth = DEFAULT_PRELOAD_JSON_SCHEMA_REF_MAX_NESTING_DEPTH; - /** - * When set to true assumes that schema is used to to validate outgoing data from an API. - */ - private Boolean writeOnly = null; + private final WalkListenerRunner propertyWalkListenerRunner; + + private final List propertyWalkListeners; /** - * The approach used to generate paths in reported messages, logs and errors. Default is the legacy "JSONPath-like" approach. + * When set to true assumes that schema is used to validate incoming data from an API. */ - private PathType pathType = PathType.DEFAULT; + private Boolean readOnly = null; /** - * Controls if the schema will automatically be preloaded. + * Used to create {@link com.networknt.schema.regex.RegularExpression}. */ - private boolean preloadJsonSchema = true; + private RegularExpressionFactory regularExpressionFactory = JDKRegularExpressionFactory.getInstance(); /** - * Controls the max depth of the evaluation path to preload when preloading refs. + * Used to validate the acceptable $id values. */ - private int preloadJsonSchemaRefMaxNestingDepth = DEFAULT_PRELOAD_JSON_SCHEMA_REF_MAX_NESTING_DEPTH; - + private JsonSchemaIdValidator schemaIdValidator = JsonSchemaIdValidator.DEFAULT; + /** - * Controls if schemas loaded from refs will be cached and reused for subsequent runs. + * Contains a mapping of how strict a keyword's validators should be. + * Defaults to {@literal true}. + *

+ * Each validator has its own understanding of what constitutes strict + * and permissive. */ - private boolean cacheRefs = true; - - // This is just a constant for listening to all Keywords. - public static final String ALL_KEYWORD_WALK_LISTENER_KEY = "com.networknt.AllKeywordWalkListener"; - - private final Map> keywordWalkListenersMap = new HashMap<>(); - - private final List propertyWalkListeners = new ArrayList<>(); - - private final List itemWalkListeners = new ArrayList<>(); - - private ExecutionContextCustomizer executionContextCustomizer; - - @Deprecated - private boolean loadCollectors = true; + private final Map strictness; /** - * The Locale to consider when loading validation messages from the default resource bundle. + * when validate type, if TYPE_LOOSE = true, will try to convert string to + * different types to match the type defined in schema. */ - private Locale locale; + private boolean typeLoose; /** - * The message source to use for generating localised messages. + * When set to true assumes that schema is used to to validate outgoing data from an API. */ - private MessageSource messageSource; + private Boolean writeOnly = null; /** - * Since Draft 2019-09 format assertions are not enabled by default. + * Constructor to create an instance. + *

+ * This is deprecated in favor of using the builder + * {@link SchemaValidatorsConfig#builder()} to create an instance. Migration + * note: The builder has different defaults from the constructor. + *

+     * SchemaValidatorsConfig config = SchemaValidatorsConfig.builder()
+     *     .pathType(PathType.LEGACY)
+     *     .errorMessageKeyword("message")
+     *     .nullableKeywordEnabled(true)
+     *     .build();
+     * 
+ *

    + *
  • customMessageSupported (errorMessageKeyword): change from message to null + *
  • pathType: changed from PathType.LEGACY to PathType.JSON_POINTER. + *
  • handleNullableField (nullableKeywordEnabled): changed from true to false + *

*/ - private Boolean formatAssertionsEnabled = null; - - /************************ START OF UNEVALUATED CHECKS **********************************/ - @Deprecated - public SchemaValidatorsConfig disableUnevaluatedAnalysis() { - return this; - } + public SchemaValidatorsConfig() { + this.strictness = new HashMap<>(0); + + this.keywordWalkListenersMap = new HashMap<>(); + this.propertyWalkListeners = new ArrayList<>(); + this.itemWalkListeners = new ArrayList<>(); + + this.itemWalkListenerRunner = new DefaultItemWalkListenerRunner(getArrayItemWalkListeners()); + this.keywordWalkListenerRunner = new DefaultKeywordWalkListenerRunner(getKeywordWalkListenersMap()); + this.propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(getPropertyWalkListeners()); + } + + SchemaValidatorsConfig(ApplyDefaultsStrategy applyDefaultsStrategy, boolean cacheRefs, + String errorMessageKeyword, ExecutionContextCustomizer executionContextCustomizer, boolean failFast, + Boolean formatAssertionsEnabled, boolean nullableKeywordEnabled, + List itemWalkListeners, boolean javaSemantics, + Map> keywordWalkListenersMap, Locale locale, boolean losslessNarrowing, + MessageSource messageSource, boolean discriminatorKeywordEnabled, PathType pathType, + boolean preloadJsonSchema, int preloadJsonSchemaRefMaxNestingDepth, + List propertyWalkListeners, Boolean readOnly, + RegularExpressionFactory regularExpressionFactory, JsonSchemaIdValidator schemaIdValidator, + Map strictness, boolean typeLoose, Boolean writeOnly) { + super(); + this.applyDefaultsStrategy = applyDefaultsStrategy; + this.cacheRefs = cacheRefs; + this.errorMessageKeyword = errorMessageKeyword; + this.executionContextCustomizer = executionContextCustomizer; + this.failFast = failFast; + this.formatAssertionsEnabled = formatAssertionsEnabled; + this.nullableKeywordEnabled = nullableKeywordEnabled; + this.itemWalkListeners = itemWalkListeners; + this.javaSemantics = javaSemantics; + this.keywordWalkListenersMap = keywordWalkListenersMap; + this.locale = locale; + this.losslessNarrowing = losslessNarrowing; + this.messageSource = messageSource; + this.discriminatorKeywordEnabled = discriminatorKeywordEnabled; + this.pathType = pathType; + this.preloadJsonSchema = preloadJsonSchema; + this.preloadJsonSchemaRefMaxNestingDepth = preloadJsonSchemaRefMaxNestingDepth; + this.propertyWalkListeners = propertyWalkListeners; + this.readOnly = readOnly; + this.regularExpressionFactory = regularExpressionFactory; + this.schemaIdValidator = schemaIdValidator; + this.strictness = strictness; + this.typeLoose = typeLoose; + this.writeOnly = writeOnly; - @Deprecated - public SchemaValidatorsConfig disableUnevaluatedItems() { - return this; + this.itemWalkListenerRunner = new DefaultItemWalkListenerRunner(getArrayItemWalkListeners()); + this.keywordWalkListenerRunner = new DefaultKeywordWalkListenerRunner(getKeywordWalkListenersMap()); + this.propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(getPropertyWalkListeners()); } - @Deprecated - public SchemaValidatorsConfig disableUnevaluatedProperties() { - return this; + public void addItemWalkListener(JsonSchemaWalkListener itemWalkListener) { + this.itemWalkListeners.add(itemWalkListener); } - @Deprecated - public SchemaValidatorsConfig enableUnevaluatedAnalysis() { - return this; + public void addItemWalkListeners(List itemWalkListeners) { + this.itemWalkListeners.addAll(itemWalkListeners); } - @Deprecated - public SchemaValidatorsConfig enableUnevaluatedItems() { - return this; + public void addKeywordWalkListener(JsonSchemaWalkListener keywordWalkListener) { + if (this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) { + List keywordWalkListeners = new ArrayList<>(); + this.keywordWalkListenersMap.put(ALL_KEYWORD_WALK_LISTENER_KEY, keywordWalkListeners); + } + this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY).add(keywordWalkListener); } - @Deprecated - public SchemaValidatorsConfig enableUnevaluatedProperties() { - return this; + public void addKeywordWalkListener(String keyword, JsonSchemaWalkListener keywordWalkListener) { + if (this.keywordWalkListenersMap.get(keyword) == null) { + List keywordWalkListeners = new ArrayList<>(); + this.keywordWalkListenersMap.put(keyword, keywordWalkListeners); + } + this.keywordWalkListenersMap.get(keyword).add(keywordWalkListener); } - @Deprecated - public boolean isUnevaluatedItemsAnalysisDisabled() { - return false; + public void addKeywordWalkListeners(List keywordWalkListeners) { + if (this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) { + List ikeywordWalkListeners = new ArrayList<>(); + this.keywordWalkListenersMap.put(ALL_KEYWORD_WALK_LISTENER_KEY, ikeywordWalkListeners); + } + this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY).addAll(keywordWalkListeners); } - @Deprecated - public boolean isUnevaluatedItemsAnalysisEnabled() { - return !isUnevaluatedItemsAnalysisDisabled(); + public void addKeywordWalkListeners(String keyword, List keywordWalkListeners) { + if (this.keywordWalkListenersMap.get(keyword) == null) { + List ikeywordWalkListeners = new ArrayList<>(); + this.keywordWalkListenersMap.put(keyword, ikeywordWalkListeners); + } + this.keywordWalkListenersMap.get(keyword).addAll(keywordWalkListeners); } - @Deprecated - public boolean isUnevaluatedPropertiesAnalysisDisabled() { - return false; + public void addPropertyWalkListener(JsonSchemaWalkListener propertyWalkListener) { + this.propertyWalkListeners.add(propertyWalkListener); } - @Deprecated - public boolean isUnevaluatedPropertiesAnalysisEnabled() { - return !isUnevaluatedPropertiesAnalysisDisabled(); + public void addPropertyWalkListeners(List propertyWalkListeners) { + this.propertyWalkListeners.addAll(propertyWalkListeners); } - /************************ END OF UNEVALUATED CHECKS **********************************/ + public ApplyDefaultsStrategy getApplyDefaultsStrategy() { + return this.applyDefaultsStrategy; + } - /** - * - * @return true if type loose is used. - */ - public boolean isTypeLoose() { - return this.typeLoose; + public List getArrayItemWalkListeners() { + return this.itemWalkListeners; } - public void setTypeLoose(boolean typeLoose) { - this.typeLoose = typeLoose; + public ExecutionContextCustomizer getExecutionContextCustomizer() { + return this.executionContextCustomizer; } /** - * When enabled, - * {@link JsonValidator#validate(ExecutionContext, JsonNode, JsonNode, JsonNodePath)} - * doesn't return any {@link java.util.Set}<{@link ValidationMessage}>, - * instead a {@link JsonSchemaException} is thrown as soon as a validation - * errors is discovered. - * - * @param failFast boolean + * Gets the format assertion enabled flag. + *

+ * This defaults to null meaning that it will follow the defaults of the + * specification. + *

+ * Since draft 2019-09 this will default to false unless enabled by using the + * $vocabulary keyword. + * + * @return the format assertions enabled flag */ - public void setFailFast(final boolean failFast) { - this.failFast = failFast; - } - - public boolean isFailFast() { - return this.failFast; - } - - public void setApplyDefaultsStrategy(ApplyDefaultsStrategy applyDefaultsStrategy) { - this.applyDefaultsStrategy = applyDefaultsStrategy != null ? applyDefaultsStrategy - : ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY; + public Boolean getFormatAssertionsEnabled() { + return formatAssertionsEnabled; } - public ApplyDefaultsStrategy getApplyDefaultsStrategy() { - return this.applyDefaultsStrategy; + WalkListenerRunner getItemWalkListenerRunner() { + return this.itemWalkListenerRunner; } - public boolean isHandleNullableField() { - return this.handleNullableField; + WalkListenerRunner getKeywordWalkListenerRunner() { + return this.keywordWalkListenerRunner; } - public void setHandleNullableField(boolean handleNullableField) { - this.handleNullableField = handleNullableField; + public Map> getKeywordWalkListenersMap() { + return this.keywordWalkListenersMap; } /** - * Gets whether to use a ECMA-262 compliant regular expression validator. + * Get the locale to consider when generating localised messages (default is the + * JVM default). *

- * This defaults to the false and setting true require inclusion of optional - * org.jruby.joni:joni or org.graalvm.js:js dependencies. + * This locale is on a schema basis and will be used as the default locale for + * {@link com.networknt.schema.ExecutionConfig}. * - * @return true if ECMA-262 compliant + * @return The locale. */ - public boolean isEcma262Validator() { - return !(this.regularExpressionFactory instanceof JDKRegularExpressionFactory); + public Locale getLocale() { + if (this.locale == null) { + // This should not be cached as it can be changed using Locale#setDefault(Locale) + return Locale.getDefault(); + } + return this.locale; } /** - * Sets whether to use a ECMA-262 compliant regular expression validator. - *

- * This defaults to the false and setting true require inclusion of optional - * org.jruby.joni:joni or org.graalvm.js:js dependencies. - * - * @param ecma262Validator true if ECMA-262 compliant + * Get the message source to use for generating localised messages. + * + * @return the message source */ - public void setEcma262Validator(boolean ecma262Validator) { - this.regularExpressionFactory = ecma262Validator ? ECMAScriptRegularExpressionFactory.getInstance() - : JDKRegularExpressionFactory.getInstance(); + public MessageSource getMessageSource() { + if (this.messageSource == null) { + return DefaultMessageSource.getInstance(); + } + return this.messageSource; } /** - * Gets the regular expression factory. - *

- * This defaults to the JDKRegularExpressionFactory and the implementations - * require inclusion of optional org.jruby.joni:joni or org.graalvm.js:js dependencies. + * Get the approach used to generate paths in messages, logs and errors. * - * @return the factory + * @return The path generation approach. */ - public RegularExpressionFactory getRegularExpressionFactory() { - return regularExpressionFactory; + public PathType getPathType() { + return this.pathType; } /** - * Sets the regular expression factory. - *

- * This defaults to the JDKRegularExpressionFactory and the implementations - * require inclusion of optional org.jruby.joni:joni or org.graalvm.js:js dependencies. + * Gets the max depth of the evaluation path to preload when preloading refs. * - * @see JDKRegularExpressionFactory - * @see ECMAScriptRegularExpressionFactory - * @param regularExpressionFactory the factory + * @return the max depth to preload */ - public void setRegularExpressionFactory(RegularExpressionFactory regularExpressionFactory) { - this.regularExpressionFactory = regularExpressionFactory; - } - - public boolean isJavaSemantics() { - return this.javaSemantics; - } - - public void setJavaSemantics(boolean javaSemantics) { - this.javaSemantics = javaSemantics; - } - - public boolean isCustomMessageSupported() { - return customMessageSupported; - } - - public void setCustomMessageSupported(boolean customMessageSupported) { - this.customMessageSupported = customMessageSupported; - } - - public void addKeywordWalkListener(JsonSchemaWalkListener keywordWalkListener) { - if (this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) { - List keywordWalkListeners = new ArrayList<>(); - this.keywordWalkListenersMap.put(ALL_KEYWORD_WALK_LISTENER_KEY, keywordWalkListeners); - } - this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY).add(keywordWalkListener); - } - - public void addKeywordWalkListener(String keyword, JsonSchemaWalkListener keywordWalkListener) { - if (this.keywordWalkListenersMap.get(keyword) == null) { - List keywordWalkListeners = new ArrayList<>(); - this.keywordWalkListenersMap.put(keyword, keywordWalkListeners); - } - this.keywordWalkListenersMap.get(keyword).add(keywordWalkListener); - } - - public void addKeywordWalkListeners(List keywordWalkListeners) { - if (this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY) == null) { - List ikeywordWalkListeners = new ArrayList<>(); - this.keywordWalkListenersMap.put(ALL_KEYWORD_WALK_LISTENER_KEY, ikeywordWalkListeners); - } - this.keywordWalkListenersMap.get(ALL_KEYWORD_WALK_LISTENER_KEY).addAll(keywordWalkListeners); - } - - public void addKeywordWalkListeners(String keyword, List keywordWalkListeners) { - if (this.keywordWalkListenersMap.get(keyword) == null) { - List ikeywordWalkListeners = new ArrayList<>(); - this.keywordWalkListenersMap.put(keyword, ikeywordWalkListeners); - } - this.keywordWalkListenersMap.get(keyword).addAll(keywordWalkListeners); + public int getPreloadJsonSchemaRefMaxNestingDepth() { + return preloadJsonSchemaRefMaxNestingDepth; } - public void addPropertyWalkListeners(List propertyWalkListeners) { - this.propertyWalkListeners.addAll(propertyWalkListeners); + WalkListenerRunner getPropertyWalkListenerRunner() { + return this.propertyWalkListenerRunner; } - public void addPropertyWalkListener(JsonSchemaWalkListener propertyWalkListener) { - this.propertyWalkListeners.add(propertyWalkListener); + public List getPropertyWalkListeners() { + return this.propertyWalkListeners; } - public void addItemWalkListener(JsonSchemaWalkListener itemWalkListener) { - this.itemWalkListeners.add(itemWalkListener); + /** + * Gets the regular expression factory. + *

+ * This defaults to the JDKRegularExpressionFactory and the implementations + * require inclusion of optional org.jruby.joni:joni or org.graalvm.js:js dependencies. + * + * @return the factory + */ + public RegularExpressionFactory getRegularExpressionFactory() { + return regularExpressionFactory; } - public void addItemWalkListeners(List itemWalkListeners) { - this.itemWalkListeners.addAll(itemWalkListeners); + /** + * Gets the schema id validator to validate $id. + * + * @return the validator + */ + public JsonSchemaIdValidator getSchemaIdValidator() { + return schemaIdValidator; } - public List getPropertyWalkListeners() { - return this.propertyWalkListeners; + /** + * Gets if schemas loaded from refs will be cached and reused for subsequent + * runs. + * + * @return true if schemas loaded from refs should be cached + */ + public boolean isCacheRefs() { + return cacheRefs; } - public Map> getKeywordWalkListenersMap() { - return this.keywordWalkListenersMap; + @Deprecated + public boolean isCustomMessageSupported() { + return this.errorMessageKeyword != null; } - public List getArrayItemWalkListeners() { - return this.itemWalkListeners; + public String getErrorMessageKeyword() { + return this.errorMessageKeyword; } - private final WalkListenerRunner itemWalkListenerRunner = new DefaultItemWalkListenerRunner(getArrayItemWalkListeners()); - - WalkListenerRunner getItemWalkListenerRunner() { - return this.itemWalkListenerRunner; + /** + * Gets whether to use a ECMA-262 compliant regular expression validator. + *

+ * This defaults to the false and setting true require inclusion of optional + * org.jruby.joni:joni or org.graalvm.js:js dependencies. + * + * @return true if ECMA-262 compliant + */ + public boolean isEcma262Validator() { + return !(this.regularExpressionFactory instanceof JDKRegularExpressionFactory); } - private final WalkListenerRunner keywordWalkListenerRunner = new DefaultKeywordWalkListenerRunner(getKeywordWalkListenersMap()); - - WalkListenerRunner getKeywordWalkListenerRunner() { - return this.keywordWalkListenerRunner; - } - - private final WalkListenerRunner propertyWalkListenerRunner = new DefaultPropertyWalkListenerRunner(getPropertyWalkListeners()); - - WalkListenerRunner getPropertyWalkListenerRunner() { - return this.propertyWalkListenerRunner; + public boolean isFailFast() { + return this.failFast; } - public SchemaValidatorsConfig() { + /** + * Deprecated use {{@link #isNullableKeywordEnabled()} instead. + * + * @return true if the nullable keyword is enabled + */ + @Deprecated + public boolean isHandleNullableField() { + return isNullableKeywordEnabled(); } - public ExecutionContextCustomizer getExecutionContextCustomizer() { - return this.executionContextCustomizer; + /** + * Gets if the nullable keyword is enabled. + * + * @return true if the nullable keyword is enabled + */ + public boolean isNullableKeywordEnabled() { + return this.nullableKeywordEnabled; } - public void setExecutionContextCustomizer(ExecutionContextCustomizer executionContextCustomizer) { - this.executionContextCustomizer = executionContextCustomizer; + public boolean isJavaSemantics() { + return this.javaSemantics; } public boolean isLosslessNarrowing() { return this.losslessNarrowing; } - public void setLosslessNarrowing(boolean losslessNarrowing) { - this.losslessNarrowing = losslessNarrowing; - } - /** * Indicates whether OpenAPI 3 style discriminators should be supported + *

+ * Deprecated use {{@link #isDiscriminatorKeywordEnabled()} instead. * * @return true in case discriminators are enabled * @since 1.0.51 */ + @Deprecated public boolean isOpenAPI3StyleDiscriminators() { - return this.openAPI3StyleDiscriminators; - } - - /** - * When enabled, the validation of anyOf and allOf in - * polymorphism will respect OpenAPI 3 style discriminators as described in the - * OpenAPI - * 3.0.3 spec. The presence of a discriminator configuration on the schema - * will lead to the following changes in the behavior: - *

    - *
  • for oneOf the spec is unfortunately very vague. Whether - * oneOf semantics should be affected by discriminators or not is - * not even 100% clear within the members of the OAS steering committee. - * Therefore oneOf at the moment ignores discriminators
  • - *
  • for anyOf the validation will choose one of the candidate - * schemas for validation based on the discriminator property value and will - * pass validation when this specific schema passes. This is in particular - * useful when the payload could match multiple candidates in the - * anyOf list and could lead to ambiguity. Example: type B has all - * mandatory properties of A and adds more mandatory ones. Whether the payload - * is an A or B is determined via the discriminator property name. A payload - * indicating it is an instance of B then requires passing the validation of B - * and passing the validation of A would not be sufficient anymore.
  • - *
  • for allOf use cases with discriminators defined on the - * copied-in parent type, it is possible to automatically validate against a - * subtype. Example: some schema specifies that there is a field of type A. A - * carries a discriminator field and B inherits from A. Then B is automatically - * a candidate for validation as well and will be chosen in case the - * discriminator property matches
  • - *
- * - * @param openAPI3StyleDiscriminators whether or not discriminators should be - * used. Defaults to false - * @since 1.0.51 - */ - public void setOpenAPI3StyleDiscriminators(boolean openAPI3StyleDiscriminators) { - this.openAPI3StyleDiscriminators = openAPI3StyleDiscriminators; + return isDiscriminatorKeywordEnabled(); } /** - * Sets if collectors are to be loaded. - *

- * This is deprecated in favor of the caller calling {@link CollectorContext#loadCollectors()} manually. + * Gets if the discriminator keyword is enabled. * - * @param loadCollectors to load collectors + * @return true if the discriminator keyword is enabled */ - @Deprecated - public void setLoadCollectors(boolean loadCollectors) { - this.loadCollectors = loadCollectors; + public boolean isDiscriminatorKeywordEnabled() { + return this.discriminatorKeywordEnabled; } /** - * Gets if collectors are to be loaded. + * Gets if the schema should be preloaded. * - * @return if collectors are to be loader + * @return true if it should be preloaded */ - @Deprecated - public boolean doLoadCollectors() { - return this.loadCollectors; + public boolean isPreloadJsonSchema() { + return preloadJsonSchema; } public boolean isReadOnly() { return null != this.readOnly && this.readOnly; } - public void setReadOnly(boolean readOnly) { - this.readOnly = readOnly; - } - - public boolean isWriteOnly() { - return null != this.writeOnly && this.writeOnly; - } - - public void setWriteOnly(boolean writeOnly) { - this.writeOnly = writeOnly; + /** + * Answers whether a keyword's validators may relax their analysis. The + * default is to perform strict checking. One must explicitly allow a + * validator to be more permissive. + *

+ * Each validator has its own understanding of what is permissive and + * strict. Consult the keyword's documentation for details. + * + * @param keyword the keyword to adjust (not null) + * @return Whether to perform a strict validation. + */ + public boolean isStrict(String keyword) { + return isStrict(keyword, Boolean.TRUE); } /** - * Use {@code isReadOnly} or {@code isWriteOnly} - * @return true if schema is used to write data + * Determines if the validator should perform strict checking. + * + * @param keyword the keyword + * @param defaultValue the default value + * @return whether to perform a strict validation */ - @Deprecated - public boolean isWriteMode() { - return null == this.writeOnly || this.writeOnly; + public boolean isStrict(String keyword, Boolean defaultValue) { + return this.strictness.getOrDefault(Objects.requireNonNull(keyword, "keyword cannot be null"), defaultValue); } /** - * Set the approach used to generate paths in messages, logs and errors (default is PathType.LEGACY). * - * @param pathType The path generation approach. + * @return true if type loose is used. */ - public void setPathType(PathType pathType) { - this.pathType = pathType; + public boolean isTypeLoose() { + return this.typeLoose; + } + + public boolean isWriteOnly() { + return null != this.writeOnly && this.writeOnly; + } + + public void setApplyDefaultsStrategy(ApplyDefaultsStrategy applyDefaultsStrategy) { + this.applyDefaultsStrategy = applyDefaultsStrategy != null ? applyDefaultsStrategy + : ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY; } /** - * Get the approach used to generate paths in messages, logs and errors. - * - * @return The path generation approach. + * Sets if schemas loaded from refs will be cached and reused for subsequent + * runs. + *

+ * Note that setting this to false will affect performance as refs will need to + * be repeatedly resolved for each evaluation run. It may be needed to be set to + * false if there are multiple nested applicators like anyOf, oneOf and allOf as + * that will consume a lot of memory to cache all the permutations. + * + * @param cacheRefs true to cache */ - public PathType getPathType() { - return this.pathType; + public void setCacheRefs(boolean cacheRefs) { + this.cacheRefs = cacheRefs; } /** - * Answers whether a keyword's validators may relax their analysis. The - * default is to perform strict checking. One must explicitly allow a - * validator to be more permissive. + * Sets whether custom error messages in the schema are used. *

- * Each validator has its own understanding of what is permissive and - * strict. Consult the keyword's documentation for details. - * - * @param keyword the keyword to adjust (not null) - * @return Whether to perform a strict validation. + * This is deprecated in favor of setting the error message keyword to use. + * + * @param customMessageSupported true to use message as the error message keyword */ - public boolean isStrict(String keyword) { - return isStrict(keyword, Boolean.TRUE); + @Deprecated + public void setCustomMessageSupported(boolean customMessageSupported) { + this.errorMessageKeyword = customMessageSupported ? "message" : null; } /** - * Determines if the validator should perform strict checking. + * Sets whether to use a ECMA-262 compliant regular expression validator. + *

+ * This defaults to the false and setting true require inclusion of optional + * org.jruby.joni:joni or org.graalvm.js:js dependencies. * - * @param keyword the keyword - * @param defaultValue the default value - * @return whether to perform a strict validation + * @param ecma262Validator true if ECMA-262 compliant */ - public boolean isStrict(String keyword, Boolean defaultValue) { - return this.strictness.getOrDefault(Objects.requireNonNull(keyword, "keyword cannot be null"), defaultValue); + public void setEcma262Validator(boolean ecma262Validator) { + this.regularExpressionFactory = ecma262Validator ? ECMAScriptRegularExpressionFactory.getInstance() + : JDKRegularExpressionFactory.getInstance(); + } + + public void setExecutionContextCustomizer(ExecutionContextCustomizer executionContextCustomizer) { + this.executionContextCustomizer = executionContextCustomizer; } /** - * Alters the strictness of validations for a specific keyword. When set to - * {@literal true}, instructs the keyword's validators to perform strict - * validation. Otherwise, a validator may perform a more permissive check. - * - * @param keyword The keyword to adjust (not null) - * @param strict Whether to perform strict validations + * When enabled, + * {@link JsonValidator#validate(ExecutionContext, JsonNode, JsonNode, JsonNodePath)} + * doesn't return any {@link java.util.Set}<{@link ValidationMessage}>, + * instead a {@link JsonSchemaException} is thrown as soon as a validation + * errors is discovered. + * + * @param failFast boolean */ - public void setStrict(String keyword, boolean strict) { - this.strictness.put(Objects.requireNonNull(keyword, "keyword cannot be null"), strict); + public void setFailFast(final boolean failFast) { + this.failFast = failFast; } /** - * Get the locale to consider when generating localised messages (default is the - * JVM default). + * Sets the format assertion enabled flag. *

- * This locale is on a schema basis and will be used as the default locale for - * {@link com.networknt.schema.ExecutionConfig}. - * - * @return The locale. + * This is deprecated. Either set this using the builder + * SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build() or + * this should be set via + * executionContext.getExecutionConfig().setFormatAssertionsEnabled(true). + * + * @param formatAssertionsEnabled the format assertions enabled flag */ - public Locale getLocale() { - if (this.locale == null) { - // This should not be cached as it can be changed using Locale#setDefault(Locale) - return Locale.getDefault(); - } - return this.locale; + @Deprecated + public void setFormatAssertionsEnabled(Boolean formatAssertionsEnabled) { + this.formatAssertionsEnabled = formatAssertionsEnabled; + } + + public void setHandleNullableField(boolean handleNullableField) { + this.nullableKeywordEnabled = handleNullableField; + } + + public void setJavaSemantics(boolean javaSemantics) { + this.javaSemantics = javaSemantics; } /** @@ -610,23 +639,20 @@ public Locale getLocale() { * Note that this locale is set on a schema basis. To configure the schema on a * per execution basis use * {@link com.networknt.schema.ExecutionConfig#setLocale(Locale)}. + *

+ * This is deprecated. Either set this using the builder + * SchemaValidatorsConfig.builder().locale(locale).build() or this should be set + * via executionContext.getExecutionConfig().setLocale(locale). * * @param locale The locale. */ + @Deprecated public void setLocale(Locale locale) { this.locale = locale; } - /** - * Get the message source to use for generating localised messages. - * - * @return the message source - */ - public MessageSource getMessageSource() { - if (this.messageSource == null) { - return DefaultMessageSource.getInstance(); - } - return this.messageSource; + public void setLosslessNarrowing(boolean losslessNarrowing) { + this.losslessNarrowing = losslessNarrowing; } /** @@ -639,36 +665,85 @@ public void setMessageSource(MessageSource messageSource) { } /** - * Gets the format assertion enabled flag. - *

- * This defaults to null meaning that it will follow the defaults of the - * specification. - *

- * Since draft 2019-09 this will default to false unless enabled by using the - * $vocabulary keyword. + * When enabled, the validation of anyOf and allOf in + * polymorphism will respect OpenAPI 3 style discriminators as described in the + * OpenAPI + * 3.0.3 spec. The presence of a discriminator configuration on the schema + * will lead to the following changes in the behavior: + *

    + *
  • for oneOf the spec is unfortunately very vague. Whether + * oneOf semantics should be affected by discriminators or not is + * not even 100% clear within the members of the OAS steering committee. + * Therefore oneOf at the moment ignores discriminators
  • + *
  • for anyOf the validation will choose one of the candidate + * schemas for validation based on the discriminator property value and will + * pass validation when this specific schema passes. This is in particular + * useful when the payload could match multiple candidates in the + * anyOf list and could lead to ambiguity. Example: type B has all + * mandatory properties of A and adds more mandatory ones. Whether the payload + * is an A or B is determined via the discriminator property name. A payload + * indicating it is an instance of B then requires passing the validation of B + * and passing the validation of A would not be sufficient anymore.
  • + *
  • for allOf use cases with discriminators defined on the + * copied-in parent type, it is possible to automatically validate against a + * subtype. Example: some schema specifies that there is a field of type A. A + * carries a discriminator field and B inherits from A. Then B is automatically + * a candidate for validation as well and will be chosen in case the + * discriminator property matches
  • + *
* - * @return the format assertions enabled flag + * @param openAPI3StyleDiscriminators whether or not discriminators should be + * used. Defaults to false + * @since 1.0.51 */ - public Boolean getFormatAssertionsEnabled() { - return formatAssertionsEnabled; + public void setOpenAPI3StyleDiscriminators(boolean openAPI3StyleDiscriminators) { + this.discriminatorKeywordEnabled = openAPI3StyleDiscriminators; } /** - * Sets the format assertion enabled flag. - * - * @param formatAssertionsEnabled the format assertions enabled flag + * Set the approach used to generate paths in messages, logs and errors (default is PathType.LEGACY). + * + * @param pathType The path generation approach. */ - public void setFormatAssertionsEnabled(Boolean formatAssertionsEnabled) { - this.formatAssertionsEnabled = formatAssertionsEnabled; + public void setPathType(PathType pathType) { + this.pathType = pathType; } /** - * Gets the schema id validator to validate $id. + * Sets if the schema should be preloaded. * - * @return the validator + * @param preloadJsonSchema true to preload */ - public JsonSchemaIdValidator getSchemaIdValidator() { - return schemaIdValidator; + public void setPreloadJsonSchema(boolean preloadJsonSchema) { + this.preloadJsonSchema = preloadJsonSchema; + } + + /** + * Sets the max depth of the evaluation path to preload when preloading refs. + * + * @param preloadJsonSchemaRefMaxNestingDepth the max depth to preload + */ + public void setPreloadJsonSchemaRefMaxNestingDepth(int preloadJsonSchemaRefMaxNestingDepth) { + this.preloadJsonSchemaRefMaxNestingDepth = preloadJsonSchemaRefMaxNestingDepth; + } + + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + } + + /** + * Sets the regular expression factory. + *

+ * This defaults to the JDKRegularExpressionFactory and the implementations + * require inclusion of optional org.jruby.joni:joni or org.graalvm.js:js dependencies. + * + * @see JDKRegularExpressionFactory + * @see ECMAScriptRegularExpressionFactory + * @param regularExpressionFactory the factory + */ + public void setRegularExpressionFactory(RegularExpressionFactory regularExpressionFactory) { + this.regularExpressionFactory = regularExpressionFactory; } /** @@ -681,63 +756,646 @@ public void setSchemaIdValidator(JsonSchemaIdValidator schemaIdValidator) { } /** - * Gets if the schema should be preloaded. + * Alters the strictness of validations for a specific keyword. When set to + * {@literal true}, instructs the keyword's validators to perform strict + * validation. Otherwise, a validator may perform a more permissive check. * - * @return true if it should be preloaded + * @param keyword The keyword to adjust (not null) + * @param strict Whether to perform strict validations */ - public boolean isPreloadJsonSchema() { - return preloadJsonSchema; + public void setStrict(String keyword, boolean strict) { + this.strictness.put(Objects.requireNonNull(keyword, "keyword cannot be null"), strict); + } + + public void setTypeLoose(boolean typeLoose) { + this.typeLoose = typeLoose; + } + + public void setWriteOnly(boolean writeOnly) { + this.writeOnly = writeOnly; + } + + public static Builder builder() { + return new Builder(); + } + + public static Builder builder(SchemaValidatorsConfig config) { + Builder builder = new Builder(); + builder.applyDefaultsStrategy = config.applyDefaultsStrategy; + builder.cacheRefs = config.cacheRefs; + builder.errorMessageKeyword = config.errorMessageKeyword; + builder.executionContextCustomizer = config.executionContextCustomizer; + builder.failFast = config.failFast; + builder.formatAssertionsEnabled = config.formatAssertionsEnabled; + builder.nullableKeywordEnabled = config.nullableKeywordEnabled; + builder.itemWalkListeners = config.itemWalkListeners; + builder.javaSemantics = config.javaSemantics; + builder.keywordWalkListeners = config.keywordWalkListenersMap; + builder.locale = config.locale; + builder.losslessNarrowing = config.losslessNarrowing; + builder.messageSource = config.messageSource; + builder.discriminatorKeywordEnabled = config.discriminatorKeywordEnabled; + builder.pathType = config.pathType; + builder.preloadJsonSchema = config.preloadJsonSchema; + builder.preloadJsonSchemaRefMaxNestingDepth = config.preloadJsonSchemaRefMaxNestingDepth; + builder.propertyWalkListeners = config.propertyWalkListeners; + builder.readOnly = config.readOnly; + builder.regularExpressionFactory = config.regularExpressionFactory; + builder.schemaIdValidator = config.schemaIdValidator; + builder.strictness = config.strictness; + builder.typeLoose = config.typeLoose; + builder.writeOnly = config.writeOnly; + return builder; } /** - * Sets if the schema should be preloaded. - * - * @param preloadJsonSchema true to preload + * Builder for {@link SchemaValidatorsConfig}. */ - public void setPreloadJsonSchema(boolean preloadJsonSchema) { - this.preloadJsonSchema = preloadJsonSchema; + public static class Builder { + private ApplyDefaultsStrategy applyDefaultsStrategy = ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY; + private boolean cacheRefs = true; + private String errorMessageKeyword = null; + private ExecutionContextCustomizer executionContextCustomizer = null; + private boolean failFast = false; + private Boolean formatAssertionsEnabled = false; + private boolean nullableKeywordEnabled = false; + private List itemWalkListeners = new ArrayList<>(); + private boolean javaSemantics = false; + private Map> keywordWalkListeners = new HashMap<>(); + private Locale locale = null; // This must be null to use Locale.getDefault() as the default can be changed + private boolean losslessNarrowing = false; + private MessageSource messageSource = null; + private boolean discriminatorKeywordEnabled = false; + private PathType pathType = PathType.JSON_POINTER; + private boolean preloadJsonSchema = true; + private int preloadJsonSchemaRefMaxNestingDepth = DEFAULT_PRELOAD_JSON_SCHEMA_REF_MAX_NESTING_DEPTH; + private List propertyWalkListeners = new ArrayList<>(); + private Boolean readOnly = null; + private RegularExpressionFactory regularExpressionFactory = JDKRegularExpressionFactory.getInstance(); + private JsonSchemaIdValidator schemaIdValidator = JsonSchemaIdValidator.DEFAULT; + private Map strictness = new HashMap<>(0); + private boolean typeLoose = false; + private Boolean writeOnly = null; + + /** + * Sets the strategy the walker uses to sets nodes to the default value. + *

+ * Defaults to {@link ApplyDefaultsStrategy#EMPTY_APPLY_DEFAULTS_STRATEGY}. + * + * @param applyDefaultsStrategy the strategy + * @return the builder + */ + public Builder applyDefaultsStrategy(ApplyDefaultsStrategy applyDefaultsStrategy) { + this.applyDefaultsStrategy = applyDefaultsStrategy != null ? applyDefaultsStrategy + : ApplyDefaultsStrategy.EMPTY_APPLY_DEFAULTS_STRATEGY; + return this; + } + /** + * Sets if schemas loaded from refs will be cached and reused for subsequent runs. + *

+ * Defaults to true. + * + * @param cacheRefs true to cache + * @return the builder + */ + public Builder cacheRefs(boolean cacheRefs) { + this.cacheRefs = cacheRefs; + return this; + } + /** + * Sets the error message keyword for setting custom messages in the schema. + *

+ * Defaults to null meaning custom messages are not enabled. + * + * @param errorMessageKeyword to use for custom messages in the schema + * @return the builder + */ + public Builder errorMessageKeyword(String errorMessageKeyword) { + this.errorMessageKeyword = errorMessageKeyword; + return this; + } + /** + * Sets the execution context customizer that is run before each run. + * + * @param executionContextCustomizer the customizer + * @return the builder + */ + public Builder executionContextCustomizer(ExecutionContextCustomizer executionContextCustomizer) { + this.executionContextCustomizer = executionContextCustomizer; + return this; + } + + /** + * Sets if the validation should immediately return once a validation error has + * occurred. This can improve performance if inputs are invalid but cannot + * return all error messages to the caller. + *

+ * Defaults to false. + * + * @param failFast true to enable + * @return the builder + */ + public Builder failFast(boolean failFast) { + this.failFast = failFast; + return this; + } + + /** + * Sets if format assertions are enabled. If format assertions are not enabled + * the format keyword will behave like a annotation and not attempt to validate + * if the inputs are valid. + *

+ * Defaults to not enabling format assertions for Draft 2019-09 and above and + * enabling format assertions for Draft 7 and below. + * + * @param formatAssertionsEnabled true to enable + * @return the builder + */ + public Builder formatAssertionsEnabled(Boolean formatAssertionsEnabled) { + this.formatAssertionsEnabled = formatAssertionsEnabled; + return this; + } + + /** + * Sets if the nullable keyword is enabled. + * + * @param nullableKeywordEnabled true to enable + * @return the builder + */ + public Builder nullableKeywordEnabled(boolean nullableKeywordEnabled) { + this.nullableKeywordEnabled = nullableKeywordEnabled; + return this; + } + public Builder itemWalkListeners(List itemWalkListeners) { + this.itemWalkListeners = itemWalkListeners; + return this; + } + public Builder javaSemantics(boolean javaSemantics) { + this.javaSemantics = javaSemantics; + return this; + } + public Builder keywordWalkListeners(Map> keywordWalkListeners) { + this.keywordWalkListeners = keywordWalkListeners; + return this; + } + /** + * Set the locale to consider when generating localised messages. + *

+ * Note that this locale is set on a schema basis. To configure the schema on a + * per execution basis use + * {@link com.networknt.schema.ExecutionConfig#setLocale(Locale)}. + *

+ * Defaults to use {@link Locale#getDefault()}. + * + * @param locale The locale. + */ + public Builder locale(Locale locale) { + this.locale = locale; + return this; + } + public Builder losslessNarrowing(boolean losslessNarrowing) { + this.losslessNarrowing = losslessNarrowing; + return this; + } + /** + * Sets the message source to use for generating localised messages. + * + * @param messageSource the message source + * @return the builder + */ + public Builder messageSource(MessageSource messageSource) { + this.messageSource = messageSource; + return this; + } + /** + * Sets if the discriminator keyword is enabled. + *

+ * Defaults to false. + * + * @param discriminatorKeywordEnabled true to enable + * @return the builder + */ + public Builder discriminatorKeywordEnabled(boolean discriminatorKeywordEnabled) { + this.discriminatorKeywordEnabled = discriminatorKeywordEnabled; + return this; + } + /** + * Sets the path type to use when reporting the instance location of errors. + *

+ * Defaults to {@link PathType#JSON_POINTER}. + * + * @param pathType the path type + * @return the path type + */ + public Builder pathType(PathType pathType) { + this.pathType = pathType; + return this; + } + /** + * Sets if the schema should be preloaded. + *

+ * Defaults to true. + * + * @param preloadJsonSchema true to preload + * @return the builder + */ + public Builder preloadJsonSchema(boolean preloadJsonSchema) { + this.preloadJsonSchema = preloadJsonSchema; + return this; + } + /** + * Sets the max depth of the evaluation path to preload when preloading refs. + *

+ * Defaults to 40. + * + * @param preloadJsonSchemaRefMaxNestingDepth to preload + * @return the builder + */ + public Builder preloadJsonSchemaRefMaxNestingDepth(int preloadJsonSchemaRefMaxNestingDepth) { + this.preloadJsonSchemaRefMaxNestingDepth = preloadJsonSchemaRefMaxNestingDepth; + return this; + } + public Builder propertyWalkListeners(List propertyWalkListeners) { + this.propertyWalkListeners = propertyWalkListeners; + return this; + } + public Builder readOnly(Boolean readOnly) { + this.readOnly = readOnly; + return this; + } + /** + * Sets the regular expression factory. + *

+ * Defaults to the {@link JDKRegularExpressionFactory} + *

+ * The {@link ECMAScriptRegularExpressionFactory} requires the inclusion of + * optional org.jruby.joni:joni or org.graalvm.js:js dependencies. + * + * @see JDKRegularExpressionFactory + * @see ECMAScriptRegularExpressionFactory + * @param regularExpressionFactory the factory + * @return the builder + */ + public Builder regularExpressionFactory(RegularExpressionFactory regularExpressionFactory) { + this.regularExpressionFactory = regularExpressionFactory; + return this; + } + /** + * Sets the schema id validator to use. + *

+ * Defaults to {@link JsonSchemaIdValidator#DEFAULT}. + * + * @param schemaIdValidator the builder + * @return the builder + */ + public Builder schemaIdValidator(JsonSchemaIdValidator schemaIdValidator) { + this.schemaIdValidator = schemaIdValidator; + return this; + } + public Builder strict(Map strict) { + this.strictness = strict; + return this; + } + public Builder typeLoose(boolean typeLoose) { + this.typeLoose = typeLoose; + return this; + } + public Builder writeOnly(Boolean writeOnly) { + this.writeOnly = writeOnly; + return this; + } + public SchemaValidatorsConfig build() { + return new ImmutableSchemaValidatorsConfig(applyDefaultsStrategy, cacheRefs, errorMessageKeyword, + executionContextCustomizer, failFast, formatAssertionsEnabled, nullableKeywordEnabled, + itemWalkListeners, javaSemantics, keywordWalkListeners, locale, losslessNarrowing, messageSource, + discriminatorKeywordEnabled, pathType, preloadJsonSchema, preloadJsonSchemaRefMaxNestingDepth, + propertyWalkListeners, readOnly, regularExpressionFactory, schemaIdValidator, strictness, typeLoose, + writeOnly); + } + public Builder strict(String keyword, boolean strict) { + this.strictness.put(Objects.requireNonNull(keyword, "keyword cannot be null"), strict); + return this; + } + public Builder keywordWalkListener(String keyword, JsonSchemaWalkListener keywordWalkListener) { + this.keywordWalkListeners.computeIfAbsent(keyword, key -> new ArrayList<>()).add(keywordWalkListener); + return this; + } + public Builder keywordWalkListener(JsonSchemaWalkListener keywordWalkListener) { + return keywordWalkListener(ALL_KEYWORD_WALK_LISTENER_KEY, keywordWalkListener); + } + public Builder keywordWalkListeners(Consumer>> keywordWalkListeners) { + keywordWalkListeners.accept(this.keywordWalkListeners); + return this; + } + public Builder propertyWalkListener(JsonSchemaWalkListener propertyWalkListener) { + this.propertyWalkListeners.add(propertyWalkListener); + return this; + } + public Builder propertyWalkListeners(Consumer> propertyWalkListeners) { + propertyWalkListeners.accept(this.propertyWalkListeners); + return this; + } + public Builder itemWalkListener(JsonSchemaWalkListener itemWalkListener) { + this.itemWalkListeners.add(itemWalkListener); + return this; + } + public Builder itemWalkListeners(Consumer> itemWalkListeners) { + itemWalkListeners.accept(this.itemWalkListeners); + return this; + } } /** - * Gets the max depth of the evaluation path to preload when preloading refs. - * - * @return the max depth to preload + * {@link SchemaValidatorsConfig} that throws on mutators or deprecated methods. + *

+ * The {@link SchemaValidatorsConfig} will be made immutable in a future breaking release. */ - public int getPreloadJsonSchemaRefMaxNestingDepth() { - return preloadJsonSchemaRefMaxNestingDepth; + public static class ImmutableSchemaValidatorsConfig extends SchemaValidatorsConfig { + public ImmutableSchemaValidatorsConfig(ApplyDefaultsStrategy applyDefaultsStrategy, boolean cacheRefs, + String errorMessageKeyword, ExecutionContextCustomizer executionContextCustomizer, boolean failFast, + Boolean formatAssertionsEnabled, boolean handleNullableField, + List itemWalkListeners, boolean javaSemantics, + Map> keywordWalkListenersMap, Locale locale, + boolean losslessNarrowing, MessageSource messageSource, boolean openAPI3StyleDiscriminators, + PathType pathType, boolean preloadJsonSchema, int preloadJsonSchemaRefMaxNestingDepth, + List propertyWalkListeners, Boolean readOnly, + RegularExpressionFactory regularExpressionFactory, JsonSchemaIdValidator schemaIdValidator, + Map strictness, boolean typeLoose, Boolean writeOnly) { + super(applyDefaultsStrategy, cacheRefs, errorMessageKeyword, executionContextCustomizer, failFast, + formatAssertionsEnabled, handleNullableField, itemWalkListeners, javaSemantics, keywordWalkListenersMap, locale, + losslessNarrowing, messageSource, openAPI3StyleDiscriminators, pathType, preloadJsonSchema, + preloadJsonSchemaRefMaxNestingDepth, propertyWalkListeners, readOnly, regularExpressionFactory, + schemaIdValidator, strictness, typeLoose, writeOnly); + } + + @Override + public void addItemWalkListener(JsonSchemaWalkListener itemWalkListener) { + throw new UnsupportedOperationException(); + } + + @Override + public void addItemWalkListeners(List itemWalkListeners) { + throw new UnsupportedOperationException(); + } + + @Override + public void addKeywordWalkListener(JsonSchemaWalkListener keywordWalkListener) { + throw new UnsupportedOperationException(); + } + + @Override + public void addKeywordWalkListener(String keyword, JsonSchemaWalkListener keywordWalkListener) { + throw new UnsupportedOperationException(); + } + + @Override + public void addKeywordWalkListeners(List keywordWalkListeners) { + throw new UnsupportedOperationException(); + } + + @Override + public void addKeywordWalkListeners(String keyword, List keywordWalkListeners) { + throw new UnsupportedOperationException(); + } + + @Override + public void addPropertyWalkListener(JsonSchemaWalkListener propertyWalkListener) { + throw new UnsupportedOperationException(); + } + + @Override + public void addPropertyWalkListeners(List propertyWalkListeners) { + throw new UnsupportedOperationException(); + } + + @Override + public void setApplyDefaultsStrategy(ApplyDefaultsStrategy applyDefaultsStrategy) { + throw new UnsupportedOperationException(); + } + + @Override + public void setCacheRefs(boolean cacheRefs) { + throw new UnsupportedOperationException(); + } + + @Override + public void setCustomMessageSupported(boolean customMessageSupported) { + throw new UnsupportedOperationException(); + } + + @Override + public void setEcma262Validator(boolean ecma262Validator) { + throw new UnsupportedOperationException(); + } + + @Override + public void setExecutionContextCustomizer(ExecutionContextCustomizer executionContextCustomizer) { + throw new UnsupportedOperationException(); + } + + @Override + public void setFailFast(boolean failFast) { + throw new UnsupportedOperationException(); + } + + @Override + public void setFormatAssertionsEnabled(Boolean formatAssertionsEnabled) { + throw new UnsupportedOperationException(); + } + + @Override + public void setHandleNullableField(boolean handleNullableField) { + throw new UnsupportedOperationException(); + } + + @Override + public void setJavaSemantics(boolean javaSemantics) { + throw new UnsupportedOperationException(); + } + + @Override + public void setLocale(Locale locale) { + throw new UnsupportedOperationException(); + } + + @Override + public void setLosslessNarrowing(boolean losslessNarrowing) { + throw new UnsupportedOperationException(); + } + + @Override + public void setMessageSource(MessageSource messageSource) { + throw new UnsupportedOperationException(); + } + + @Override + public void setOpenAPI3StyleDiscriminators(boolean openAPI3StyleDiscriminators) { + throw new UnsupportedOperationException(); + } + + @Override + public void setPathType(PathType pathType) { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreloadJsonSchema(boolean preloadJsonSchema) { + throw new UnsupportedOperationException(); + } + + @Override + public void setPreloadJsonSchemaRefMaxNestingDepth(int preloadJsonSchemaRefMaxNestingDepth) { + throw new UnsupportedOperationException(); + } + + @Override + public void setReadOnly(boolean readOnly) { + throw new UnsupportedOperationException(); + } + + @Override + public void setRegularExpressionFactory(RegularExpressionFactory regularExpressionFactory) { + throw new UnsupportedOperationException(); + } + + @Override + public void setSchemaIdValidator(JsonSchemaIdValidator schemaIdValidator) { + throw new UnsupportedOperationException(); + } + + @Override + public void setStrict(String keyword, boolean strict) { + throw new UnsupportedOperationException(); + } + + @Override + public void setTypeLoose(boolean typeLoose) { + throw new UnsupportedOperationException(); + } + + @Override + public void setWriteOnly(boolean writeOnly) { + throw new UnsupportedOperationException(); + } + + @Override + public void setLoadCollectors(boolean loadCollectors) { + throw new UnsupportedOperationException(); + } + + @Override + public SchemaValidatorsConfig disableUnevaluatedAnalysis() { + throw new UnsupportedOperationException(); + } + + @Override + public SchemaValidatorsConfig disableUnevaluatedItems() { + throw new UnsupportedOperationException(); + } + + @Override + public SchemaValidatorsConfig disableUnevaluatedProperties() { + throw new UnsupportedOperationException(); + } + + @Override + public SchemaValidatorsConfig enableUnevaluatedAnalysis() { + throw new UnsupportedOperationException(); + } + + @Override + public SchemaValidatorsConfig enableUnevaluatedItems() { + throw new UnsupportedOperationException(); + } + + @Override + public SchemaValidatorsConfig enableUnevaluatedProperties() { + throw new UnsupportedOperationException(); + } } + /* Below are deprecated for removal */ + @Deprecated + private boolean loadCollectors = true; + /** - * Sets the max depth of the evaluation path to preload when preloading refs. - * - * @param preloadJsonSchemaRefMaxNestingDepth the max depth to preload + * Use {@code isReadOnly} or {@code isWriteOnly} + * @return true if schema is used to write data */ - public void setPreloadJsonSchemaRefMaxNestingDepth(int preloadJsonSchemaRefMaxNestingDepth) { - this.preloadJsonSchemaRefMaxNestingDepth = preloadJsonSchemaRefMaxNestingDepth; + @Deprecated + public boolean isWriteMode() { + return null == this.writeOnly || this.writeOnly; } - + /** - * Gets if schemas loaded from refs will be cached and reused for subsequent - * runs. - * - * @return true if schemas loaded from refs should be cached + * Sets if collectors are to be loaded. + *

+ * This is deprecated in favor of the caller calling {@link CollectorContext#loadCollectors()} manually. + * + * @param loadCollectors to load collectors */ - public boolean isCacheRefs() { - return cacheRefs; + @Deprecated + public void setLoadCollectors(boolean loadCollectors) { + this.loadCollectors = loadCollectors; } /** - * Sets if schemas loaded from refs will be cached and reused for subsequent - * runs. - *

- * Note that setting this to false will affect performance as refs will need to - * be repeatedly resolved for each evaluation run. It may be needed to be set to - * false if there are multiple nested applicators like anyOf, oneOf and allOf as - * that will consume a lot of memory to cache all the permutations. + * Gets if collectors are to be loaded. * - * @param cacheRefs true to cache + * @return if collectors are to be loader */ - public void setCacheRefs(boolean cacheRefs) { - this.cacheRefs = cacheRefs; + @Deprecated + public boolean doLoadCollectors() { + return this.loadCollectors; + } + + @Deprecated + public SchemaValidatorsConfig disableUnevaluatedAnalysis() { + return this; + } + + @Deprecated + public SchemaValidatorsConfig disableUnevaluatedItems() { + return this; + } + + @Deprecated + public SchemaValidatorsConfig disableUnevaluatedProperties() { + return this; + } + + @Deprecated + public SchemaValidatorsConfig enableUnevaluatedAnalysis() { + return this; + } + + @Deprecated + public SchemaValidatorsConfig enableUnevaluatedItems() { + return this; + } + + @Deprecated + public SchemaValidatorsConfig enableUnevaluatedProperties() { + return this; + } + + @Deprecated + public boolean isUnevaluatedItemsAnalysisDisabled() { + return false; + } + + @Deprecated + public boolean isUnevaluatedItemsAnalysisEnabled() { + return !isUnevaluatedItemsAnalysisDisabled(); + } + + @Deprecated + public boolean isUnevaluatedPropertiesAnalysisDisabled() { + return false; + } + + @Deprecated + public boolean isUnevaluatedPropertiesAnalysisEnabled() { + return !isUnevaluatedPropertiesAnalysisDisabled(); } } diff --git a/src/main/java/com/networknt/schema/ValidationContext.java b/src/main/java/com/networknt/schema/ValidationContext.java index 8572a9a9d..f4d6e12f7 100644 --- a/src/main/java/com/networknt/schema/ValidationContext.java +++ b/src/main/java/com/networknt/schema/ValidationContext.java @@ -47,6 +47,7 @@ public ValidationContext(JsonMetaSchema metaSchema, JsonSchemaFactory jsonSchema } this.metaSchema = metaSchema; this.jsonSchemaFactory = jsonSchemaFactory; + // The deprecated SchemaValidatorsConfig constructor needs to remain until removed this.config = config == null ? new SchemaValidatorsConfig() : config; this.schemaReferences = schemaReferences; this.schemaResources = schemaResources; diff --git a/src/main/java/com/networknt/schema/ValidationMessage.java b/src/main/java/com/networknt/schema/ValidationMessage.java index 41850940d..aa5f73ec8 100644 --- a/src/main/java/com/networknt/schema/ValidationMessage.java +++ b/src/main/java/com/networknt/schema/ValidationMessage.java @@ -39,7 +39,7 @@ * "https://github.com/json-schema-org/json-schema-spec/blob/main/jsonschema-validation-output-machines.md">JSON * Schema */ -@JsonIgnoreProperties({ "messageSupplier", "schemaNode", "instanceNode", "valid" }) +@JsonIgnoreProperties({ "messageSupplier", "schemaNode", "instanceNode", "valid", "error" }) @JsonPropertyOrder({ "type", "code", "message", "instanceLocation", "property", "evaluationPath", "schemaLocation", "messageKey", "arguments", "details" }) @JsonInclude(Include.NON_NULL) @@ -158,6 +158,11 @@ public Map getDetails() { return details; } + /** + * Gets the formatted error message. + * + * @return the error message + */ public String getMessage() { return messageSupplier.get(); } @@ -170,6 +175,28 @@ public boolean isValid() { return messageSupplier != null; } + /** + * Gets the error. + * + * @return the error + */ + public String getError() { + String message = getMessage(); + int index = message.indexOf(':'); + if (index != -1) { + int length = message.length(); + while (index + 1 < length) { + if (message.charAt(index + 1) == ' ') { + index++; + } else { + break; + } + } + return message.substring(index + 1); + } + return message; + } + @Override public String toString() { return messageSupplier.get(); diff --git a/src/main/java/com/networknt/schema/ValidationMessageHandler.java b/src/main/java/com/networknt/schema/ValidationMessageHandler.java index 1183f4ada..c0ca43d34 100644 --- a/src/main/java/com/networknt/schema/ValidationMessageHandler.java +++ b/src/main/java/com/networknt/schema/ValidationMessageHandler.java @@ -14,7 +14,7 @@ */ public abstract class ValidationMessageHandler { protected final ErrorMessageType errorMessageType; - protected final boolean customErrorMessagesEnabled; + protected final String errorMessageKeyword; protected final MessageSource messageSource; protected final Keyword keyword; protected final JsonSchema parentSchema; @@ -23,7 +23,7 @@ public abstract class ValidationMessageHandler { protected final JsonSchema evaluationParentSchema; protected final Map errorMessage; - protected ValidationMessageHandler(ErrorMessageType errorMessageType, boolean customErrorMessagesEnabled, + protected ValidationMessageHandler(ErrorMessageType errorMessageType, String errorMessageKeyword, MessageSource messageSource, Keyword keyword, JsonSchema parentSchema, SchemaLocation schemaLocation, JsonNodePath evaluationPath) { ErrorMessageType currentErrorMessageType = errorMessageType; @@ -32,14 +32,15 @@ protected ValidationMessageHandler(ErrorMessageType errorMessageType, boolean cu this.evaluationPath = Objects.requireNonNull(evaluationPath); this.parentSchema = parentSchema; this.evaluationParentSchema = null; - this.customErrorMessagesEnabled = customErrorMessagesEnabled; + this.errorMessageKeyword = errorMessageKeyword; this.keyword = keyword; Map currentErrorMessage = null; if (this.keyword != null) { - if (this.customErrorMessagesEnabled && keyword != null && parentSchema != null) { - currentErrorMessage = getErrorMessage(parentSchema.getSchemaNode(), keyword.getValue()); + if (this.errorMessageKeyword != null && keyword != null && parentSchema != null) { + currentErrorMessage = getErrorMessage(this.errorMessageKeyword, parentSchema.getSchemaNode(), + keyword.getValue()); } String errorCodeKey = getErrorCodeKey(keyword.getValue()); if (errorCodeKey != null && this.parentSchema != null) { @@ -60,7 +61,7 @@ protected ValidationMessageHandler(ErrorMessageType errorMessageType, boolean cu * Constructor to create a copy using fields. * * @param errorMessageType the error message type - * @param customErrorMessagesEnabled whether custom error msessages are enabled + * @param errorMessageKeyword the error message keyword * @param messageSource the message source * @param keyword the keyword * @param parentSchema the parent schema @@ -69,11 +70,11 @@ protected ValidationMessageHandler(ErrorMessageType errorMessageType, boolean cu * @param evaluationParentSchema the evaluation parent schema * @param errorMessage the error message */ - protected ValidationMessageHandler(ErrorMessageType errorMessageType, boolean customErrorMessagesEnabled, + protected ValidationMessageHandler(ErrorMessageType errorMessageType, String errorMessageKeyword, MessageSource messageSource, Keyword keyword, JsonSchema parentSchema, SchemaLocation schemaLocation, JsonNodePath evaluationPath, JsonSchema evaluationParentSchema, Map errorMessage) { this.errorMessageType = errorMessageType; - this.customErrorMessagesEnabled = customErrorMessagesEnabled; + this.errorMessageKeyword = errorMessageKeyword; this.messageSource = messageSource; this.keyword = keyword; this.parentSchema = parentSchema; @@ -104,9 +105,9 @@ protected ErrorMessageType getErrorMessageType() { * @param keyword the keyword * @return the custom error message */ - protected Map getErrorMessage(JsonNode schemaNode, String keyword) { + protected Map getErrorMessage(String errorMessageKeyword, JsonNode schemaNode, String keyword) { final JsonSchema parentSchema = this.parentSchema; - final JsonNode message = getMessageNode(schemaNode, parentSchema, keyword); + final JsonNode message = getMessageNode(errorMessageKeyword, schemaNode, parentSchema, keyword); if (message != null) { JsonNode messageNode = message.get(keyword); if (messageNode != null) { @@ -126,16 +127,18 @@ protected Map getErrorMessage(JsonNode schemaNode, String keywor return Collections.emptyMap(); } - protected JsonNode getMessageNode(JsonNode schemaNode, JsonSchema parentSchema, String pname) { - if (schemaNode.get("message") != null && schemaNode.get("message").get(pname) != null) { - return schemaNode.get("message"); + protected JsonNode getMessageNode(String errorMessageKeyword, JsonNode schemaNode, JsonSchema parentSchema, + String pname) { + if (schemaNode.get(errorMessageKeyword) != null && schemaNode.get(errorMessageKeyword).get(pname) != null) { + return schemaNode.get(errorMessageKeyword); } JsonNode messageNode; - messageNode = schemaNode.get("message"); + messageNode = schemaNode.get(errorMessageKeyword); if (messageNode == null && parentSchema != null) { - messageNode = parentSchema.schemaNode.get("message"); + messageNode = parentSchema.schemaNode.get(errorMessageKeyword); if (messageNode == null) { - return getMessageNode(parentSchema.schemaNode, parentSchema.getParentSchema(), pname); + return getMessageNode(errorMessageKeyword, parentSchema.schemaNode, parentSchema.getParentSchema(), + pname); } } return messageNode; diff --git a/src/main/java/com/networknt/schema/output/OutputUnitData.java b/src/main/java/com/networknt/schema/output/OutputUnitData.java index 7489c8f82..8867f71c2 100644 --- a/src/main/java/com/networknt/schema/output/OutputUnitData.java +++ b/src/main/java/com/networknt/schema/output/OutputUnitData.java @@ -57,7 +57,7 @@ public static String formatAssertion(ValidationMessage validationMessage) { } public static String formatMessage(String message) { - int index = message.indexOf(":"); + int index = message.indexOf(':'); if (index != -1) { int length = message.length(); while (index + 1 < length) { diff --git a/src/main/java/com/networknt/schema/utils/JsonNodeUtil.java b/src/main/java/com/networknt/schema/utils/JsonNodeUtil.java index 793856e95..07c3b9151 100644 --- a/src/main/java/com/networknt/schema/utils/JsonNodeUtil.java +++ b/src/main/java/com/networknt/schema/utils/JsonNodeUtil.java @@ -63,7 +63,7 @@ public static boolean isNodeNullable(JsonNode schema){ //Check to see if a JsonNode is nullable with checking the isHandleNullableField public static boolean isNodeNullable(JsonNode schema, SchemaValidatorsConfig config){ // check if the parent schema declares the fields as nullable - if (config.isHandleNullableField()) { + if (config.isNullableKeywordEnabled()) { return isNodeNullable(schema); } return false; diff --git a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java index cc066e93b..2b0577f54 100644 --- a/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java +++ b/src/test/java/com/networknt/schema/AbstractJsonSchemaTestSuite.java @@ -200,27 +200,27 @@ private DynamicNode buildTest(JsonSchemaFactory validatorFactory, TestSpec testS // if test file do not contains typeLoose flag, use default value: false. @SuppressWarnings("deprecation") boolean typeLoose = testSpec.isTypeLoose(); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setTypeLoose(typeLoose); - config.setRegularExpressionFactory( + SchemaValidatorsConfig.Builder configBuilder = SchemaValidatorsConfig.builder(); + configBuilder.typeLoose(typeLoose); + configBuilder.regularExpressionFactory( TestSpec.RegexKind.JDK == testSpec.getRegex() ? JDKRegularExpressionFactory.getInstance() : JoniRegularExpressionFactory.getInstance()); - testSpec.getStrictness().forEach(config::setStrict); + testSpec.getStrictness().forEach(configBuilder::strict); if (testSpec.getConfig() != null) { if (testSpec.getConfig().containsKey("isCustomMessageSupported")) { - config.setCustomMessageSupported((Boolean) testSpec.getConfig().get("isCustomMessageSupported")); + configBuilder.errorMessageKeyword( + (Boolean) testSpec.getConfig().get("isCustomMessageSupported") ? "message" : null); } if (testSpec.getConfig().containsKey("readOnly")) { - config.setReadOnly((Boolean) testSpec.getConfig().get("readOnly")); + configBuilder.readOnly((Boolean) testSpec.getConfig().get("readOnly")); } if (testSpec.getConfig().containsKey("writeOnly")) { - config.setWriteOnly((Boolean) testSpec.getConfig().get("writeOnly")); + configBuilder.writeOnly((Boolean) testSpec.getConfig().get("writeOnly")); } } - SchemaLocation testCaseFileUri = SchemaLocation.of("classpath:" + toForwardSlashPath(testSpec.getTestCase().getSpecification())); - JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testSpec.getTestCase().getSchema(), config); + JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testSpec.getTestCase().getSchema(), configBuilder.build()); return dynamicTest(testSpec.getDescription(), () -> executeAndReset(schema, testSpec)); } diff --git a/src/test/java/com/networknt/schema/AdditionalPropertiesValidatorTest.java b/src/test/java/com/networknt/schema/AdditionalPropertiesValidatorTest.java index c133d83af..0bb20407f 100644 --- a/src/test/java/com/networknt/schema/AdditionalPropertiesValidatorTest.java +++ b/src/test/java/com/networknt/schema/AdditionalPropertiesValidatorTest.java @@ -46,8 +46,7 @@ void messageFalse() { + " \"additionalProperties\": false\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "{\r\n" + " \"foo\":\"hello\",\r\n" @@ -82,8 +81,7 @@ void messageSchema() { + " \"additionalProperties\": { \"type\": \"number\" }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "{\r\n" + " \"foo\":\"hello\",\r\n" diff --git a/src/test/java/com/networknt/schema/CollectorContextTest.java b/src/test/java/com/networknt/schema/CollectorContextTest.java index dae6baf40..a91eb5f74 100644 --- a/src/test/java/com/networknt/schema/CollectorContextTest.java +++ b/src/test/java/com/networknt/schema/CollectorContextTest.java @@ -154,7 +154,7 @@ private void setupSchema() throws Exception { final JsonSchemaFactory schemaFactory = JsonSchemaFactory .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).metaSchema(metaSchema) .build(); - SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder().build(); this.jsonSchema = schemaFactory.getSchema(getSchemaString(), schemaValidatorsConfig); this.jsonSchemaForCombine = schemaFactory.getSchema(getSchemaStringMultipleProperties(), schemaValidatorsConfig); } diff --git a/src/test/java/com/networknt/schema/ConstValidatorTest.java b/src/test/java/com/networknt/schema/ConstValidatorTest.java index 0d22c2d70..f412fd722 100644 --- a/src/test/java/com/networknt/schema/ConstValidatorTest.java +++ b/src/test/java/com/networknt/schema/ConstValidatorTest.java @@ -36,12 +36,13 @@ void localeMessageOthers() { String schemaData = "{\r\n" + " \"const\": \"aa\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setMessageSource(new ResourceBundleMessageSource("const-messages-override", "jsv-messages")); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .messageSource(new ResourceBundleMessageSource("const-messages-override", "jsv-messages")) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); String inputData = "\"bb\""; Set messages = schema.validate(inputData, InputFormat.JSON); - assertEquals("$: must be the constant value 'aa' but is 'bb'", messages.iterator().next().getMessage()); + assertEquals(": must be the constant value 'aa' but is 'bb'", messages.iterator().next().getMessage()); } @Test @@ -49,12 +50,13 @@ void localeMessageNumber() { String schemaData = "{\r\n" + " \"const\": 1\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setMessageSource(new ResourceBundleMessageSource("const-messages-override", "jsv-messages")); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .messageSource(new ResourceBundleMessageSource("const-messages-override", "jsv-messages")) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); String inputData = "2"; Set messages = schema.validate(inputData, InputFormat.JSON); - assertEquals("$: must be the constant value '1' but is '2'", messages.iterator().next().getMessage()); + assertEquals(": must be the constant value '1' but is '2'", messages.iterator().next().getMessage()); } @Test @@ -62,7 +64,7 @@ void validOthers() { String schemaData = "{\r\n" + " \"const\": \"aa\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); String inputData = "\"aa\""; Set messages = schema.validate(inputData, InputFormat.JSON); @@ -74,7 +76,7 @@ void validNumber() { String schemaData = "{\r\n" + " \"const\": 1234.56789\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); String inputData = "1234.56789"; Set messages = schema.validate(inputData, InputFormat.JSON); @@ -86,7 +88,7 @@ void invalidNumber() { String schemaData = "{\r\n" + " \"const\": 1234.56789\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); String inputData = "\"1234.56789\""; Set messages = schema.validate(inputData, InputFormat.JSON); diff --git a/src/test/java/com/networknt/schema/ContentSchemaValidatorTest.java b/src/test/java/com/networknt/schema/ContentSchemaValidatorTest.java index d5f4607b2..187f51a9b 100644 --- a/src/test/java/com/networknt/schema/ContentSchemaValidatorTest.java +++ b/src/test/java/com/networknt/schema/ContentSchemaValidatorTest.java @@ -55,8 +55,7 @@ void annotationCollection() throws JsonProcessingException { + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "\"helloworld\""; diff --git a/src/test/java/com/networknt/schema/CyclicDependencyTest.java b/src/test/java/com/networknt/schema/CyclicDependencyTest.java index 8dd294d67..5b6a356db 100644 --- a/src/test/java/com/networknt/schema/CyclicDependencyTest.java +++ b/src/test/java/com/networknt/schema/CyclicDependencyTest.java @@ -32,7 +32,7 @@ public void whenDependencyBetweenSchemaThenValidationSuccessful() throws Excepti " ]\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = schemaFactory.getSchema(SchemaLocation.of("resource:/draft4/issue258/Master.json"), config); assertEquals(0, schema.validate(new ObjectMapper().readTree(jsonObject)).size()); } diff --git a/src/test/java/com/networknt/schema/DefaultJsonSchemaIdValidatorTest.java b/src/test/java/com/networknt/schema/DefaultJsonSchemaIdValidatorTest.java index f8dc64dd9..43b56ecfb 100644 --- a/src/test/java/com/networknt/schema/DefaultJsonSchemaIdValidatorTest.java +++ b/src/test/java/com/networknt/schema/DefaultJsonSchemaIdValidatorTest.java @@ -31,8 +31,9 @@ public class DefaultJsonSchemaIdValidatorTest { void givenRelativeIdShouldThrowInvalidSchemaException() { String schema = "{\r\n" + " \"$id\": \"0\",\r\n" + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setSchemaIdValidator(JsonSchemaIdValidator.DEFAULT); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .schemaIdValidator(JsonSchemaIdValidator.DEFAULT) + .build(); assertThrowsExactly(InvalidSchemaException.class, () -> JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schema, config)); try { @@ -46,8 +47,9 @@ void givenRelativeIdShouldThrowInvalidSchemaException() { void givenFragmentWithNoContextShouldNotThrowInvalidSchemaException() { String schema = "{\r\n" + " \"$id\": \"#0\",\r\n" + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setSchemaIdValidator(JsonSchemaIdValidator.DEFAULT); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .schemaIdValidator(JsonSchemaIdValidator.DEFAULT) + .build(); assertDoesNotThrow(() -> JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schema, config)); } @@ -55,15 +57,17 @@ void givenFragmentWithNoContextShouldNotThrowInvalidSchemaException() { void givenSlashWithNoContextShouldNotThrowInvalidSchemaException() { String schema = "{\r\n" + " \"$id\": \"/base\",\r\n" + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setSchemaIdValidator(JsonSchemaIdValidator.DEFAULT); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .schemaIdValidator(JsonSchemaIdValidator.DEFAULT) + .build(); assertDoesNotThrow(() -> JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schema, config)); } @Test void givenRelativeIdWithClasspathBaseShouldNotThrowInvalidSchemaException() { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setSchemaIdValidator(JsonSchemaIdValidator.DEFAULT); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .schemaIdValidator(JsonSchemaIdValidator.DEFAULT) + .build(); assertDoesNotThrow(() -> JsonSchemaFactory.getInstance(VersionFlag.V202012) .getSchema(SchemaLocation.of("classpath:schema/id-relative.json"), config)); } diff --git a/src/test/java/com/networknt/schema/DiscriminatorValidatorTest.java b/src/test/java/com/networknt/schema/DiscriminatorValidatorTest.java index eb2f92fa8..816aaf0ae 100644 --- a/src/test/java/com/networknt/schema/DiscriminatorValidatorTest.java +++ b/src/test/java/com/networknt/schema/DiscriminatorValidatorTest.java @@ -117,8 +117,7 @@ void discriminatorInArray() { + "]"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setOpenAPI3StyleDiscriminators(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().discriminatorKeywordEnabled(true).build(); JsonSchema schema = factory.getSchema(schemaData, config); Set messages = schema.validate(inputData, InputFormat.JSON); assertTrue(messages.isEmpty()); @@ -150,8 +149,7 @@ void anyOfWithConfigEnabledButNoDiscriminator() { + " }"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setOpenAPI3StyleDiscriminators(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().discriminatorKeywordEnabled(true).build(); JsonSchema schema = factory.getSchema(schemaData, config); Set messages = schema.validate(inputData, InputFormat.JSON); assertTrue(messages.isEmpty()); @@ -244,8 +242,7 @@ void discriminatorInArrayInvalidDiscriminatorPropertyAnyOf() { + "]"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setOpenAPI3StyleDiscriminators(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().discriminatorKeywordEnabled(true).build(); JsonSchema schema = factory.getSchema(schemaData, config); Set messages = schema.validate(inputData, InputFormat.JSON); assertEquals(1, messages.size()); @@ -338,8 +335,7 @@ void discriminatorInArrayInvalidDiscriminatorPropertyOneOf() { + "]"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setOpenAPI3StyleDiscriminators(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().discriminatorKeywordEnabled(true).build(); JsonSchema schema = factory.getSchema(schemaData, config); Set messages = schema.validate(inputData, InputFormat.JSON); assertEquals(1, messages.size()); @@ -429,8 +425,7 @@ void discriminatorInArrayOneOfShouldOnlyReportErrorsInMatchingDiscriminator() { + "]"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setOpenAPI3StyleDiscriminators(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().discriminatorKeywordEnabled(true).build(); JsonSchema schema = factory.getSchema(schemaData, config); Set messages = schema.validate(inputData, InputFormat.JSON); // Only the oneOf and the error in the BedRoom discriminator is reported @@ -526,8 +521,7 @@ void discriminatorInOneOfShouldOnlyReportErrorsInMatchingDiscriminator() { + "]"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setOpenAPI3StyleDiscriminators(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().discriminatorKeywordEnabled(true).build(); JsonSchema schema = factory.getSchema(schemaData, config); Set messages = schema.validate(inputData, InputFormat.JSON); // Only the oneOf and the error in the BedRoom discriminator is reported @@ -627,8 +621,7 @@ void discriminatorMappingInOneOfShouldOnlyReportErrorsInMatchingDiscriminator() + "]"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setOpenAPI3StyleDiscriminators(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().discriminatorKeywordEnabled(true).build(); JsonSchema schema = factory.getSchema(schemaData, config); Set messages = schema.validate(inputData, InputFormat.JSON); // Only the oneOf and the error in the BedRoom discriminator is reported @@ -683,8 +676,7 @@ void oneOfMissingDiscriminatorValue() { String inputData = "{}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setOpenAPI3StyleDiscriminators(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().discriminatorKeywordEnabled(true).build(); JsonSchema schema = factory.getSchema(schemaData, config); Set messages = schema.validate(inputData, InputFormat.JSON); assertEquals(3, messages.size()); @@ -780,8 +772,7 @@ void anyOfMissingDiscriminatorValue() { + "]"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setOpenAPI3StyleDiscriminators(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().discriminatorKeywordEnabled(true).build(); JsonSchema schema = factory.getSchema(schemaData, config); Set messages = schema.validate(inputData, InputFormat.JSON); List list = messages.stream().collect(Collectors.toList()); diff --git a/src/test/java/com/networknt/schema/ExampleTest.java b/src/test/java/com/networknt/schema/ExampleTest.java index ca3650d57..17c9925e8 100644 --- a/src/test/java/com/networknt/schema/ExampleTest.java +++ b/src/test/java/com/networknt/schema/ExampleTest.java @@ -30,8 +30,7 @@ public void exampleSchemaLocation() throws Exception { JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.schemaMappers(schemaMappers -> schemaMappers.mapPrefix("https://www.example.org/", "classpath:schema/")) ); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = jsonSchemaFactory.getSchema(SchemaLocation.of("https://www.example.org/example-main.json"), config); String input = "{\r\n" + " \"DriverProperties\": {\r\n" @@ -54,8 +53,7 @@ public void exampleSchemaLocation() throws Exception { public void exampleClasspath() throws Exception { // This creates a schema factory that will use Draft 2012-12 as the default if $schema is not specified in the initial schema JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = jsonSchemaFactory.getSchema(SchemaLocation.of("classpath:schema/example-main.json"), config); String input = "{\r\n" + " \"DriverProperties\": {\r\n" diff --git a/src/test/java/com/networknt/schema/FormatValidatorTest.java b/src/test/java/com/networknt/schema/FormatValidatorTest.java index 2d0ec9ba3..25e4e8856 100644 --- a/src/test/java/com/networknt/schema/FormatValidatorTest.java +++ b/src/test/java/com/networknt/schema/FormatValidatorTest.java @@ -53,8 +53,7 @@ void unknownFormatNoVocabStrictTrue() { String schemaData = "{\r\n" + " \"format\":\"unknown\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setStrict("format", true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().strict("format", true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"hello\"", InputFormat.JSON, executionContext -> { executionContext.getExecutionConfig().setFormatAssertionsEnabled(true); @@ -83,7 +82,7 @@ void unknownFormatAssertionsVocab() { + " \"$schema\": \"https://www.example.com/format-assertion/schema\",\r\n" + " \"format\":\"unknown\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = JsonSchemaFactory .getInstance(VersionFlag.V202012, builder -> builder @@ -143,8 +142,7 @@ void formatAssertions(FormatInput formatInput) { + " \"format\": \""+formatInput.format+"\"\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(formatSchema, config); Set messages = schema.validate("\"inval!i:d^(abc]\"", InputFormat.JSON, executionConfiguration -> { executionConfiguration.getExecutionConfig().setFormatAssertionsEnabled(true); @@ -173,8 +171,7 @@ void patternFormatDeprecated() { + " \"type\": \"string\",\r\n" + " \"format\": \"custom\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(formatSchema, config); Set messages = schema.validate("\"inval!i:d^(abc]\"", InputFormat.JSON, executionConfiguration -> { executionConfiguration.getExecutionConfig().setFormatAssertionsEnabled(true); @@ -223,8 +220,7 @@ void shouldAllowNumberFormat() { + " \"type\": \"number\",\r\n" + " \"format\": \"custom-number\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(formatSchema, config); Set messages = schema.validate("123451", InputFormat.JSON, executionConfiguration -> { executionConfiguration.getExecutionConfig().setFormatAssertionsEnabled(true); diff --git a/src/test/java/com/networknt/schema/IfValidatorTest.java b/src/test/java/com/networknt/schema/IfValidatorTest.java index 511fee0fc..6b6f1dcec 100644 --- a/src/test/java/com/networknt/schema/IfValidatorTest.java +++ b/src/test/java/com/networknt/schema/IfValidatorTest.java @@ -49,25 +49,24 @@ void walkValidateThen() { + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addKeywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - return WalkFlow.CONTINUE; - } - - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - @SuppressWarnings("unchecked") - List types = (List) walkEvent.getExecutionContext() - .getCollectorContext() - .getCollectorMap() - .computeIfAbsent("types", key -> new ArrayList()); - types.add(walkEvent); - } - }); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .keywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + return WalkFlow.CONTINUE; + } + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + @SuppressWarnings("unchecked") + List types = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("types", key -> new ArrayList()); + types.add(walkEvent); + } + }) + .build(); JsonSchema schema = factory.getSchema(schemaData, config); ValidationResult result = schema.walk("\"false\"", InputFormat.JSON, true); assertFalse(result.getValidationMessages().isEmpty()); @@ -93,25 +92,24 @@ void walkValidateElse() { + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addKeywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - return WalkFlow.CONTINUE; - } - - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - @SuppressWarnings("unchecked") - List types = (List) walkEvent.getExecutionContext() - .getCollectorContext() - .getCollectorMap() - .computeIfAbsent("types", key -> new ArrayList()); - types.add(walkEvent); - } - }); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .keywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + return WalkFlow.CONTINUE; + } + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + @SuppressWarnings("unchecked") + List types = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("types", key -> new ArrayList()); + types.add(walkEvent); + } + }) + .build(); JsonSchema schema = factory.getSchema(schemaData, config); ValidationResult result = schema.walk("\"hello\"", InputFormat.JSON, true); assertFalse(result.getValidationMessages().isEmpty()); @@ -137,25 +135,24 @@ void walkValidateNull() { + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addKeywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - return WalkFlow.CONTINUE; - } - - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - @SuppressWarnings("unchecked") - List types = (List) walkEvent.getExecutionContext() - .getCollectorContext() - .getCollectorMap() - .computeIfAbsent("types", key -> new ArrayList()); - types.add(walkEvent); - } - }); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .keywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + return WalkFlow.CONTINUE; + } + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + @SuppressWarnings("unchecked") + List types = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("types", key -> new ArrayList()); + types.add(walkEvent); + } + }) + .build(); JsonSchema schema = factory.getSchema(schemaData, config); ValidationResult result = schema.walk(null, true); assertTrue(result.getValidationMessages().isEmpty()); @@ -179,25 +176,24 @@ void walkNoValidate() { + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addKeywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - return WalkFlow.CONTINUE; - } - - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - @SuppressWarnings("unchecked") - List types = (List) walkEvent.getExecutionContext() - .getCollectorContext() - .getCollectorMap() - .computeIfAbsent("types", key -> new ArrayList()); - types.add(walkEvent); - } - }); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .keywordWalkListener(ValidatorTypeCode.TYPE.getValue(), new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + return WalkFlow.CONTINUE; + } + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + @SuppressWarnings("unchecked") + List types = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("types", key -> new ArrayList()); + types.add(walkEvent); + } + }) + .build(); JsonSchema schema = factory.getSchema(schemaData, config); ValidationResult result = schema.walk("\"false\"", InputFormat.JSON, false); assertTrue(result.getValidationMessages().isEmpty()); diff --git a/src/test/java/com/networknt/schema/Issue366FailFastTest.java b/src/test/java/com/networknt/schema/Issue366FailFastTest.java index a863ecff9..0bd37e036 100644 --- a/src/test/java/com/networknt/schema/Issue366FailFastTest.java +++ b/src/test/java/com/networknt/schema/Issue366FailFastTest.java @@ -23,14 +23,12 @@ public void setup() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); private void setupSchema() throws IOException { - - SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); - schemaValidatorsConfig.setFailFast(true); + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder() + .failFast(true) + .typeLoose(false) + .build(); JsonSchemaFactory schemaFactory = JsonSchemaFactory .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)).build(); - - schemaValidatorsConfig.setTypeLoose(false); - SchemaLocation uri = getSchema(); InputStream in = getClass().getResourceAsStream("/schema/issue366_schema.json"); diff --git a/src/test/java/com/networknt/schema/Issue366FailSlowTest.java b/src/test/java/com/networknt/schema/Issue366FailSlowTest.java index 38f25a7cb..862591cb2 100644 --- a/src/test/java/com/networknt/schema/Issue366FailSlowTest.java +++ b/src/test/java/com/networknt/schema/Issue366FailSlowTest.java @@ -14,90 +14,89 @@ public class Issue366FailSlowTest { - @BeforeEach - public void setup() throws IOException { - setupSchema(); - } - - JsonSchema jsonSchema; - ObjectMapper objectMapper = new ObjectMapper(); - private void setupSchema() throws IOException { - - SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); - JsonSchemaFactory schemaFactory = JsonSchemaFactory - .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)) - .build(); - - schemaValidatorsConfig.setTypeLoose(false); - - SchemaLocation uri = getSchema(); - - InputStream in = getClass().getResourceAsStream("/schema/issue366_schema.json"); - JsonNode testCases = objectMapper.readValue(in, JsonNode.class); - this.jsonSchema = schemaFactory.getSchema(uri, testCases,schemaValidatorsConfig); - } - - protected JsonNode getJsonNodeFromStreamContent(InputStream content) throws Exception { - ObjectMapper mapper = new ObjectMapper(); - JsonNode node = mapper.readTree(content); - return node; - } - - @Test - public void firstOneValid() throws Exception { - String dataPath = "/data/issue366.json"; - - InputStream dataInputStream = getClass().getResourceAsStream(dataPath); - JsonNode node = getJsonNodeFromStreamContent(dataInputStream); - List testNodes = node.findValues("tests"); - JsonNode testNode = testNodes.get(0).get(0); - JsonNode dataNode = testNode.get("data"); - Set errors = jsonSchema.validate(dataNode); - assertTrue(errors.isEmpty()); - } - - @Test - public void secondOneValid() throws Exception { - String dataPath = "/data/issue366.json"; - - InputStream dataInputStream = getClass().getResourceAsStream(dataPath); - JsonNode node = getJsonNodeFromStreamContent(dataInputStream); - List testNodes = node.findValues("tests"); - JsonNode testNode = testNodes.get(0).get(1); - JsonNode dataNode = testNode.get("data"); - Set errors = jsonSchema.validate(dataNode); - assertTrue(errors.isEmpty()); - } - - @Test - public void bothValid() throws Exception { - String dataPath = "/data/issue366.json"; - - InputStream dataInputStream = getClass().getResourceAsStream(dataPath); - JsonNode node = getJsonNodeFromStreamContent(dataInputStream); - List testNodes = node.findValues("tests"); - JsonNode testNode = testNodes.get(0).get(2); - JsonNode dataNode = testNode.get("data"); - Set errors = jsonSchema.validate(dataNode); - assertTrue(!errors.isEmpty()); - assertEquals(errors.size(),1); - } - - @Test - public void neitherValid() throws Exception { - String dataPath = "/data/issue366.json"; - - InputStream dataInputStream = getClass().getResourceAsStream(dataPath); - JsonNode node = getJsonNodeFromStreamContent(dataInputStream); - List testNodes = node.findValues("tests"); - JsonNode testNode = testNodes.get(0).get(3); - JsonNode dataNode = testNode.get("data"); - Set errors = jsonSchema.validate(dataNode); - assertTrue(!errors.isEmpty()); - assertEquals(errors.size(),3); - } - - private SchemaLocation getSchema() { - return SchemaLocation.of("classpath:" + "/draft7/issue366_schema.json"); - } + @BeforeEach + public void setup() throws IOException { + setupSchema(); + } + + JsonSchema jsonSchema; + ObjectMapper objectMapper = new ObjectMapper(); + + private void setupSchema() throws IOException { + + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder().typeLoose(false).build(); + JsonSchemaFactory schemaFactory = JsonSchemaFactory + .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7)) + .build(); + + SchemaLocation uri = getSchema(); + + InputStream in = getClass().getResourceAsStream("/schema/issue366_schema.json"); + JsonNode testCases = objectMapper.readValue(in, JsonNode.class); + this.jsonSchema = schemaFactory.getSchema(uri, testCases, schemaValidatorsConfig); + } + + protected JsonNode getJsonNodeFromStreamContent(InputStream content) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + JsonNode node = mapper.readTree(content); + return node; + } + + @Test + public void firstOneValid() throws Exception { + String dataPath = "/data/issue366.json"; + + InputStream dataInputStream = getClass().getResourceAsStream(dataPath); + JsonNode node = getJsonNodeFromStreamContent(dataInputStream); + List testNodes = node.findValues("tests"); + JsonNode testNode = testNodes.get(0).get(0); + JsonNode dataNode = testNode.get("data"); + Set errors = jsonSchema.validate(dataNode); + assertTrue(errors.isEmpty()); + } + + @Test + public void secondOneValid() throws Exception { + String dataPath = "/data/issue366.json"; + + InputStream dataInputStream = getClass().getResourceAsStream(dataPath); + JsonNode node = getJsonNodeFromStreamContent(dataInputStream); + List testNodes = node.findValues("tests"); + JsonNode testNode = testNodes.get(0).get(1); + JsonNode dataNode = testNode.get("data"); + Set errors = jsonSchema.validate(dataNode); + assertTrue(errors.isEmpty()); + } + + @Test + public void bothValid() throws Exception { + String dataPath = "/data/issue366.json"; + + InputStream dataInputStream = getClass().getResourceAsStream(dataPath); + JsonNode node = getJsonNodeFromStreamContent(dataInputStream); + List testNodes = node.findValues("tests"); + JsonNode testNode = testNodes.get(0).get(2); + JsonNode dataNode = testNode.get("data"); + Set errors = jsonSchema.validate(dataNode); + assertTrue(!errors.isEmpty()); + assertEquals(errors.size(), 1); + } + + @Test + public void neitherValid() throws Exception { + String dataPath = "/data/issue366.json"; + + InputStream dataInputStream = getClass().getResourceAsStream(dataPath); + JsonNode node = getJsonNodeFromStreamContent(dataInputStream); + List testNodes = node.findValues("tests"); + JsonNode testNode = testNodes.get(0).get(3); + JsonNode dataNode = testNode.get("data"); + Set errors = jsonSchema.validate(dataNode); + assertTrue(!errors.isEmpty()); + assertEquals(errors.size(), 3); + } + + private SchemaLocation getSchema() { + return SchemaLocation.of("classpath:" + "/draft7/issue366_schema.json"); + } } diff --git a/src/test/java/com/networknt/schema/Issue428Test.java b/src/test/java/com/networknt/schema/Issue428Test.java index 5785cec79..03f2eac00 100644 --- a/src/test/java/com/networknt/schema/Issue428Test.java +++ b/src/test/java/com/networknt/schema/Issue428Test.java @@ -26,7 +26,6 @@ private void runTestFile(String testCaseFile) throws Exception { for (int j = 0; j < testCases.size(); j++) { try { JsonNode testCase = testCases.get(j); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); ArrayNode testNodes = (ArrayNode) testCase.get("tests"); for (int i = 0; i < testNodes.size(); i++) { @@ -36,9 +35,10 @@ private void runTestFile(String testCaseFile) throws Exception { JsonNode typeLooseNode = test.get("isTypeLoose"); // Configure the schemaValidator to set typeLoose's value based on the test file, // if test file do not contains typeLoose flag, use default value: true. - config.setTypeLoose(typeLooseNode != null && typeLooseNode.asBoolean()); - config.setOpenAPI3StyleDiscriminators(false); - JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testCase.get("schema"), config); + SchemaValidatorsConfig.Builder configBuilder = SchemaValidatorsConfig.builder(); + configBuilder.typeLoose(typeLooseNode != null && typeLooseNode.asBoolean()); + configBuilder.discriminatorKeywordEnabled(false); + JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testCase.get("schema"), configBuilder.build()); List errors = new ArrayList(schema.validate(node)); diff --git a/src/test/java/com/networknt/schema/Issue451Test.java b/src/test/java/com/networknt/schema/Issue451Test.java index 53a829e50..760e51d6f 100644 --- a/src/test/java/com/networknt/schema/Issue451Test.java +++ b/src/test/java/com/networknt/schema/Issue451Test.java @@ -22,8 +22,9 @@ public class Issue451Test { protected JsonSchema getJsonSchemaFromStreamContentV7(InputStream schemaContent) { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7); - SchemaValidatorsConfig svc = new SchemaValidatorsConfig(); - svc.addPropertyWalkListener(new CountingWalker()); + SchemaValidatorsConfig svc = SchemaValidatorsConfig.builder() + .propertyWalkListener(new CountingWalker()) + .build(); return factory.getSchema(schemaContent, svc); } diff --git a/src/test/java/com/networknt/schema/Issue461Test.java b/src/test/java/com/networknt/schema/Issue461Test.java index 239f61739..a3d06c550 100644 --- a/src/test/java/com/networknt/schema/Issue461Test.java +++ b/src/test/java/com/networknt/schema/Issue461Test.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.serialization.JsonMapperFactory; import com.networknt.schema.walk.JsonSchemaWalkListener; import com.networknt.schema.walk.WalkEvent; import com.networknt.schema.walk.WalkFlow; @@ -13,12 +14,13 @@ import java.util.Set; public class Issue461Test { - protected ObjectMapper mapper = new ObjectMapper(); + protected ObjectMapper mapper = JsonMapperFactory.getInstance(); protected JsonSchema getJsonSchemaFromStreamContentV7(SchemaLocation schemaUri) { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7); - SchemaValidatorsConfig svc = new SchemaValidatorsConfig(); - svc.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new Walker()); + SchemaValidatorsConfig svc = SchemaValidatorsConfig.builder() + .keywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new Walker()) + .build(); return factory.getSchema(schemaUri, svc); } diff --git a/src/test/java/com/networknt/schema/Issue467Test.java b/src/test/java/com/networknt/schema/Issue467Test.java index ad8bb14fe..d5aa396f3 100644 --- a/src/test/java/com/networknt/schema/Issue467Test.java +++ b/src/test/java/com/networknt/schema/Issue467Test.java @@ -44,22 +44,23 @@ public class Issue467Test { public void shouldWalkKeywordWithValidation() throws URISyntaxException, IOException { InputStream schemaInputStream = Issue467Test.class.getResourceAsStream(schemaPath); final Set properties = new LinkedHashSet<>(); - final SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new JsonSchemaWalkListener() { - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - properties.add(walkEvent.getSchema().getEvaluationPath().append(walkEvent.getKeyword())); - return WalkFlow.CONTINUE; - } + final SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .keywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + properties.add(walkEvent.getSchema().getEvaluationPath().append(walkEvent.getKeyword())); + return WalkFlow.CONTINUE; + } - @Override - public void onWalkEnd(WalkEvent walkEvent, Set set) { - } - }); + @Override + public void onWalkEnd(WalkEvent walkEvent, Set set) { + } + }) + .build(); JsonSchema schema = factory.getSchema(schemaInputStream, config); JsonNode data = mapper.readTree(Issue467Test.class.getResource("/data/issue467.json")); ValidationResult result = schema.walk(data, true); - assertEquals(new HashSet<>(Arrays.asList("$.properties", "$.properties.tags.items[0].properties")), + assertEquals(new HashSet<>(Arrays.asList("/properties", "/properties/tags/items/0/properties")), properties.stream().map(Object::toString).collect(Collectors.toSet())); assertEquals(1, result.getValidationMessages().size()); } @@ -68,23 +69,24 @@ public void onWalkEnd(WalkEvent walkEvent, Set set) { public void shouldWalkPropertiesWithValidation() throws URISyntaxException, IOException { InputStream schemaInputStream = Issue467Test.class.getResourceAsStream(schemaPath); final Set properties = new LinkedHashSet<>(); - final SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.addPropertyWalkListener(new JsonSchemaWalkListener() { - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - properties.add(walkEvent.getSchema().getEvaluationPath()); - return WalkFlow.CONTINUE; - } + final SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .propertyWalkListener(new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + properties.add(walkEvent.getSchema().getEvaluationPath()); + return WalkFlow.CONTINUE; + } - @Override - public void onWalkEnd(WalkEvent walkEvent, Set set) { - } - }); + @Override + public void onWalkEnd(WalkEvent walkEvent, Set set) { + } + }) + .build(); JsonSchema schema = factory.getSchema(schemaInputStream, config); JsonNode data = mapper.readTree(Issue467Test.class.getResource("/data/issue467.json")); ValidationResult result = schema.walk(data, true); assertEquals( - new HashSet<>(Arrays.asList("$.properties.tags", "$.properties.tags.items[0].properties.category", "$.properties.tags.items[0].properties.value")), + new HashSet<>(Arrays.asList("/properties/tags", "/properties/tags/items/0/properties/category", "/properties/tags/items/0/properties/value")), properties.stream().map(Object::toString).collect(Collectors.toSet())); assertEquals(1, result.getValidationMessages().size()); } diff --git a/src/test/java/com/networknt/schema/Issue475Test.java b/src/test/java/com/networknt/schema/Issue475Test.java index e9944cbd6..0f3d59f63 100644 --- a/src/test/java/com/networknt/schema/Issue475Test.java +++ b/src/test/java/com/networknt/schema/Issue475Test.java @@ -52,8 +52,7 @@ public class Issue475Test { public void draft4() throws Exception { JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag.V4, builder -> builder .schemaMappers(schemaMappers -> schemaMappers.mapPrefix("http://json-schema.org", "classpath:"))); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = jsonSchemaFactory.getSchema(SchemaLocation.of(SchemaId.V4), config); Set assertions = schema.validate(JsonMapperFactory.getInstance().readTree(INVALID_INPUT)); @@ -67,8 +66,7 @@ public void draft4() throws Exception { public void draft6() throws Exception { JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag.V6, builder -> builder .schemaMappers(schemaMappers -> schemaMappers.mapPrefix("http://json-schema.org", "classpath:"))); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = jsonSchemaFactory.getSchema(SchemaLocation.of(SchemaId.V6), config); Set assertions = schema.validate(JsonMapperFactory.getInstance().readTree(INVALID_INPUT)); @@ -82,8 +80,7 @@ public void draft6() throws Exception { public void draft7() throws Exception { JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag.V7, builder -> builder .schemaMappers(schemaMappers -> schemaMappers.mapPrefix("http://json-schema.org", "classpath:"))); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = jsonSchemaFactory.getSchema(SchemaLocation.of(SchemaId.V7), config); Set assertions = schema.validate(JsonMapperFactory.getInstance().readTree(INVALID_INPUT)); @@ -97,8 +94,7 @@ public void draft7() throws Exception { public void draft201909() throws Exception { JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag.V201909, builder -> builder .schemaMappers(schemaMappers -> schemaMappers.mapPrefix("https://json-schema.org", "classpath:"))); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = jsonSchemaFactory.getSchema(SchemaLocation.of(SchemaId.V201909), config); Set assertions = schema.validate(JsonMapperFactory.getInstance().readTree(INVALID_INPUT)); @@ -107,13 +103,12 @@ public void draft201909() throws Exception { assertions = schema.validate(JsonMapperFactory.getInstance().readTree(VALID_INPUT)); assertEquals(0, assertions.size()); } - + @Test public void draft202012() throws Exception { JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder .schemaMappers(schemaMappers -> schemaMappers.mapPrefix("https://json-schema.org", "classpath:"))); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = jsonSchemaFactory.getSchema(SchemaLocation.of(SchemaId.V202012), config); Set assertions = schema.validate(JsonMapperFactory.getInstance().readTree(INVALID_INPUT)); diff --git a/src/test/java/com/networknt/schema/Issue604Test.java b/src/test/java/com/networknt/schema/Issue604Test.java index 40e68d87c..183aef221 100644 --- a/src/test/java/com/networknt/schema/Issue604Test.java +++ b/src/test/java/com/networknt/schema/Issue604Test.java @@ -8,8 +8,9 @@ public class Issue604Test { @Test public void failure() { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setApplyDefaultsStrategy(new ApplyDefaultsStrategy(true, false, false)); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .applyDefaultsStrategy(new ApplyDefaultsStrategy(true, false, false)) + .build(); JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7); JsonSchema schema = factory.getSchema("{ \"type\": \"object\", \"properties\": { \"foo\": { \"type\": \"object\", \"properties\": { \"bar\": { \"type\": \"boolean\", \"default\": false } } } } }", config); ObjectMapper objectMapper = new ObjectMapper(); diff --git a/src/test/java/com/networknt/schema/Issue650Test.java b/src/test/java/com/networknt/schema/Issue650Test.java index 02bffbf25..dabbd7c5e 100644 --- a/src/test/java/com/networknt/schema/Issue650Test.java +++ b/src/test/java/com/networknt/schema/Issue650Test.java @@ -66,6 +66,7 @@ private static class Model { * @return the data * @since 1.0.77 */ + @SuppressWarnings("unused") // called by jackson public byte[] getData() { return this.data; } diff --git a/src/test/java/com/networknt/schema/Issue686Test.java b/src/test/java/com/networknt/schema/Issue686Test.java index 1b35f0666..0148dfac5 100644 --- a/src/test/java/com/networknt/schema/Issue686Test.java +++ b/src/test/java/com/networknt/schema/Issue686Test.java @@ -18,40 +18,39 @@ public class Issue686Test { @Test void testDefaults() { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); assertEquals(DefaultMessageSource.getInstance(), config.getMessageSource()); } @Test void testValidationWithDefaultBundleAndLocale() throws JsonProcessingException { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); ResourceBundle resourceBundle = ResourceBundle.getBundle(DefaultMessageSource.BUNDLE_BASE_NAME, Locale.getDefault()); - String expectedMessage = new MessageFormat(resourceBundle.getString("type")).format(new String[] {"$.foo", "integer", "string"}); + String expectedMessage = new MessageFormat(resourceBundle.getString("type")).format(new String[] {"/foo", "integer", "string"}); verify(config, expectedMessage); } @Test void testValidationWithDefaultBundleAndCustomLocale() throws JsonProcessingException { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setLocale(Locale.ITALIAN); - verify(config, "$.foo: integer trovato, string previsto"); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().locale(Locale.ITALIAN).build(); + verify(config, "/foo: integer trovato, string previsto"); } @Test void testValidationWithCustomBundle() throws JsonProcessingException { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setMessageSource(new ResourceBundleMessageSource("issue686/translations")); - config.setLocale(Locale.FRENCH); - verify(config, "$.foo: integer found, string expected (TEST) (FR)"); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .messageSource(new ResourceBundleMessageSource("issue686/translations")) + .locale(Locale.FRENCH) + .build(); + verify(config, "/foo: integer found, string expected (TEST) (FR)"); } @Test void testLocaleSwitch() throws JsonProcessingException { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setLocale(Locale.ITALIAN); - verify(config, "$.foo: integer trovato, string previsto"); - config.setLocale(Locale.FRENCH); - verify(config, "$.foo: integer trouvé, string attendu"); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().locale(Locale.ITALIAN).build(); + verify(config, "/foo: integer trovato, string previsto"); + SchemaValidatorsConfig config2 = SchemaValidatorsConfig.builder().locale(Locale.FRENCH).build(); + verify(config2, "/foo: integer trouvé, string attendu"); } private JsonSchema getSchema(SchemaValidatorsConfig config) { diff --git a/src/test/java/com/networknt/schema/Issue687Test.java b/src/test/java/com/networknt/schema/Issue687Test.java index 962105098..fb4d6e0f4 100644 --- a/src/test/java/com/networknt/schema/Issue687Test.java +++ b/src/test/java/com/networknt/schema/Issue687Test.java @@ -80,8 +80,7 @@ void testAppendIndex(PathType pathType, String currentPath, Integer index, Strin @ParameterizedTest @MethodSource("validationMessages") void testValidationMessage(PathType pathType, String schemaPath, String content, String[] expectedMessagePaths) throws JsonProcessingException { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(pathType); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().pathType(pathType).build(); JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909); JsonSchema schema = factory.getSchema(Issue687Test.class.getResourceAsStream(schemaPath), config); Set messages = schema.validate(new ObjectMapper().readTree(content)); @@ -114,8 +113,7 @@ public static Stream specialCharacterTests() { @MethodSource("specialCharacterTests") void testSpecialCharacters(PathType pathType, String propertyName, String expectedPath) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); - SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); - schemaValidatorsConfig.setPathType(pathType); + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder().pathType(pathType).build(); JsonSchema schema = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909) .getSchema(mapper.readTree("{\n" + " \"$schema\": \"https://json-schema.org/draft/2019-09/schema\",\n" + diff --git a/src/test/java/com/networknt/schema/Issue724Test.java b/src/test/java/com/networknt/schema/Issue724Test.java index 47b060cfe..b4d0c0faa 100644 --- a/src/test/java/com/networknt/schema/Issue724Test.java +++ b/src/test/java/com/networknt/schema/Issue724Test.java @@ -22,9 +22,8 @@ class Issue724Test { @Test void test() throws JsonProcessingException { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); StringCollector stringCollector = new StringCollector(); - config.addKeywordWalkListener(stringCollector); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().keywordWalkListener(stringCollector).build(); String schema = "{\n" diff --git a/src/test/java/com/networknt/schema/Issue792.java b/src/test/java/com/networknt/schema/Issue792.java index 935336e6b..c76c12873 100644 --- a/src/test/java/com/networknt/schema/Issue792.java +++ b/src/test/java/com/networknt/schema/Issue792.java @@ -28,9 +28,7 @@ void test() throws JsonProcessingException { " }\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setTypeLoose(false); - config.setFailFast(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().typeLoose(false).failFast(true).build(); JsonSchema jsonSchema = schemaFactory.getSchema(schemaDef, config); JsonNode jsonNode = new ObjectMapper().readTree("{\"field\": \"pattern-violation\"}"); diff --git a/src/test/java/com/networknt/schema/Issue857Test.java b/src/test/java/com/networknt/schema/Issue857Test.java index 8d533e5ea..045ce23f7 100644 --- a/src/test/java/com/networknt/schema/Issue857Test.java +++ b/src/test/java/com/networknt/schema/Issue857Test.java @@ -48,8 +48,7 @@ void test() { + " \"id\": \"4\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFailFast(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().failFast(true).build(); JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); Set result = factory.getSchema(schema, config).validate(input, InputFormat.JSON); assertTrue(result.isEmpty()); diff --git a/src/test/java/com/networknt/schema/Issue898Test.java b/src/test/java/com/networknt/schema/Issue898Test.java index 1ec5f3ed5..41bfab480 100644 --- a/src/test/java/com/networknt/schema/Issue898Test.java +++ b/src/test/java/com/networknt/schema/Issue898Test.java @@ -13,8 +13,7 @@ class Issue898Test extends BaseJsonSchemaValidatorTest { @Test void testMessagesWithSingleQuotes() throws Exception { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setLocale(Locale.FRENCH); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().locale(Locale.FRENCH).build(); JsonSchema schema = getJsonSchemaFromClasspath("schema/issue898.json", SpecVersion.VersionFlag.V202012, config); JsonNode node = getJsonNodeFromClasspath("data/issue898.json"); @@ -24,8 +23,8 @@ void testMessagesWithSingleQuotes() throws Exception { .collect(toList()); Assertions.assertEquals(2, messages.size()); - Assertions.assertEquals("$.foo: n'a pas de valeur dans l'énumération [foo1, foo2]", messages.get(0)); - Assertions.assertEquals("$.bar: ne correspond pas au modèle d'expression régulière (bar)+", messages.get(1)); + Assertions.assertEquals("/foo: n'a pas de valeur dans l'énumération [foo1, foo2]", messages.get(0)); + Assertions.assertEquals("/bar: ne correspond pas au modèle d'expression régulière (bar)+", messages.get(1)); } } diff --git a/src/test/java/com/networknt/schema/Issue936Test.java b/src/test/java/com/networknt/schema/Issue936Test.java index b9be21f76..65e56c179 100644 --- a/src/test/java/com/networknt/schema/Issue936Test.java +++ b/src/test/java/com/networknt/schema/Issue936Test.java @@ -27,8 +27,9 @@ public class Issue936Test { void shouldThrowInvalidSchemaException() { String schema = "{\r\n" + " \"$id\": \"0\",\r\n" + " \"$schema\": \"https://json-schema.org/draft/2020-12/schema\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setSchemaIdValidator(JsonSchemaIdValidator.DEFAULT); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .schemaIdValidator(JsonSchemaIdValidator.DEFAULT) + .build(); assertThrowsExactly(InvalidSchemaException.class, () -> JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schema, config)); try { diff --git a/src/test/java/com/networknt/schema/Issue943Test.java b/src/test/java/com/networknt/schema/Issue943Test.java index 82fbc6a92..2cf9c32b3 100644 --- a/src/test/java/com/networknt/schema/Issue943Test.java +++ b/src/test/java/com/networknt/schema/Issue943Test.java @@ -69,8 +69,7 @@ void test() { + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas(external))); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); assertTrue(schema.validate(inputData, InputFormat.JSON).isEmpty()); diff --git a/src/test/java/com/networknt/schema/ItemsValidator202012Test.java b/src/test/java/com/networknt/schema/ItemsValidator202012Test.java index f7330f37c..24d77718b 100644 --- a/src/test/java/com/networknt/schema/ItemsValidator202012Test.java +++ b/src/test/java/com/networknt/schema/ItemsValidator202012Test.java @@ -46,8 +46,7 @@ void messageInvalid() { + " \"items\": {\"type\": \"integer\"}" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "[1, \"x\"]"; Set messages = schema.validate(inputData, InputFormat.JSON); @@ -70,15 +69,12 @@ void walkNull() { + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addItemWalkListener(new JsonSchemaWalkListener() { - + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().itemWalkListener(new JsonSchemaWalkListener() { @Override public WalkFlow onWalkStart(WalkEvent walkEvent) { return WalkFlow.CONTINUE; } - + @Override public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { @SuppressWarnings("unchecked") @@ -88,7 +84,7 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess .computeIfAbsent("items", key -> new ArrayList()); items.add(walkEvent); } - }); + }).build(); JsonSchema schema = factory.getSchema(schemaData, config); ValidationResult result = schema.walk(null, true); assertTrue(result.getValidationMessages().isEmpty()); @@ -112,15 +108,12 @@ void walkNullPrefixItems() { + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addItemWalkListener(new JsonSchemaWalkListener() { - + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().itemWalkListener(new JsonSchemaWalkListener() { @Override public WalkFlow onWalkStart(WalkEvent walkEvent) { return WalkFlow.CONTINUE; } - + @Override public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { @SuppressWarnings("unchecked") @@ -130,7 +123,7 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess .computeIfAbsent("items", key -> new ArrayList()); items.add(walkEvent); } - }); + }).build(); JsonSchema schema = factory.getSchema(schemaData, config); ValidationResult result = schema.walk(null, true); assertTrue(result.getValidationMessages().isEmpty()); diff --git a/src/test/java/com/networknt/schema/ItemsValidatorTest.java b/src/test/java/com/networknt/schema/ItemsValidatorTest.java index 901dbc002..9864f73d3 100644 --- a/src/test/java/com/networknt/schema/ItemsValidatorTest.java +++ b/src/test/java/com/networknt/schema/ItemsValidatorTest.java @@ -50,8 +50,7 @@ void messageInvalid() { + " \"items\": {\"type\": \"integer\"}" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "[1, \"x\"]"; Set messages = schema.validate(inputData, InputFormat.JSON); @@ -65,7 +64,7 @@ void messageInvalid() { assertEquals("/1: string found, integer expected", message.getMessage()); assertNull(message.getProperty()); } - + /** * Tests that the message contains the correct values when there are invalid * items. @@ -78,8 +77,7 @@ void messageAdditionalItemsInvalid() { + " \"additionalItems\": {\"type\": \"integer\"}" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "[ null, 2, 3, \"foo\" ]"; Set messages = schema.validate(inputData, InputFormat.JSON); @@ -93,7 +91,7 @@ void messageAdditionalItemsInvalid() { assertEquals("/3: string found, integer expected", message.getMessage()); assertNull(message.getProperty()); } - + /** * Tests that the message contains the correct values when there are invalid * items. @@ -106,8 +104,7 @@ void messageAdditionalItemsFalseInvalid() { + " \"additionalItems\": false" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "[ null, 2, 3, \"foo\" ]"; Set messages = schema.validate(inputData, InputFormat.JSON); @@ -130,15 +127,12 @@ void walk() { + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addItemWalkListener(new JsonSchemaWalkListener() { - + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().itemWalkListener(new JsonSchemaWalkListener() { @Override public WalkFlow onWalkStart(WalkEvent walkEvent) { return WalkFlow.CONTINUE; } - + @Override public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { @SuppressWarnings("unchecked") @@ -148,7 +142,7 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess .computeIfAbsent("items", key -> new ArrayList()); items.add(walkEvent); } - }); + }).build(); JsonSchema schema = factory.getSchema(schemaData, config); ValidationResult result = schema.walk("[\"the\",\"quick\",\"brown\"]", InputFormat.JSON, true); assertTrue(result.getValidationMessages().isEmpty()); @@ -169,15 +163,12 @@ void walkNull() { + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addItemWalkListener(new JsonSchemaWalkListener() { - + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().itemWalkListener(new JsonSchemaWalkListener() { @Override public WalkFlow onWalkStart(WalkEvent walkEvent) { return WalkFlow.CONTINUE; } - + @Override public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { @SuppressWarnings("unchecked") @@ -187,7 +178,7 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess .computeIfAbsent("items", key -> new ArrayList()); items.add(walkEvent); } - }); + }).build(); JsonSchema schema = factory.getSchema(schemaData, config); ValidationResult result = schema.walk(null, true); assertTrue(result.getValidationMessages().isEmpty()); @@ -214,15 +205,12 @@ void walkNullTupleItemsAdditional() { + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addItemWalkListener(new JsonSchemaWalkListener() { - + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().itemWalkListener(new JsonSchemaWalkListener() { @Override public WalkFlow onWalkStart(WalkEvent walkEvent) { return WalkFlow.CONTINUE; } - + @Override public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { @SuppressWarnings("unchecked") @@ -232,7 +220,7 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess .computeIfAbsent("items", key -> new ArrayList()); items.add(walkEvent); } - }); + }).build(); JsonSchema schema = factory.getSchema(schemaData, config); ValidationResult result = schema.walk(null, true); assertTrue(result.getValidationMessages().isEmpty()); @@ -267,15 +255,12 @@ void walkTupleItemsAdditional() throws JsonMappingException, JsonProcessingExcep + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addItemWalkListener(new JsonSchemaWalkListener() { - + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().itemWalkListener(new JsonSchemaWalkListener() { @Override public WalkFlow onWalkStart(WalkEvent walkEvent) { return WalkFlow.CONTINUE; } - + @Override public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { @SuppressWarnings("unchecked") @@ -285,7 +270,7 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess .computeIfAbsent("items", key -> new ArrayList()); items.add(walkEvent); } - }); + }).build(); JsonSchema schema = factory.getSchema(schemaData, config); JsonNode input = JsonMapperFactory.getInstance().readTree("[\"hello\"]"); ValidationResult result = schema.walk(input, true); @@ -324,26 +309,26 @@ void walkTupleItemsAdditionalDefaults() throws JsonMappingException, JsonProcess + " }\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V201909); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setApplyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)); - config.setPathType(PathType.JSON_POINTER); - config.addItemWalkListener(new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - return WalkFlow.CONTINUE; - } - - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - @SuppressWarnings("unchecked") - List items = (List) walkEvent.getExecutionContext() - .getCollectorContext() - .getCollectorMap() - .computeIfAbsent("items", key -> new ArrayList()); - items.add(walkEvent); - } - }); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .applyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)) + .itemWalkListener(new JsonSchemaWalkListener() { + + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + return WalkFlow.CONTINUE; + } + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + @SuppressWarnings("unchecked") + List items = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("items", key -> new ArrayList()); + items.add(walkEvent); + } + }) + .build(); JsonSchema schema = factory.getSchema(schemaData, config); JsonNode input = JsonMapperFactory.getInstance().readTree("[null, null, null, null]"); ValidationResult result = schema.walk(input, true); diff --git a/src/test/java/com/networknt/schema/JsonSchemaFactoryTest.java b/src/test/java/com/networknt/schema/JsonSchemaFactoryTest.java index e68f9966d..474222049 100644 --- a/src/test/java/com/networknt/schema/JsonSchemaFactoryTest.java +++ b/src/test/java/com/networknt/schema/JsonSchemaFactoryTest.java @@ -56,7 +56,7 @@ void concurrency() { for (int i = 0; i < 50; ++i) { Runnable runner = new Runnable() { public void run() { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); try { latch.await(); } catch (InterruptedException e) { diff --git a/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java b/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java index 27cac9a05..31d4854b1 100644 --- a/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java +++ b/src/test/java/com/networknt/schema/JsonSchemaFactoryUriCacheTest.java @@ -35,12 +35,12 @@ private void runCacheTest(boolean enableCache) throws JsonProcessingException { SchemaLocation schemaUri = SchemaLocation.of("cache:uri_mapping/schema1.json"); String schema = "{ \"$schema\": \"https://json-schema.org/draft/2020-12/schema\", \"title\": \"json-object-with-schema\", \"type\": \"string\" }"; fetcher.addResource(schemaUri.getAbsoluteIri(), schema); - assertEquals(objectMapper.readTree(schema), factory.getSchema(schemaUri, new SchemaValidatorsConfig()).schemaNode); + assertEquals(objectMapper.readTree(schema), factory.getSchema(schemaUri, SchemaValidatorsConfig.builder().build()).schemaNode); String modifiedSchema = "{ \"$schema\": \"https://json-schema.org/draft/2020-12/schema\", \"title\": \"json-object-with-schema\", \"type\": \"object\" }"; fetcher.addResource(schemaUri.getAbsoluteIri(), modifiedSchema); - assertEquals(objectMapper.readTree(enableCache ? schema : modifiedSchema), factory.getSchema(schemaUri, new SchemaValidatorsConfig()).schemaNode); + assertEquals(objectMapper.readTree(enableCache ? schema : modifiedSchema), factory.getSchema(schemaUri, SchemaValidatorsConfig.builder().build()).schemaNode); } private JsonSchemaFactory buildJsonSchemaFactory(CustomURIFetcher uriFetcher, boolean enableSchemaCache) { diff --git a/src/test/java/com/networknt/schema/JsonSchemaPreloadTest.java b/src/test/java/com/networknt/schema/JsonSchemaPreloadTest.java index 2fe9e3036..d2a467814 100644 --- a/src/test/java/com/networknt/schema/JsonSchemaPreloadTest.java +++ b/src/test/java/com/networknt/schema/JsonSchemaPreloadTest.java @@ -27,16 +27,16 @@ public class JsonSchemaPreloadTest { @Test void cacheRefsFalse() { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V7); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setCacheRefs(false); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().cacheRefs(false).build(); factory.getSchema(SchemaLocation.of("classpath:/issues/1016/schema.json"), config); } @Test void preloadSchemaRefMaxNestingDepth() { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V7); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPreloadJsonSchemaRefMaxNestingDepth(20); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .preloadJsonSchemaRefMaxNestingDepth(20) + .build(); factory.getSchema(SchemaLocation.of("classpath:/issues/1016/schema.json"), config); } } diff --git a/src/test/java/com/networknt/schema/JsonSchemaTest.java b/src/test/java/com/networknt/schema/JsonSchemaTest.java index 506763c54..0f76ac1f6 100644 --- a/src/test/java/com/networknt/schema/JsonSchemaTest.java +++ b/src/test/java/com/networknt/schema/JsonSchemaTest.java @@ -58,8 +58,7 @@ void concurrency() throws Exception { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders .schemas(Collections.singletonMap("http://example.org/ref.json", refSchemaData)))); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPreloadJsonSchema(false); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().preloadJsonSchema(false).build(); JsonSchema schema = factory.getSchema(schemaData, config); Exception[] instance = new Exception[1]; CountDownLatch latch = new CountDownLatch(1); diff --git a/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java b/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java index abb0b06a6..6fb41efc0 100644 --- a/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java +++ b/src/test/java/com/networknt/schema/JsonWalkApplyDefaultsTest.java @@ -16,12 +16,6 @@ class JsonWalkApplyDefaultsTest { - - /* @AfterEach - void cleanup() { - CollectorContext.getInstance().reset(); - }*/ - @ParameterizedTest @ValueSource(booleans = { true, false}) void testApplyDefaults3(boolean shouldValidateSchema) throws IOException { @@ -31,9 +25,9 @@ void testApplyDefaults3(boolean shouldValidateSchema) throws IOException { ValidationResult result = jsonSchema.walk(inputNode, shouldValidateSchema); if (shouldValidateSchema) { assertThat(result.getValidationMessages().stream().map(ValidationMessage::getMessage).collect(Collectors.toList()), - Matchers.containsInAnyOrder("$.outer.mixedObject.intValue_missingButError: string found, integer expected", - "$.outer.badArray[1]: integer found, string expected", - "$.outer.reference.stringValue_missing_with_default_null: null found, string expected")); + Matchers.containsInAnyOrder("/outer/mixedObject/intValue_missingButError: string found, integer expected", + "/outer/badArray/1: integer found, string expected", + "/outer/reference/stringValue_missing_with_default_null: null found, string expected")); } else { assertThat(result.getValidationMessages(), Matchers.empty()); } @@ -51,10 +45,10 @@ void testApplyDefaults2() throws IOException { JsonSchema jsonSchema = createSchema(new ApplyDefaultsStrategy(true, true, false)); ValidationResult result = jsonSchema.walk(inputNode, true); assertThat(result.getValidationMessages().stream().map(ValidationMessage::getMessage).collect(Collectors.toList()), - Matchers.containsInAnyOrder("$.outer.mixedObject.intValue_missingButError: string found, integer expected", - "$.outer.goodArray[1]: null found, string expected", - "$.outer.badArray[1]: null found, string expected", - "$.outer.reference.stringValue_missing_with_default_null: null found, string expected")); + Matchers.containsInAnyOrder("/outer/mixedObject/intValue_missingButError: string found, integer expected", + "/outer/goodArray/1: null found, string expected", + "/outer/badArray/1: null found, string expected", + "/outer/reference/stringValue_missing_with_default_null: null found, string expected")); assertEquals( objectMapper.readTree( "{\"outer\":{\"mixedObject\":{\"intValue_present\":8,\"intValue_null\":35,\"intValue_missingButError\":\"forty-five\",\"intValue_missing\":15,\"intValue_missing_notRequired\":25},\"goodArray\":[\"hello\",null],\"badArray\":[\"hello\",null],\"reference\":{\"stringValue_missing_with_default_null\":null,\"stringValue_missing\":\"hello\"}}}"), @@ -68,11 +62,11 @@ void testApplyDefaults1() throws IOException { JsonSchema jsonSchema = createSchema(new ApplyDefaultsStrategy(true, false, false)); ValidationResult result = jsonSchema.walk(inputNode, true); assertThat(result.getValidationMessages().stream().map(ValidationMessage::getMessage).collect(Collectors.toList()), - Matchers.containsInAnyOrder("$.outer.mixedObject.intValue_null: null found, integer expected", - "$.outer.mixedObject.intValue_missingButError: string found, integer expected", - "$.outer.goodArray[1]: null found, string expected", - "$.outer.badArray[1]: null found, string expected", - "$.outer.reference.stringValue_missing_with_default_null: null found, string expected")); + Matchers.containsInAnyOrder("/outer/mixedObject/intValue_null: null found, integer expected", + "/outer/mixedObject/intValue_missingButError: string found, integer expected", + "/outer/goodArray/1: null found, string expected", + "/outer/badArray/1: null found, string expected", + "/outer/reference/stringValue_missing_with_default_null: null found, string expected")); assertEquals( objectMapper.readTree( "{\"outer\":{\"mixedObject\":{\"intValue_present\":8,\"intValue_null\":null,\"intValue_missingButError\":\"forty-five\",\"intValue_missing\":15,\"intValue_missing_notRequired\":25},\"goodArray\":[\"hello\",null],\"badArray\":[\"hello\",null],\"reference\":{\"stringValue_missing_with_default_null\":null,\"stringValue_missing\":\"hello\"}}}"), @@ -107,12 +101,12 @@ void testApplyDefaults0(String method) throws IOException { throw new UnsupportedOperationException(); } assertThat(validationMessages.stream().map(ValidationMessage::getMessage).collect(Collectors.toList()), - Matchers.containsInAnyOrder("$.outer.mixedObject: required property 'intValue_missing' not found", - "$.outer.mixedObject: required property 'intValue_missingButError' not found", - "$.outer.mixedObject.intValue_null: null found, integer expected", - "$.outer.goodArray[1]: null found, string expected", - "$.outer.badArray[1]: null found, string expected", - "$.outer.reference: required property 'stringValue_missing' not found")); + Matchers.containsInAnyOrder("/outer/mixedObject: required property 'intValue_missing' not found", + "/outer/mixedObject: required property 'intValue_missingButError' not found", + "/outer/mixedObject/intValue_null: null found, integer expected", + "/outer/goodArray/1: null found, string expected", + "/outer/badArray/1: null found, string expected", + "/outer/reference: required property 'stringValue_missing' not found")); assertEquals(inputNodeOriginal, inputNode); } @@ -127,8 +121,7 @@ void testIllegalArgumentException() { private JsonSchema createSchema(ApplyDefaultsStrategy applyDefaultsStrategy) { JsonSchemaFactory schemaFactory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4); - SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); - schemaValidatorsConfig.setApplyDefaultsStrategy(applyDefaultsStrategy); + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder().applyDefaultsStrategy(applyDefaultsStrategy).build(); return schemaFactory.getSchema(getClass().getClassLoader().getResourceAsStream("schema/walk-schema-default.json"), schemaValidatorsConfig); } } diff --git a/src/test/java/com/networknt/schema/JsonWalkTest.java b/src/test/java/com/networknt/schema/JsonWalkTest.java index 3ff08e11d..d8020ebc9 100644 --- a/src/test/java/com/networknt/schema/JsonWalkTest.java +++ b/src/test/java/com/networknt/schema/JsonWalkTest.java @@ -36,21 +36,21 @@ public void setup() { private void setupSchema() { final JsonMetaSchema metaSchema = getJsonMetaSchema(); // Create Schema. - SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); - schemaValidatorsConfig.addKeywordWalkListener(new AllKeywordListener()); - schemaValidatorsConfig.addKeywordWalkListener(ValidatorTypeCode.REF.getValue(), new RefKeywordListener()); - schemaValidatorsConfig.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), + SchemaValidatorsConfig.Builder schemaValidatorsConfigBuilder = SchemaValidatorsConfig.builder(); + schemaValidatorsConfigBuilder.keywordWalkListener(new AllKeywordListener()); + schemaValidatorsConfigBuilder.keywordWalkListener(ValidatorTypeCode.REF.getValue(), new RefKeywordListener()); + schemaValidatorsConfigBuilder.keywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new PropertiesKeywordListener()); final JsonSchemaFactory schemaFactory = JsonSchemaFactory .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909)).metaSchema(metaSchema) .build(); - this.jsonSchema = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig); + this.jsonSchema = schemaFactory.getSchema(getSchema(), schemaValidatorsConfigBuilder.build()); // Create another Schema. - SchemaValidatorsConfig schemaValidatorsConfig1 = new SchemaValidatorsConfig(); - schemaValidatorsConfig1.addKeywordWalkListener(ValidatorTypeCode.REF.getValue(), new RefKeywordListener()); - schemaValidatorsConfig1.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), + SchemaValidatorsConfig.Builder schemaValidatorsConfig1Builder = SchemaValidatorsConfig.builder(); + schemaValidatorsConfig1Builder.keywordWalkListener(ValidatorTypeCode.REF.getValue(), new RefKeywordListener()); + schemaValidatorsConfig1Builder.keywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new PropertiesKeywordListener()); - this.jsonSchema1 = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig1); + this.jsonSchema1 = schemaFactory.getSchema(getSchema(), schemaValidatorsConfig1Builder.build()); } private JsonMetaSchema getJsonMetaSchema() { diff --git a/src/test/java/com/networknt/schema/LocaleTest.java b/src/test/java/com/networknt/schema/LocaleTest.java index 4fc386f0e..d8783e2a9 100644 --- a/src/test/java/com/networknt/schema/LocaleTest.java +++ b/src/test/java/com/networknt/schema/LocaleTest.java @@ -51,7 +51,7 @@ private JsonSchema getSchema(SchemaValidatorsConfig config) { @Test void executionContextLocale() throws JsonMappingException, JsonProcessingException { JsonNode rootNode = new ObjectMapper().readTree(" { \"foo\": 123 } "); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema jsonSchema = getSchema(config); Locale locale = Locales.findSupported("it;q=0.9,fr;q=1.0"); // fr @@ -60,7 +60,7 @@ void executionContextLocale() throws JsonMappingException, JsonProcessingExcepti executionContext.getExecutionConfig().setLocale(locale); Set messages = jsonSchema.validate(executionContext, rootNode); assertEquals(1, messages.size()); - assertEquals("$.foo: integer trouvé, string attendu", messages.iterator().next().getMessage()); + assertEquals("/foo: integer trouvé, string attendu", messages.iterator().next().getMessage()); locale = Locales.findSupported("it;q=1.0,fr;q=0.9"); // it executionContext = jsonSchema.createExecutionContext(); @@ -68,7 +68,7 @@ void executionContextLocale() throws JsonMappingException, JsonProcessingExcepti executionContext.getExecutionConfig().setLocale(locale); messages = jsonSchema.validate(executionContext, rootNode); assertEquals(1, messages.size()); - assertEquals("$.foo: integer trovato, string previsto", messages.iterator().next().getMessage()); + assertEquals("/foo: integer trovato, string previsto", messages.iterator().next().getMessage()); } /** @@ -96,13 +96,12 @@ void englishLocale() throws JsonMappingException, JsonProcessingException { assertEquals(1, messages.size()); assertEquals("$: integer gefunden, object erwartet", messages.iterator().next().toString()); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setLocale(Locale.ENGLISH); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().locale(Locale.ENGLISH).build(); jsonSchema = JsonSchemaFactory.getInstance(VersionFlag.V7) .getSchema(JsonMapperFactory.getInstance().readTree(schema), config); messages = jsonSchema.validate(input, InputFormat.JSON); assertEquals(1, messages.size()); - assertEquals("$: integer found, object expected", messages.iterator().next().toString()); + assertEquals(": integer found, object expected", messages.iterator().next().toString()); } finally { Locale.setDefault(locale); } diff --git a/src/test/java/com/networknt/schema/MaximumValidatorTest.java b/src/test/java/com/networknt/schema/MaximumValidatorTest.java index d252648b3..ff65cc9d3 100644 --- a/src/test/java/com/networknt/schema/MaximumValidatorTest.java +++ b/src/test/java/com/networknt/schema/MaximumValidatorTest.java @@ -185,8 +185,7 @@ public void negativeDoubleOverflowTest() throws IOException { String maximum = aTestCycle[0]; String value = aTestCycle[1]; String schema = format(NUMBER, maximum); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setTypeLoose(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().typeLoose(true).build(); // Schema and document parsed with just double JsonSchema v = factory.getSchema(mapper.readTree(schema), config); JsonNode doc = mapper.readTree(value); @@ -295,8 +294,7 @@ private static void expectNoMessages(String[][] values, String schemaTemplate, O String maximum = aTestCycle[0]; String value = aTestCycle[1]; String schema = format(schemaTemplate, maximum); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setTypeLoose(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().typeLoose(true).build(); JsonSchema v = factory.getSchema(mapper.readTree(schema), config); JsonNode doc = mapper.readTree(value); diff --git a/src/test/java/com/networknt/schema/MetaSchemaValidationTest.java b/src/test/java/com/networknt/schema/MetaSchemaValidationTest.java index 4067e25f7..fa7e35f2c 100644 --- a/src/test/java/com/networknt/schema/MetaSchemaValidationTest.java +++ b/src/test/java/com/networknt/schema/MetaSchemaValidationTest.java @@ -40,8 +40,7 @@ public class MetaSchemaValidationTest { void oas31() throws IOException { try (InputStream input = MetaSchemaValidationTest.class.getResourceAsStream("/schema/oas/3.1/petstore.json")) { JsonNode inputData = JsonMapperFactory.getInstance().readTree(input); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = JsonSchemaFactory .getInstance(VersionFlag.V202012, builder -> builder.schemaMappers(schemaMappers -> schemaMappers diff --git a/src/test/java/com/networknt/schema/MinimumValidatorTest.java b/src/test/java/com/networknt/schema/MinimumValidatorTest.java index 329b6040e..8ce5f300b 100644 --- a/src/test/java/com/networknt/schema/MinimumValidatorTest.java +++ b/src/test/java/com/networknt/schema/MinimumValidatorTest.java @@ -16,20 +16,21 @@ package com.networknt.schema; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import static com.networknt.schema.MaximumValidatorTest.augmentWithQuotes; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.IOException; import java.math.BigDecimal; import java.util.Set; -import static com.networknt.schema.MaximumValidatorTest.augmentWithQuotes; -import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; public class MinimumValidatorTest { private static final String NUMBER = "{ \"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"number\", \"minimum\": %s }"; @@ -171,8 +172,7 @@ public void negativeDoubleOverflowTest() throws IOException { String minimum = aTestCycle[0]; String value = aTestCycle[1]; String schema = format(NUMBER, minimum); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setTypeLoose(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().typeLoose(true).build(); // Schema and document parsed with just double JsonSchema v = factory.getSchema(mapper.readTree(schema), config); @@ -287,8 +287,7 @@ private void expectNoMessages(String[][] values, String integer, ObjectMapper ma String minimum = aTestCycle[0]; String value = aTestCycle[1]; String schema = format(integer, minimum); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setTypeLoose(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().typeLoose(true).build(); JsonSchema v = factory.getSchema(mapper.readTree(schema), config); JsonNode doc = bigIntegerMapper.readTree(value); diff --git a/src/test/java/com/networknt/schema/MultipleOfValidatorTest.java b/src/test/java/com/networknt/schema/MultipleOfValidatorTest.java index d53edf387..02ba8d439 100644 --- a/src/test/java/com/networknt/schema/MultipleOfValidatorTest.java +++ b/src/test/java/com/networknt/schema/MultipleOfValidatorTest.java @@ -80,8 +80,7 @@ void testTypeLoose() { assertEquals(2, messages.stream().filter(m -> "type".equals(m.getType())).count()); // With type loose this has 3 multipleOf errors - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setTypeLoose(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().typeLoose(true).build(); JsonSchema typeLoose = factory.getSchema(schemaData, config); messages = typeLoose.validate(inputData, InputFormat.JSON); assertEquals(3, messages.size()); diff --git a/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java b/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java index 136f637f0..e39d1f379 100644 --- a/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java +++ b/src/test/java/com/networknt/schema/OpenAPI30JsonSchemaTest.java @@ -29,7 +29,6 @@ private void runTestFile(String testCaseFile) throws Exception { for (int j = 0; j < testCases.size(); j++) { try { JsonNode testCase = testCases.get(j); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); ArrayNode testNodes = (ArrayNode) testCase.get("tests"); for (int i = 0; i < testNodes.size(); i++) { @@ -39,9 +38,10 @@ private void runTestFile(String testCaseFile) throws Exception { JsonNode typeLooseNode = test.get("isTypeLoose"); // Configure the schemaValidator to set typeLoose's value based on the test file, // if test file do not contains typeLoose flag, use default value: true. - config.setTypeLoose(typeLooseNode != null && typeLooseNode.asBoolean()); - config.setOpenAPI3StyleDiscriminators(true); - JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testCase.get("schema"), config); + SchemaValidatorsConfig.Builder configBuilder = SchemaValidatorsConfig.builder(); + configBuilder.typeLoose(typeLooseNode != null && typeLooseNode.asBoolean()); + configBuilder.discriminatorKeywordEnabled(true); + JsonSchema schema = validatorFactory.getSchema(testCaseFileUri, testCase.get("schema"), configBuilder.build()); List errors = new ArrayList(schema.validate(node)); diff --git a/src/test/java/com/networknt/schema/OutputFormatTest.java b/src/test/java/com/networknt/schema/OutputFormatTest.java index 8fbd35b36..bbd002880 100644 --- a/src/test/java/com/networknt/schema/OutputFormatTest.java +++ b/src/test/java/com/networknt/schema/OutputFormatTest.java @@ -1,9 +1,13 @@ package com.networknt.schema; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.InputStream; +import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.hamcrest.Matchers; @@ -13,6 +17,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.SpecVersion.VersionFlag; +import com.networknt.schema.utils.CachingSupplier; class OutputFormatTest { @@ -29,8 +35,7 @@ private JsonNode getJsonNodeFromJsonData(String jsonFilePath) throws Exception { @DisplayName("Test Validation Messages") void testInvalidJson() throws Exception { InputStream schemaInputStream = OutputFormatTest.class.getResourceAsStream(schemaPath1); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaInputStream, config); JsonNode node = getJsonNodeFromJsonData("/data/output-format-input.json"); Set errors = schema.validate(node); @@ -47,4 +52,69 @@ void testInvalidJson() throws Exception { "/1: property 'z' is not defined in the schema and the schema does not allow additional properties" }, new String[] { "/items/$ref/required", "https://example.com/polygon#/$defs/point/required", "/1", "/1: required property 'y' not found"})); } + + public static class Detailed implements OutputFormat> { + private ValidationMessage format(ValidationMessage message) { + Supplier messageSupplier = () -> { + StringBuilder builder = new StringBuilder(); + builder.append("["); + builder.append(message.getInstanceLocation().toString()); + builder.append("] "); + JsonNode value = message.getInstanceNode(); + if (!value.isObject() && !value.isArray()) { + builder.append("with value "); + builder.append("'"); + builder.append(value.asText()); + builder.append("'"); + builder.append(" "); + } + builder.append(message.getError()); + + return builder.toString(); + }; + return ValidationMessage.builder() + .messageSupplier(new CachingSupplier<>(messageSupplier)) + .evaluationPath(message.getEvaluationPath()) + .instanceLocation(message.getInstanceLocation()) + .instanceNode(message.getInstanceNode()) + .schemaLocation(message.getSchemaLocation()) + .schemaNode(message.getSchemaNode()) + .arguments(message.getArguments()) + .build(); + } + + @Override + public Set format(JsonSchema jsonSchema, Set validationMessages, + ExecutionContext executionContext, ValidationContext validationContext) { + return validationMessages.stream().map(this::format).collect(Collectors.toCollection(LinkedHashSet::new)); + } + } + + public static final OutputFormat> DETAILED = new Detailed(); + + @Test + void customFormat() { + String schemaData = "{\n" + + " \"properties\": {\n" + + " \"type\": {\n" + + " \"enum\": [\n" + + " \"book\",\n" + + " \"author\"\n" + + " ]\n" + + " },\n" + + " \"id\": {\n" + + " \"type\": \"string\"\n" + + " }\n" + + " }\n" + + "}"; + JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012) + .getSchema(schemaData, InputFormat.JSON, SchemaValidatorsConfig.builder().build()); + String inputData = "{\n" + + " \"type\": \"cat\",\n" + + " \"id\": 1\n" + + "}"; + List messages = schema.validate(inputData, InputFormat.JSON, DETAILED).stream().collect(Collectors.toList()); + assertEquals("[/type] with value 'cat' does not have a value in the enumeration [book, author]", messages.get(0).getMessage()); + assertEquals("[/id] with value '1' integer found, string expected", messages.get(1).getMessage()); + } } diff --git a/src/test/java/com/networknt/schema/OutputUnitTest.java b/src/test/java/com/networknt/schema/OutputUnitTest.java index 035aa7073..35d3a9717 100644 --- a/src/test/java/com/networknt/schema/OutputUnitTest.java +++ b/src/test/java/com/networknt/schema/OutputUnitTest.java @@ -96,8 +96,7 @@ public class OutputUnitTest { @Test void annotationCollectionList() throws JsonProcessingException { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = inputData1; @@ -114,8 +113,7 @@ void annotationCollectionList() throws JsonProcessingException { @Test void annotationCollectionHierarchical() throws JsonProcessingException { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = inputData1; @@ -132,8 +130,7 @@ void annotationCollectionHierarchical() throws JsonProcessingException { @Test void annotationCollectionHierarchical2() throws JsonProcessingException { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = inputData2; @@ -182,8 +179,7 @@ void formatAnnotation(FormatInput formatInput) { + " \"format\": \""+formatInput.format+"\"\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(formatSchema, config); OutputUnit outputUnit = schema.validate("\"inval!i:d^(abc]\"", InputFormat.JSON, OutputFormat.LIST, executionConfiguration -> { executionConfiguration.getExecutionConfig().setAnnotationCollectionEnabled(true); @@ -202,8 +198,7 @@ void formatAssertion(FormatInput formatInput) { + " \"format\": \""+formatInput.format+"\"\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(formatSchema, config); OutputUnit outputUnit = schema.validate("\"inval!i:d^(abc]\"", InputFormat.JSON, OutputFormat.LIST, executionConfiguration -> { executionConfiguration.getExecutionConfig().setAnnotationCollectionEnabled(true); @@ -222,8 +217,7 @@ void typeUnion() { + " \"type\": [\"string\",\"array\"]\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(typeSchema, config); OutputUnit outputUnit = schema.validate("1", InputFormat.JSON, OutputFormat.LIST, executionConfiguration -> { executionConfiguration.getExecutionConfig().setAnnotationCollectionEnabled(true); @@ -273,8 +267,7 @@ void unevaluatedProperties() throws JsonProcessingException { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas(external))); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); // The following checks if the heirarchical output format is correct with multiple unevaluated properties @@ -323,8 +316,7 @@ void anyOf() throws JsonProcessingException { + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "{\r\n" @@ -346,8 +338,7 @@ void listAssertionMapper() { + " \"type\": \"string\"\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(formatSchema, config); OutputUnit outputUnit = schema.validate("1234", InputFormat.JSON, new OutputFormat.List(a -> a)); assertFalse(outputUnit.isValid()); @@ -362,8 +353,7 @@ void hierarchicalAssertionMapper() { + " \"type\": \"string\"\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(formatSchema, config); OutputUnit outputUnit = schema.validate("1234", InputFormat.JSON, new OutputFormat.Hierarchical(a -> a)); assertFalse(outputUnit.isValid()); diff --git a/src/test/java/com/networknt/schema/PatternPropertiesValidatorTest.java b/src/test/java/com/networknt/schema/PatternPropertiesValidatorTest.java index c21d7ef5b..14c45cbe4 100644 --- a/src/test/java/com/networknt/schema/PatternPropertiesValidatorTest.java +++ b/src/test/java/com/networknt/schema/PatternPropertiesValidatorTest.java @@ -52,8 +52,9 @@ public void testInvalidPatternPropertiesValidator() throws Exception { @Test public void testInvalidPatternPropertiesValidatorECMA262() throws Exception { Assertions.assertThrows(JsonSchemaException.class, () -> { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setRegularExpressionFactory(JoniRegularExpressionFactory.getInstance()); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .regularExpressionFactory(JoniRegularExpressionFactory.getInstance()) + .build(); JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4); JsonSchema schema = factory.getSchema("{\"patternProperties\":6}", config); @@ -78,8 +79,7 @@ void message() { + " }\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "{\n" + " \"valid_array\": [\"array1_value\", \"array2_value\"],\n" @@ -130,8 +130,7 @@ void annotation() { + " }\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "{\n" + " \"test\": 5\n" diff --git a/src/test/java/com/networknt/schema/PrefixItemsValidatorTest.java b/src/test/java/com/networknt/schema/PrefixItemsValidatorTest.java index a06eda485..9b561d6de 100644 --- a/src/test/java/com/networknt/schema/PrefixItemsValidatorTest.java +++ b/src/test/java/com/networknt/schema/PrefixItemsValidatorTest.java @@ -56,8 +56,7 @@ void messageInvalid() { + " \"prefixItems\": [{\"type\": \"string\"},{\"type\": \"integer\"}]" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "[1, \"x\"]"; Set messages = schema.validate(inputData, InputFormat.JSON); @@ -83,8 +82,7 @@ void messageValid() { + " \"prefixItems\": [{\"type\": \"string\"},{\"type\": \"integer\"}]" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "[\"x\", 1, 1]"; Set messages = schema.validate(inputData, InputFormat.JSON); @@ -103,8 +101,7 @@ void messageInvalidAdditionalItems() { + " \"items\": false" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "[\"x\", 1, 1, 2]"; Set messages = schema.validate(inputData, InputFormat.JSON); @@ -135,15 +132,12 @@ void walkNull() { + " ]\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addItemWalkListener(new JsonSchemaWalkListener() { - + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().itemWalkListener(new JsonSchemaWalkListener() { @Override public WalkFlow onWalkStart(WalkEvent walkEvent) { return WalkFlow.CONTINUE; } - + @Override public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { @SuppressWarnings("unchecked") @@ -153,7 +147,7 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess .computeIfAbsent("items", key -> new ArrayList()); items.add(walkEvent); } - }); + }).build(); JsonSchema schema = factory.getSchema(schemaData, config); ValidationResult result = schema.walk(null, true); assertTrue(result.getValidationMessages().isEmpty()); @@ -191,26 +185,26 @@ void walkDefaults() throws JsonMappingException, JsonProcessingException { + " ]\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.setApplyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)); - config.addItemWalkListener(new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - return WalkFlow.CONTINUE; - } - - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - @SuppressWarnings("unchecked") - List items = (List) walkEvent.getExecutionContext() - .getCollectorContext() - .getCollectorMap() - .computeIfAbsent("items", key -> new ArrayList()); - items.add(walkEvent); - } - }); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .applyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)) + .itemWalkListener(new JsonSchemaWalkListener() { + + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + return WalkFlow.CONTINUE; + } + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + @SuppressWarnings("unchecked") + List items = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("items", key -> new ArrayList()); + items.add(walkEvent); + } + }) + .build(); JsonSchema schema = factory.getSchema(schemaData, config); JsonNode input = JsonMapperFactory.getInstance().readTree("[null, null]"); ValidationResult result = schema.walk(input, true); diff --git a/src/test/java/com/networknt/schema/PropertiesValidatorTest.java b/src/test/java/com/networknt/schema/PropertiesValidatorTest.java index 887f6ca5d..9717100d5 100644 --- a/src/test/java/com/networknt/schema/PropertiesValidatorTest.java +++ b/src/test/java/com/networknt/schema/PropertiesValidatorTest.java @@ -14,12 +14,9 @@ public void testDoesNotThrowWhenApplyingDefaultPropertiesToNonObjects() throws E Assertions.assertDoesNotThrow(() -> { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4); - SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); - schemaValidatorsConfig.setApplyDefaultsStrategy(new ApplyDefaultsStrategy( - true, - true, - true - )); + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder() + .applyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)) + .build(); JsonSchema schema = factory.getSchema("{\"type\":\"object\",\"properties\":{\"foo\":{\"type\":\"object\", \"properties\": {} },\"i-have-default\":{\"type\":\"string\",\"default\":\"foo\"}}}", schemaValidatorsConfig); JsonNode node = getJsonNodeFromStringContent("{\"foo\": \"bar\"}"); diff --git a/src/test/java/com/networknt/schema/PropertyNamesValidatorTest.java b/src/test/java/com/networknt/schema/PropertyNamesValidatorTest.java index 9b81d4c04..81a8837cf 100644 --- a/src/test/java/com/networknt/schema/PropertyNamesValidatorTest.java +++ b/src/test/java/com/networknt/schema/PropertyNamesValidatorTest.java @@ -39,8 +39,7 @@ void messageInvalid() { + " \"propertyNames\": {\"maxLength\": 3}\r\n" + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, config); String inputData = "{\r\n" + " \"foo\": {},\r\n" diff --git a/src/test/java/com/networknt/schema/ReadOnlyValidatorTest.java b/src/test/java/com/networknt/schema/ReadOnlyValidatorTest.java index 7ac837943..0beeb701f 100644 --- a/src/test/java/com/networknt/schema/ReadOnlyValidatorTest.java +++ b/src/test/java/com/networknt/schema/ReadOnlyValidatorTest.java @@ -28,7 +28,7 @@ void givenConfigWriteTrueWhenReadOnlyTrueThenDenies() throws IOException { ObjectNode node = getJsonNode(); Set errors = loadJsonSchema(true).validate(node); assertFalse(errors.isEmpty()); - assertEquals("$.firstName: is a readonly field, it cannot be changed", + assertEquals("/firstName: is a readonly field, it cannot be changed", errors.stream().map(e -> e.getMessage()).collect(Collectors.toList()).get(0)); } @@ -47,8 +47,7 @@ private JsonSchema getJsonSchema(Boolean write) { } private SchemaValidatorsConfig createSchemaConfig(Boolean write) { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setReadOnly(write); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().readOnly(write).build(); return config; } diff --git a/src/test/java/com/networknt/schema/RefTest.java b/src/test/java/com/networknt/schema/RefTest.java index 6a50457b7..f84700fcd 100644 --- a/src/test/java/com/networknt/schema/RefTest.java +++ b/src/test/java/com/networknt/schema/RefTest.java @@ -17,8 +17,7 @@ public class RefTest { @Test void shouldLoadRelativeClasspathReference() throws JsonMappingException, JsonProcessingException { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(SchemaLocation.of("classpath:///schema/ref-main.json"), config); String input = "{\r\n" + " \"DriverProperties\": {\r\n" @@ -41,8 +40,7 @@ void shouldLoadRelativeClasspathReference() throws JsonMappingException, JsonPro @Test void shouldLoadSchemaResource() throws JsonMappingException, JsonProcessingException { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(SchemaLocation.of("classpath:///schema/ref-main-schema-resource.json"), config); String input = "{\r\n" + " \"DriverProperties\": {\r\n" diff --git a/src/test/java/com/networknt/schema/SampleTest.java b/src/test/java/com/networknt/schema/SampleTest.java new file mode 100644 index 000000000..9a74b216e --- /dev/null +++ b/src/test/java/com/networknt/schema/SampleTest.java @@ -0,0 +1,125 @@ +package com.networknt.schema; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Collections; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.schema.SpecVersion.VersionFlag; +import com.networknt.schema.serialization.JsonMapperFactory; + +/** + * Sample test. + */ +public class SampleTest { + @Test + void schemaFromSchemaLocationMapping() throws JsonMappingException, JsonProcessingException { + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.schemaMappers( + schemaMappers -> schemaMappers.mapPrefix("https://www.example.com/schema", "classpath:schema"))); + /* + * This should be cached for performance. + */ + JsonSchema schemaFromSchemaLocation = factory + .getSchema(SchemaLocation.of("https://www.example.com/schema/example-ref.json")); + /* + * By default all schemas are preloaded eagerly but ref resolve failures are not + * thrown. You check if there are issues with ref resolving using + * initializeValidators() + */ + schemaFromSchemaLocation.initializeValidators(); + Set errors = schemaFromSchemaLocation.validate("{\"id\": \"2\"}", InputFormat.JSON, + executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); + assertEquals(1, errors.size()); + } + + @Test + void schemaFromSchemaLocationContent() throws JsonMappingException, JsonProcessingException { + String schemaData = "{\"enum\":[1, 2, 3, 4],\"enumErrorCode\":\"Not in the list\"}"; + + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, + builder -> builder.schemaLoaders(schemaLoaders -> schemaLoaders.schemas( + Collections.singletonMap("https://www.example.com/schema/example-ref.json", schemaData)))); + /* + * This should be cached for performance. + */ + JsonSchema schemaFromSchemaLocation = factory + .getSchema(SchemaLocation.of("https://www.example.com/schema/example-ref.json")); + /* + * By default all schemas are preloaded eagerly but ref resolve failures are not + * thrown. You check if there are issues with ref resolving using + * initializeValidators() + */ + schemaFromSchemaLocation.initializeValidators(); + Set errors = schemaFromSchemaLocation.validate("{\"id\": \"2\"}", InputFormat.JSON, + executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); + assertEquals(1, errors.size()); + } + + @Test + void schemaFromClasspath() throws JsonMappingException, JsonProcessingException { + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); + /* + * This should be cached for performance. + * + * Loading from using the retrieval IRI is not recommended as it may cause + * confusing when resolving relative $ref when $id is also used. + */ + JsonSchema schemaFromClasspath = factory.getSchema(SchemaLocation.of("classpath:schema/example-ref.json")); + /* + * By default all schemas are preloaded eagerly but ref resolve failures are not + * thrown. You check if there are issues with ref resolving using + * initializeValidators() + */ + schemaFromClasspath.initializeValidators(); + Set errors = schemaFromClasspath.validate("{\"id\": \"2\"}", InputFormat.JSON, + executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); + assertEquals(1, errors.size()); + } + + @Test + void schemaFromString() throws JsonMappingException, JsonProcessingException { + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); + /* + * This should be cached for performance. + * + * Loading from a String is not recommended as there is no base IRI to use for + * resolving relative $ref. + */ + JsonSchema schemaFromString = factory + .getSchema("{\"enum\":[1, 2, 3, 4],\"enumErrorCode\":\"Not in the list\"}"); + Set errors = schemaFromString.validate("7", InputFormat.JSON, + executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); + assertEquals(1, errors.size()); + } + + @Test + void schemaFromJsonNode() throws JsonMappingException, JsonProcessingException { + JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012); + JsonNode schemaNode = JsonMapperFactory.getInstance().readTree( + "{\"$schema\": \"http://json-schema.org/draft-06/schema#\", \"properties\": { \"id\": {\"type\": \"number\"}}}"); + /* + * This should be cached for performance. + * + * Loading from a JsonNode is not recommended as there is no base IRI to use for + * resolving relative $ref. + * + * Note that the V202012 from the factory is the default version if $schema is not + * specified. As $schema is specified in the data, V6 is used. + */ + JsonSchema schemaFromNode = factory.getSchema(schemaNode); + /* + * By default all schemas are preloaded eagerly but ref resolve failures are not + * thrown. You check if there are issues with ref resolving using + * initializeValidators() + */ + schemaFromNode.initializeValidators(); + Set errors = schemaFromNode.validate("{\"id\": \"2\"}", InputFormat.JSON, + executionContext -> executionContext.getExecutionConfig().setFormatAssertionsEnabled(true)); + assertEquals(1, errors.size()); + } +} diff --git a/src/test/java/com/networknt/schema/SchemaValidatorsConfigTest.java b/src/test/java/com/networknt/schema/SchemaValidatorsConfigTest.java index 511eac987..fcfa31304 100644 --- a/src/test/java/com/networknt/schema/SchemaValidatorsConfigTest.java +++ b/src/test/java/com/networknt/schema/SchemaValidatorsConfigTest.java @@ -25,6 +25,7 @@ /** * Test for SchemaValidatorsConfig. */ +@SuppressWarnings("deprecation") class SchemaValidatorsConfigTest { @Test void defaultEcma262Validator() { @@ -46,4 +47,51 @@ void setEcma262Validator() { assertFalse(config.isEcma262Validator()); } + @Test + void constructorPathType() { + SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + assertEquals(PathType.LEGACY, config.getPathType()); + } + + @Test + void builderPathType() { + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); + assertEquals(PathType.JSON_POINTER, config.getPathType()); + } + + @Test + void constructorCustomMessageSupported() { + SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + assertEquals(true, config.isCustomMessageSupported()); + } + + @Test + void builderCustomMessageSupported() { + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); + assertEquals(false, config.isCustomMessageSupported()); + } + + @Test + void constructorHandleNullableField() { + SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + assertEquals(true, config.isHandleNullableField()); + } + + @Test + void builderHandleNullableField() { + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); + assertEquals(false, config.isHandleNullableField()); + } + + @Test + void constructorMutable() { + SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + assertDoesNotThrow(() -> config.setFailFast(true)); + } + + @Test + void builderImmutable() { + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); + assertThrows(UnsupportedOperationException.class, () -> config.setFailFast(true)); + } } diff --git a/src/test/java/com/networknt/schema/SharedConfigTest.java b/src/test/java/com/networknt/schema/SharedConfigTest.java index 6173872ef..b22cea7d4 100644 --- a/src/test/java/com/networknt/schema/SharedConfigTest.java +++ b/src/test/java/com/networknt/schema/SharedConfigTest.java @@ -32,9 +32,10 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess public void shouldCallAllKeywordListenerOnWalkStart() throws Exception { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7); - SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); AllKeywordListener allKeywordListener = new AllKeywordListener(); - schemaValidatorsConfig.addKeywordWalkListener(allKeywordListener); + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder() + .keywordWalkListener(allKeywordListener) + .build(); SchemaLocation draft07Schema = SchemaLocation.of("resource:/draft-07/schema#"); diff --git a/src/test/java/com/networknt/schema/TypeFactoryTest.java b/src/test/java/com/networknt/schema/TypeFactoryTest.java index 74d33c463..2e5248bc8 100755 --- a/src/test/java/com/networknt/schema/TypeFactoryTest.java +++ b/src/test/java/com/networknt/schema/TypeFactoryTest.java @@ -41,11 +41,9 @@ public class TypeFactoryTest { private static final String[] validNonIntegralNumberValues = { "1.1", "-1.1", "1.10" }; - private final SchemaValidatorsConfig schemaValidatorsConfig = new SchemaValidatorsConfig(); - @Test void testIntegralValuesWithJavaSemantics() { - schemaValidatorsConfig.setJavaSemantics(true); + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder().javaSemantics(true).build(); for (String validValue : validIntegralValues) { assertSame(JsonType.INTEGER, getValueNodeType(DecimalNode.valueOf(new BigDecimal(validValue)), schemaValidatorsConfig), @@ -60,7 +58,7 @@ void testIntegralValuesWithJavaSemantics() { @Test void testIntegralValuesWithoutJavaSemantics() { - schemaValidatorsConfig.setJavaSemantics(false); + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder().javaSemantics(false).build(); for (String validValue : validIntegralValues) { assertSame(JsonType.NUMBER, getValueNodeType(DecimalNode.valueOf(new BigDecimal(validValue)), schemaValidatorsConfig), @@ -75,7 +73,7 @@ void testIntegralValuesWithoutJavaSemantics() { @Test void testWithLosslessNarrowing() { - schemaValidatorsConfig.setLosslessNarrowing(true); + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder().losslessNarrowing(true).build(); for (String validValue : validIntegralValues) { assertSame(JsonType.INTEGER, getValueNodeType(DecimalNode.valueOf(new BigDecimal("1.0")), schemaValidatorsConfig), validValue); @@ -87,7 +85,7 @@ void testWithLosslessNarrowing() { @Test void testWithoutLosslessNarrowing() { - schemaValidatorsConfig.setLosslessNarrowing(false); + SchemaValidatorsConfig schemaValidatorsConfig = SchemaValidatorsConfig.builder().losslessNarrowing(false).build(); for (String validValue : validIntegralValues) { assertSame(JsonType.NUMBER, getValueNodeType(DecimalNode.valueOf(new BigDecimal("1.0")), schemaValidatorsConfig), validValue); @@ -101,44 +99,44 @@ void testWithoutLosslessNarrowing() { @Test void testObjectValue() { assertSame(JsonType.OBJECT, getValueNodeType(JsonMapperFactory.getInstance().getNodeFactory().objectNode(), - schemaValidatorsConfig)); + SchemaValidatorsConfig.builder().build())); } @Test void testArrayValue() { assertSame(JsonType.ARRAY, - getValueNodeType(JsonMapperFactory.getInstance().getNodeFactory().arrayNode(), schemaValidatorsConfig)); + getValueNodeType(JsonMapperFactory.getInstance().getNodeFactory().arrayNode(), SchemaValidatorsConfig.builder().build())); } @Test void testBooleanValue() { assertSame(JsonType.BOOLEAN, getValueNodeType( - JsonMapperFactory.getInstance().getNodeFactory().booleanNode(true), schemaValidatorsConfig)); + JsonMapperFactory.getInstance().getNodeFactory().booleanNode(true), SchemaValidatorsConfig.builder().build())); } @Test public void testNullValue() { assertSame(JsonType.NULL, - getValueNodeType(JsonMapperFactory.getInstance().getNodeFactory().nullNode(), schemaValidatorsConfig)); + getValueNodeType(JsonMapperFactory.getInstance().getNodeFactory().nullNode(), SchemaValidatorsConfig.builder().build())); } @Test void testMissingValue() { assertSame(JsonType.UNKNOWN, getValueNodeType(JsonMapperFactory.getInstance().getNodeFactory().missingNode(), - schemaValidatorsConfig)); + SchemaValidatorsConfig.builder().build())); } @Test void testIntegerValue() { assertSame(JsonType.INTEGER, getValueNodeType(JsonMapperFactory.getInstance().getNodeFactory().numberNode(10), - schemaValidatorsConfig)); + SchemaValidatorsConfig.builder().build())); } @Test void testBinaryValue() { assertSame(JsonType.STRING, getValueNodeType( JsonMapperFactory.getInstance().getNodeFactory().binaryNode("test".getBytes(StandardCharsets.UTF_8)), - schemaValidatorsConfig)); + SchemaValidatorsConfig.builder().build())); } @Test diff --git a/src/test/java/com/networknt/schema/TypeValidatorTest.java b/src/test/java/com/networknt/schema/TypeValidatorTest.java index 487b8142e..b5919f25e 100644 --- a/src/test/java/com/networknt/schema/TypeValidatorTest.java +++ b/src/test/java/com/networknt/schema/TypeValidatorTest.java @@ -78,8 +78,7 @@ void testTypeLoose() { assertEquals(1, messages.stream().filter(m -> "type".equals(m.getType())).count()); // With type loose this has 0 type errors as any item can also be interpreted as an array of 1 item - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setTypeLoose(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().typeLoose(true).build(); JsonSchema typeLoose = factory.getSchema(schemaData, config); messages = typeLoose.validate(inputData, InputFormat.JSON); assertEquals(0, messages.size()); diff --git a/src/test/java/com/networknt/schema/UriMappingTest.java b/src/test/java/com/networknt/schema/UriMappingTest.java index 3616704f7..332fb1c48 100644 --- a/src/test/java/com/networknt/schema/UriMappingTest.java +++ b/src/test/java/com/networknt/schema/UriMappingTest.java @@ -15,23 +15,21 @@ */ package com.networknt.schema; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.networknt.schema.JsonSchemaFactory.Builder; -import com.networknt.schema.resource.MapSchemaMapper; -import com.networknt.schema.resource.SchemaMapper; - -import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URL; -import java.net.UnknownHostException; import java.util.HashMap; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import org.junit.jupiter.api.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.networknt.schema.JsonSchemaFactory.Builder; +import com.networknt.schema.resource.MapSchemaMapper; +import com.networknt.schema.resource.SchemaMapper; public class UriMappingTest { @@ -123,7 +121,7 @@ public void testValidatorConfigExampleMappings() throws IOException { URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/invalid-schema-uri.json"); JsonSchemaFactory instance = JsonSchemaFactory .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)).build(); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); SchemaLocation example = SchemaLocation.of("https://example.com/invalid/schema/url"); // first test that attempting to use example URL throws an error try { @@ -150,7 +148,7 @@ public void testMappingsForRef() throws IOException { URL mappings = UriMappingTest.class.getResource("/draft4/extra/uri_mapping/schema-with-ref-mapping.json"); JsonSchemaFactory instance = JsonSchemaFactory.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)) .schemaMappers(schemaMappers -> schemaMappers.add(getUriMappingsFromUrl(mappings))).build(); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = instance.getSchema(SchemaLocation.of("resource:draft4/extra/uri_mapping/schema-with-ref.json"), config); assertEquals(0, schema.validate(mapper.readTree("[]")).size()); diff --git a/src/test/java/com/networknt/schema/V4JsonSchemaTest.java b/src/test/java/com/networknt/schema/V4JsonSchemaTest.java index 9a71a7a6b..e8f4a4f0a 100644 --- a/src/test/java/com/networknt/schema/V4JsonSchemaTest.java +++ b/src/test/java/com/networknt/schema/V4JsonSchemaTest.java @@ -85,8 +85,7 @@ private Set validateFailingFastSchemaFor(final String schemaF final ObjectMapper objectMapper = new ObjectMapper(); final JsonNode schema = getJsonNodeFromResource(objectMapper, schemaFileName); final JsonNode dataFile = getJsonNodeFromResource(objectMapper, dataFileName); - final SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFailFast(true); + final SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().failFast(true).build(); return JsonSchemaFactory .builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4)) .build() diff --git a/src/test/java/com/networknt/schema/ValidationMessageHandlerTest.java b/src/test/java/com/networknt/schema/ValidationMessageHandlerTest.java new file mode 100644 index 000000000..a0b9c3cec --- /dev/null +++ b/src/test/java/com/networknt/schema/ValidationMessageHandlerTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.networknt.schema; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import com.networknt.schema.SpecVersion.VersionFlag; + +/** + * ValidationMessageHandlerTest. + */ +class ValidationMessageHandlerTest { + @Test + void errorMessage() { + String schemaData = "{\r\n" + + " \"type\": \"object\",\r\n" + + " \"required\": [\r\n" + + " \"foo\"\r\n" + + " ],\r\n" + + " \"properties\": {\r\n" + + " \"foo\": {\r\n" + + " \"type\": \"integer\"\r\n" + + " }\r\n" + + " },\r\n" + + " \"additionalProperties\": false,\r\n" + + " \"errorMessage\": {\r\n" + + " \"type\": \"should be an object\",\r\n" + + " \"required\": \"should have property foo\",\r\n" + + " \"additionalProperties\": \"should not have properties other than foo\"\r\n" + + " }\r\n" + + "}"; + String inputData = "{\r\n" + + " \"foo\": \"a\",\r\n" + + " \"bar\": 2\r\n" + + "}"; + JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, + SchemaValidatorsConfig.builder().errorMessageKeyword("errorMessage").build()); + List messages = schema.validate(inputData, InputFormat.JSON).stream().collect(Collectors.toList()); + assertFalse(messages.isEmpty()); + assertEquals("/foo", messages.get(0).getInstanceLocation().toString()); + assertEquals("should be an object", messages.get(0).getMessage()); + assertEquals("", messages.get(1).getInstanceLocation().toString()); + assertEquals("should not have properties other than foo", messages.get(1).getMessage()); + } + +} diff --git a/src/test/java/com/networknt/schema/format/IriFormatTest.java b/src/test/java/com/networknt/schema/format/IriFormatTest.java index 112f726f3..b371b1b47 100644 --- a/src/test/java/com/networknt/schema/format/IriFormatTest.java +++ b/src/test/java/com/networknt/schema/format/IriFormatTest.java @@ -15,7 +15,8 @@ */ package com.networknt.schema.format; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Set; @@ -34,9 +35,8 @@ void uriShouldPass() { String schemaData = "{\r\n" + " \"format\": \"iri\"\r\n" + "}"; - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf\"", InputFormat.JSON); @@ -48,9 +48,8 @@ void queryWithBracketsShouldFail() { String schemaData = "{\r\n" + " \"format\": \"iri\"\r\n" + "}"; - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf?filter[test]=1\"", InputFormat.JSON); @@ -63,22 +62,20 @@ void queryWithEncodedBracketsShouldPass() { + " \"format\": \"iri\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf?filter%5Btest%5D=1\"", InputFormat.JSON); assertTrue(messages.isEmpty()); } - + @Test void iriShouldPass() { String schemaData = "{\r\n" + " \"format\": \"iri\"\r\n" + "}"; - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/produktdatenblätter.pdf\"", InputFormat.JSON); diff --git a/src/test/java/com/networknt/schema/format/IriReferenceFormatTest.java b/src/test/java/com/networknt/schema/format/IriReferenceFormatTest.java index eba077115..445937b00 100644 --- a/src/test/java/com/networknt/schema/format/IriReferenceFormatTest.java +++ b/src/test/java/com/networknt/schema/format/IriReferenceFormatTest.java @@ -15,7 +15,8 @@ */ package com.networknt.schema.format; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Set; @@ -34,9 +35,8 @@ void uriShouldPass() { String schemaData = "{\r\n" + " \"format\": \"iri-reference\"\r\n" + "}"; - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf\"", InputFormat.JSON); @@ -48,9 +48,8 @@ void queryWithBracketsShouldFail() { String schemaData = "{\r\n" + " \"format\": \"iri-reference\"\r\n" + "}"; - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf?filter[test]=1\"", InputFormat.JSON); @@ -63,8 +62,7 @@ void queryWithEncodedBracketsShouldPass() { + " \"format\": \"iri-reference\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf?filter%5Btest%5D=1\"", InputFormat.JSON); @@ -76,9 +74,8 @@ void iriShouldPass() { String schemaData = "{\r\n" + " \"format\": \"iri-reference\"\r\n" + "}"; - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/produktdatenblätter.pdf\"", InputFormat.JSON); diff --git a/src/test/java/com/networknt/schema/format/UriFormatTest.java b/src/test/java/com/networknt/schema/format/UriFormatTest.java index 0cac5ff46..6584ef4fe 100644 --- a/src/test/java/com/networknt/schema/format/UriFormatTest.java +++ b/src/test/java/com/networknt/schema/format/UriFormatTest.java @@ -15,7 +15,8 @@ */ package com.networknt.schema.format; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Set; @@ -35,8 +36,7 @@ void uriShouldPass() { + " \"format\": \"uri\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf\"", InputFormat.JSON); @@ -48,9 +48,8 @@ void queryWithBracketsShouldFail() { String schemaData = "{\r\n" + " \"format\": \"uri\"\r\n" + "}"; - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf?filter[test]=1\"", InputFormat.JSON); @@ -63,8 +62,7 @@ void queryWithEncodedBracketsShouldPass() { + " \"format\": \"uri\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf?filter%5Btest%5D=1\"", InputFormat.JSON); @@ -76,9 +74,8 @@ void iriShouldFail() { String schemaData = "{\r\n" + " \"format\": \"uri\"\r\n" + "}"; - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/produktdatenblätter.pdf\"", InputFormat.JSON); diff --git a/src/test/java/com/networknt/schema/format/UriReferenceFormatTest.java b/src/test/java/com/networknt/schema/format/UriReferenceFormatTest.java index 0f0e9aa60..4522d0377 100644 --- a/src/test/java/com/networknt/schema/format/UriReferenceFormatTest.java +++ b/src/test/java/com/networknt/schema/format/UriReferenceFormatTest.java @@ -15,7 +15,8 @@ */ package com.networknt.schema.format; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Set; @@ -35,8 +36,7 @@ void uriShouldPass() { + " \"format\": \"uri-reference\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf\"", InputFormat.JSON); @@ -49,8 +49,7 @@ void queryWithBracketsShouldFail() { + " \"format\": \"uri-reference\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf?filter[test]=1\"", InputFormat.JSON); @@ -63,8 +62,7 @@ void queryWithEncodedBracketsShouldPass() { + " \"format\": \"uri-reference\"\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/product.pdf?filter%5Btest%5D=1\"", InputFormat.JSON); @@ -76,9 +74,8 @@ void iriShouldFail() { String schemaData = "{\r\n" + " \"format\": \"uri-reference\"\r\n" + "}"; - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setFormatAssertionsEnabled(true); + + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().formatAssertionsEnabled(true).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); Set messages = schema.validate("\"https://test.com/assets/produktdatenblätter.pdf\"", InputFormat.JSON); diff --git a/src/test/java/com/networknt/schema/utils/JsonNodesTest.java b/src/test/java/com/networknt/schema/utils/JsonNodesTest.java index 24c55c700..a6c8fa430 100644 --- a/src/test/java/com/networknt/schema/utils/JsonNodesTest.java +++ b/src/test/java/com/networknt/schema/utils/JsonNodesTest.java @@ -35,7 +35,6 @@ import com.networknt.schema.InputFormat; import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; -import com.networknt.schema.PathType; import com.networknt.schema.SchemaValidatorsConfig; import com.networknt.schema.SpecVersion.VersionFlag; import com.networknt.schema.ValidationMessage; @@ -91,8 +90,7 @@ void jsonLocation() { + "}"; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.jsonNodeReader(JsonNodeReader.builder().locationAware().build())); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, InputFormat.JSON, config); Set messages = schema.validate(inputData, InputFormat.JSON, executionContext -> { executionContext.getExecutionConfig().setFormatAssertionsEnabled(true); @@ -140,8 +138,7 @@ void yamlLocation() { + ""; JsonSchemaFactory factory = JsonSchemaFactory.getInstance(VersionFlag.V202012, builder -> builder.jsonNodeReader(JsonNodeReader.builder().locationAware().build())); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().build(); JsonSchema schema = factory.getSchema(schemaData, InputFormat.YAML, config); Set messages = schema.validate(inputData, InputFormat.YAML, executionContext -> { executionContext.getExecutionConfig().setFormatAssertionsEnabled(true); diff --git a/src/test/java/com/networknt/schema/walk/JsonSchemaWalkListenerTest.java b/src/test/java/com/networknt/schema/walk/JsonSchemaWalkListenerTest.java index af19ac0c3..eb31a9079 100644 --- a/src/test/java/com/networknt/schema/walk/JsonSchemaWalkListenerTest.java +++ b/src/test/java/com/networknt/schema/walk/JsonSchemaWalkListenerTest.java @@ -42,7 +42,6 @@ import com.networknt.schema.JsonSchema; import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.JsonSchemaRef; -import com.networknt.schema.PathType; import com.networknt.schema.PropertiesValidator; import com.networknt.schema.SchemaId; import com.networknt.schema.SchemaLocation; @@ -87,24 +86,25 @@ void keywordListener() { + " }\r\n" + " }\r\n" + "}"; - - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - @SuppressWarnings("unchecked") - List propertyKeywords = (List) walkEvent.getExecutionContext().getCollectorContext() - .getCollectorMap().computeIfAbsent("propertyKeywords", key -> new ArrayList<>()); - propertyKeywords.add(walkEvent); - return WalkFlow.CONTINUE; - } - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - } - }); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .keywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + @SuppressWarnings("unchecked") + List propertyKeywords = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("propertyKeywords", key -> new ArrayList<>()); + propertyKeywords.add(walkEvent); + return WalkFlow.CONTINUE; + } + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + } + }) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V7).getSchema(schemaData, config); String inputData = "{\r\n" + " \"tags\": [\r\n" @@ -167,23 +167,24 @@ void propertyListener() { + " }\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addPropertyWalkListener(new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - @SuppressWarnings("unchecked") - List properties = (List) walkEvent.getExecutionContext().getCollectorContext() - .getCollectorMap().computeIfAbsent("properties", key -> new ArrayList<>()); - properties.add(walkEvent); - return WalkFlow.CONTINUE; - } + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .propertyWalkListener(new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + @SuppressWarnings("unchecked") + List properties = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("properties", key -> new ArrayList<>()); + properties.add(walkEvent); + return WalkFlow.CONTINUE; + } - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - } - }); + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + } + }) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V7).getSchema(schemaData, config); String inputData = "{\r\n" + " \"tags\": [\r\n" @@ -253,15 +254,14 @@ void itemsListener() { + " }\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addItemWalkListener(new JsonSchemaWalkListener() { - + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().itemWalkListener(new JsonSchemaWalkListener() { @Override public WalkFlow onWalkStart(WalkEvent walkEvent) { @SuppressWarnings("unchecked") - List items = (List) walkEvent.getExecutionContext().getCollectorContext() - .getCollectorMap().computeIfAbsent("items", key -> new ArrayList<>()); + List items = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("items", key -> new ArrayList<>()); items.add(walkEvent); return WalkFlow.CONTINUE; } @@ -269,7 +269,7 @@ public WalkFlow onWalkStart(WalkEvent walkEvent) { @Override public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { } - }); + }).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V7).getSchema(schemaData, config); String inputData = "{\r\n" + " \"tags\": [\r\n" @@ -327,15 +327,14 @@ void items202012Listener() { + " }\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addItemWalkListener(new JsonSchemaWalkListener() { - + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder().itemWalkListener(new JsonSchemaWalkListener() { @Override public WalkFlow onWalkStart(WalkEvent walkEvent) { @SuppressWarnings("unchecked") - List items = (List) walkEvent.getExecutionContext().getCollectorContext() - .getCollectorMap().computeIfAbsent("items", key -> new ArrayList<>()); + List items = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("items", key -> new ArrayList<>()); items.add(walkEvent); return WalkFlow.CONTINUE; } @@ -343,7 +342,7 @@ public WalkFlow onWalkStart(WalkEvent walkEvent) { @Override public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { } - }); + }).build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V7).getSchema(schemaData, config); String inputData = "{\r\n" + " \"tags\": [\r\n" @@ -375,23 +374,24 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess @Test void draft201909() { - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setPathType(PathType.JSON_POINTER); - config.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - @SuppressWarnings("unchecked") - List propertyKeywords = (List) walkEvent.getExecutionContext().getCollectorContext() - .getCollectorMap().computeIfAbsent("propertyKeywords", key -> new ArrayList<>()); - propertyKeywords.add(walkEvent); - return WalkFlow.CONTINUE; - } - - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - } - }); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .keywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + @SuppressWarnings("unchecked") + List propertyKeywords = (List) walkEvent.getExecutionContext() + .getCollectorContext() + .getCollectorMap() + .computeIfAbsent("propertyKeywords", key -> new ArrayList<>()); + propertyKeywords.add(walkEvent); + return WalkFlow.CONTINUE; + } + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + } + }) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V201909) .getSchema(SchemaLocation.of(SchemaId.V201909), config); String inputData = "{\r\n" @@ -553,8 +553,9 @@ void applyDefaults() throws JsonMappingException, JsonProcessingException { + " }\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.setApplyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .applyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); JsonNode inputNode = JsonMapperFactory.getInstance().readTree("{}"); ValidationResult result = schema.walk(inputNode, true); @@ -585,33 +586,33 @@ void applyDefaultsWithWalker() throws JsonMappingException, JsonProcessingExcept + " }\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.addPropertyWalkListener(new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - if (walkEvent.getInstanceNode() == null || walkEvent.getInstanceNode().isMissingNode() - || walkEvent.getInstanceNode().isNull()) { - JsonSchema schema = walkEvent.getSchema(); - JsonSchemaRef schemaRef = JsonSchemaRefs.from(schema); - if (schemaRef != null) { - schema = schemaRef.getSchema(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .propertyWalkListener(new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + if (walkEvent.getInstanceNode() == null || walkEvent.getInstanceNode().isMissingNode() + || walkEvent.getInstanceNode().isNull()) { + JsonSchema schema = walkEvent.getSchema(); + JsonSchemaRef schemaRef = JsonSchemaRefs.from(schema); + if (schemaRef != null) { + schema = schemaRef.getSchema(); + } + JsonNode defaultNode = schema.getSchemaNode().get("default"); + if (defaultNode != null) { + ObjectNode parentNode = (ObjectNode) JsonNodes.get(walkEvent.getRootNode(), + walkEvent.getInstanceLocation().getParent()); + parentNode.set(walkEvent.getInstanceLocation().getName(-1), defaultNode); + } + } + return WalkFlow.CONTINUE; } - JsonNode defaultNode = schema.getSchemaNode().get("default"); - if (defaultNode != null) { - ObjectNode parentNode = (ObjectNode) JsonNodes.get(walkEvent.getRootNode(), - walkEvent.getInstanceLocation().getParent()); - parentNode.set(walkEvent.getInstanceLocation().getName(-1), defaultNode); + + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { } - } - return WalkFlow.CONTINUE; - } + }) + .build(); - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - } - }); - JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); JsonNode inputNode = JsonMapperFactory.getInstance().readTree("{}"); ValidationResult result = schema.walk(inputNode, true); @@ -642,32 +643,32 @@ void applyInvalidDefaultsWithWalker() throws JsonMappingException, JsonProcessin + " }\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.addPropertyWalkListener(new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - if (walkEvent.getInstanceNode() == null || walkEvent.getInstanceNode().isMissingNode() - || walkEvent.getInstanceNode().isNull()) { - JsonSchema schema = walkEvent.getSchema(); - JsonSchemaRef schemaRef = JsonSchemaRefs.from(schema); - if (schemaRef != null) { - schema = schemaRef.getSchema(); - } - JsonNode defaultNode = schema.getSchemaNode().get("default"); - if (defaultNode != null) { - ObjectNode parentNode = (ObjectNode) JsonNodes.get(walkEvent.getRootNode(), - walkEvent.getInstanceLocation().getParent()); - parentNode.set(walkEvent.getInstanceLocation().getName(-1), defaultNode); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .propertyWalkListener(new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + if (walkEvent.getInstanceNode() == null || walkEvent.getInstanceNode().isMissingNode() + || walkEvent.getInstanceNode().isNull()) { + JsonSchema schema = walkEvent.getSchema(); + JsonSchemaRef schemaRef = JsonSchemaRefs.from(schema); + if (schemaRef != null) { + schema = schemaRef.getSchema(); + } + JsonNode defaultNode = schema.getSchemaNode().get("default"); + if (defaultNode != null) { + ObjectNode parentNode = (ObjectNode) JsonNodes.get(walkEvent.getRootNode(), + walkEvent.getInstanceLocation().getParent()); + parentNode.set(walkEvent.getInstanceLocation().getName(-1), defaultNode); + } + } + return WalkFlow.CONTINUE; } - } - return WalkFlow.CONTINUE; - } - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - } - }); + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + } + }) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); JsonNode inputNode = JsonMapperFactory.getInstance().readTree("{}"); @@ -702,40 +703,40 @@ void missingRequired() throws JsonMappingException, JsonProcessingException { + " }\r\n" + "}"; Map missingSchemaNode = new LinkedHashMap<>(); - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.addKeywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new JsonSchemaWalkListener() { - - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - JsonNode requiredNode = walkEvent.getSchema().getSchemaNode().get("required"); - List requiredProperties = new ArrayList<>(); - if (requiredNode != null) { - if (requiredNode.isArray()) { - for (JsonNode fieldName : requiredNode) { - requiredProperties.add(fieldName.asText()); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .keywordWalkListener(ValidatorTypeCode.PROPERTIES.getValue(), new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + JsonNode requiredNode = walkEvent.getSchema().getSchemaNode().get("required"); + List requiredProperties = new ArrayList<>(); + if (requiredNode != null) { + if (requiredNode.isArray()) { + for (JsonNode fieldName : requiredNode) { + requiredProperties.add(fieldName.asText()); + } + } } - } - } - for (String requiredProperty : requiredProperties) { - JsonNode propertyNode = walkEvent.getInstanceNode().get(requiredProperty); - if (propertyNode == null) { - // Get the schema - PropertiesValidator propertiesValidator = walkEvent.getValidator(); - JsonSchema propertySchema = propertiesValidator.getSchemas().get(requiredProperty); - JsonSchemaRef schemaRef = JsonSchemaRefs.from(propertySchema); - if (schemaRef != null) { - propertySchema = schemaRef.getSchema(); + for (String requiredProperty : requiredProperties) { + JsonNode propertyNode = walkEvent.getInstanceNode().get(requiredProperty); + if (propertyNode == null) { + // Get the schema + PropertiesValidator propertiesValidator = walkEvent.getValidator(); + JsonSchema propertySchema = propertiesValidator.getSchemas().get(requiredProperty); + JsonSchemaRef schemaRef = JsonSchemaRefs.from(propertySchema); + if (schemaRef != null) { + propertySchema = schemaRef.getSchema(); + } + missingSchemaNode.put(requiredProperty, propertySchema.getSchemaNode()); + } } - missingSchemaNode.put(requiredProperty, propertySchema.getSchemaNode()); + return WalkFlow.CONTINUE; } - } - return WalkFlow.CONTINUE; - } - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - } - }); + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + } + }) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); JsonNode inputNode = JsonMapperFactory.getInstance().readTree("{}"); @@ -774,37 +775,38 @@ void generateDataWithWalker() throws JsonMappingException, JsonProcessingExcepti + " }\r\n" + "}"; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.addPropertyWalkListener(new JsonSchemaWalkListener() { - @Override - public WalkFlow onWalkStart(WalkEvent walkEvent) { - if (walkEvent.getInstanceNode() == null || walkEvent.getInstanceNode().isMissingNode() - || walkEvent.getInstanceNode().isNull()) { - JsonSchema schema = walkEvent.getSchema(); - JsonSchemaRef schemaRef = null; - do { - schemaRef = JsonSchemaRefs.from(schema); - if (schemaRef != null) { - schema = schemaRef.getSchema(); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .propertyWalkListener(new JsonSchemaWalkListener() { + @Override + public WalkFlow onWalkStart(WalkEvent walkEvent) { + if (walkEvent.getInstanceNode() == null || walkEvent.getInstanceNode().isMissingNode() + || walkEvent.getInstanceNode().isNull()) { + JsonSchema schema = walkEvent.getSchema(); + JsonSchemaRef schemaRef = null; + do { + schemaRef = JsonSchemaRefs.from(schema); + if (schemaRef != null) { + schema = schemaRef.getSchema(); + } + } while (schemaRef != null); + JsonNode fakerNode = schema.getSchemaNode().get("faker"); + if (fakerNode != null) { + String faker = fakerNode.asText(); + String fakeData = generators.get(faker).get(); + JsonNode fakeDataNode = JsonNodeFactory.instance.textNode(fakeData); + ObjectNode parentNode = (ObjectNode) JsonNodes.get(walkEvent.getRootNode(), + walkEvent.getInstanceLocation().getParent()); + parentNode.set(walkEvent.getInstanceLocation().getName(-1), fakeDataNode); + } } - } while (schemaRef != null); - JsonNode fakerNode = schema.getSchemaNode().get("faker"); - if (fakerNode != null) { - String faker = fakerNode.asText(); - String fakeData = generators.get(faker).get(); - JsonNode fakeDataNode = JsonNodeFactory.instance.textNode(fakeData); - ObjectNode parentNode = (ObjectNode) JsonNodes.get(walkEvent.getRootNode(), - walkEvent.getInstanceLocation().getParent()); - parentNode.set(walkEvent.getInstanceLocation().getName(-1), fakeDataNode); + return WalkFlow.CONTINUE; } - } - return WalkFlow.CONTINUE; - } - @Override - public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { - } - }); + @Override + public void onWalkEnd(WalkEvent walkEvent, Set validationMessages) { + } + }) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); JsonNode inputNode = JsonMapperFactory.getInstance().readTree("{}"); @@ -853,24 +855,25 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess items.add(walkEvent); } }; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.addItemWalkListener(listener); - config.addPropertyWalkListener(listener); + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .itemWalkListener(listener) + .propertyWalkListener(listener) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V201909).getSchema(schemaData, config); ValidationResult result = schema.walk(null, true); @SuppressWarnings("unchecked") List items = (List) result.getExecutionContext().getCollectorContext().get("items"); assertEquals(4, items.size()); - assertEquals("$.name", items.get(0).getInstanceLocation().toString()); + assertEquals("/name", items.get(0).getInstanceLocation().toString()); assertEquals("properties", items.get(0).getKeyword()); assertEquals("#/properties/name", items.get(0).getSchema().getSchemaLocation().toString()); - assertEquals("$.children[0].name", items.get(1).getInstanceLocation().toString()); + assertEquals("/children/0/name", items.get(1).getInstanceLocation().toString()); assertEquals("properties", items.get(1).getKeyword()); assertEquals("#/properties/children/items/properties/name", items.get(1).getSchema().getSchemaLocation().toString()); - assertEquals("$.children[0]", items.get(2).getInstanceLocation().toString()); + assertEquals("/children/0", items.get(2).getInstanceLocation().toString()); assertEquals("items", items.get(2).getKeyword()); assertEquals("#/properties/children/items", items.get(2).getSchema().getSchemaLocation().toString()); - assertEquals("$.children", items.get(3).getInstanceLocation().toString()); + assertEquals("/children", items.get(3).getInstanceLocation().toString()); assertEquals("properties", items.get(3).getKeyword()); assertEquals("#/properties/children", items.get(3).getSchema().getSchemaLocation().toString()); } @@ -914,25 +917,26 @@ public void onWalkEnd(WalkEvent walkEvent, Set validationMess .computeIfAbsent("items", key -> new ArrayList()); items.add(walkEvent); } - }; - SchemaValidatorsConfig config = new SchemaValidatorsConfig(); - config.addItemWalkListener(listener); - config.addPropertyWalkListener(listener); + }; + SchemaValidatorsConfig config = SchemaValidatorsConfig.builder() + .itemWalkListener(listener) + .propertyWalkListener(listener) + .build(); JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config); ValidationResult result = schema.walk(null, true); @SuppressWarnings("unchecked") List items = (List) result.getExecutionContext().getCollectorContext().get("items"); assertEquals(4, items.size()); - assertEquals("$.name", items.get(0).getInstanceLocation().toString()); + assertEquals("/name", items.get(0).getInstanceLocation().toString()); assertEquals("properties", items.get(0).getKeyword()); assertEquals("#/properties/name", items.get(0).getSchema().getSchemaLocation().toString()); - assertEquals("$.children[0].name", items.get(1).getInstanceLocation().toString()); + assertEquals("/children/0/name", items.get(1).getInstanceLocation().toString()); assertEquals("properties", items.get(1).getKeyword()); assertEquals("#/properties/children/items/properties/name", items.get(1).getSchema().getSchemaLocation().toString()); - assertEquals("$.children[0]", items.get(2).getInstanceLocation().toString()); + assertEquals("/children/0", items.get(2).getInstanceLocation().toString()); assertEquals("items", items.get(2).getKeyword()); assertEquals("#/properties/children/items", items.get(2).getSchema().getSchemaLocation().toString()); - assertEquals("$.children", items.get(3).getInstanceLocation().toString()); + assertEquals("/children", items.get(3).getInstanceLocation().toString()); assertEquals("properties", items.get(3).getKeyword()); assertEquals("#/properties/children", items.get(3).getSchema().getSchemaLocation().toString()); } diff --git a/src/test/resources/draft2019-09/invalid-min-max-contains.json b/src/test/resources/draft2019-09/invalid-min-max-contains.json index 5bb15f114..17fa59c3f 100644 --- a/src/test/resources/draft2019-09/invalid-min-max-contains.json +++ b/src/test/resources/draft2019-09/invalid-min-max-contains.json @@ -11,7 +11,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"minContains\":\"1\"}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"minContains\":\"1\"}" ] } ] @@ -28,7 +28,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"minContains\":0.5}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"minContains\":0.5}" ] } ] @@ -45,7 +45,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"minContains\":-1}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"minContains\":-1}" ] } ] @@ -62,7 +62,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"maxContains\":\"1\"}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"maxContains\":\"1\"}" ] } ] @@ -79,7 +79,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"maxContains\":0.5}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"maxContains\":0.5}" ] } ] @@ -96,7 +96,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"maxContains\":-1}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"maxContains\":-1}" ] } ] @@ -114,8 +114,8 @@ "data": [], "valid": false, "validationMessages": [ - "$: minContains must less than or equal to maxContains in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"maxContains\":0,\"minContains\":1}", - "$: minContains must less than or equal to maxContains in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"maxContains\":0,\"minContains\":1}" + ": minContains must less than or equal to maxContains in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"maxContains\":0,\"minContains\":1}", + ": minContains must less than or equal to maxContains in {\"$schema\":\"https://json-schema.org/draft/2019-09/schema\",\"maxContains\":0,\"minContains\":1}" ] } ] diff --git a/src/test/resources/draft2020-12/invalid-min-max-contains.json b/src/test/resources/draft2020-12/invalid-min-max-contains.json index 4a3ee68dd..84fae3cec 100644 --- a/src/test/resources/draft2020-12/invalid-min-max-contains.json +++ b/src/test/resources/draft2020-12/invalid-min-max-contains.json @@ -11,7 +11,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"minContains\":\"1\"}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"minContains\":\"1\"}" ] } ] @@ -28,7 +28,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"minContains\":0.5}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"minContains\":0.5}" ] } ] @@ -45,7 +45,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"minContains\":-1}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"minContains\":-1}" ] } ] @@ -62,7 +62,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"maxContains\":\"1\"}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"maxContains\":\"1\"}" ] } ] @@ -79,7 +79,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"maxContains\":0.5}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"maxContains\":0.5}" ] } ] @@ -96,7 +96,7 @@ "data": [], "valid": false, "validationMessages": [ - "$: must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"maxContains\":-1}" + ": must be a non-negative integer in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"maxContains\":-1}" ] } ] @@ -114,8 +114,8 @@ "data": [], "valid": false, "validationMessages": [ - "$: minContains must less than or equal to maxContains in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"maxContains\":0,\"minContains\":1}", - "$: minContains must less than or equal to maxContains in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"maxContains\":0,\"minContains\":1}" + ": minContains must less than or equal to maxContains in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"maxContains\":0,\"minContains\":1}", + ": minContains must less than or equal to maxContains in {\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"maxContains\":0,\"minContains\":1}" ] } ] diff --git a/src/test/resources/draft2020-12/issue656.json b/src/test/resources/draft2020-12/issue656.json index ca23935b5..e46dd6cbd 100644 --- a/src/test/resources/draft2020-12/issue656.json +++ b/src/test/resources/draft2020-12/issue656.json @@ -155,7 +155,7 @@ }, "valid": false, "validationMessages": [ - "$: must be valid to one and only one schema, but 2 are valid with indexes '0, 1'" + ": must be valid to one and only one schema, but 2 are valid with indexes '0, 1'" ] } ] diff --git a/src/test/resources/draft2020-12/issue798.json b/src/test/resources/draft2020-12/issue798.json index 5e2402beb..20d500336 100644 --- a/src/test/resources/draft2020-12/issue798.json +++ b/src/test/resources/draft2020-12/issue798.json @@ -22,14 +22,14 @@ "data": { "a": "a string", "b": "a string" }, "valid": false, "config": { "readOnly": true, "writeOnly": true }, - "validationMessages": [ "$.b: is a readonly field, it cannot be changed" ] + "validationMessages": [ "/b: is a readonly field, it cannot be changed" ] }, { "description": "write-only behavior", "data": { "a": "a string", "c": "a string" }, "valid": false, "config": { "readOnly": true, "writeOnly": true }, - "validationMessages": [ "$.c: is a write-only field, it cannot appear in the data" ] + "validationMessages": [ "/c: is a write-only field, it cannot appear in the data" ] }, { "description": "both behavior", @@ -37,8 +37,8 @@ "valid": false, "config": { "readOnly": true, "writeOnly": true }, "validationMessages": [ - "$.d: is a readonly field, it cannot be changed", - "$.d: is a write-only field, it cannot appear in the data" + "/d: is a readonly field, it cannot be changed", + "/d: is a write-only field, it cannot appear in the data" ] } ] diff --git a/src/test/resources/draft4/issue425.json b/src/test/resources/draft4/issue425.json index 70e18fea5..c9c614337 100644 --- a/src/test/resources/draft4/issue425.json +++ b/src/test/resources/draft4/issue425.json @@ -1,6 +1,6 @@ [ { - "description": "Nuallable oneOf validation", + "description": "Nullable oneOf validation", "schema": { "properties": { "values": { @@ -35,9 +35,9 @@ }, "valid": false, "validationMessages": [ - "$.values: must be valid to one and only one schema, but 0 are valid", - "$.values: integer found, array expected", - "$.values: integer found, string expected" + "/values: must be valid to one and only one schema, but 0 are valid", + "/values: integer found, array expected", + "/values: integer found, string expected" ] }, { diff --git a/src/test/resources/draft7/issue470.json b/src/test/resources/draft7/issue470.json index 8dbbfa6d8..d5ced20c4 100644 --- a/src/test/resources/draft7/issue470.json +++ b/src/test/resources/draft7/issue470.json @@ -84,9 +84,9 @@ }, "valid": false, "validationMessages": [ - "$.search: must be valid to one and only one schema, but 0 are valid", - "$.search: property 'byName' is not defined in the schema and the schema does not allow additional properties", - "$.search.byName.name: integer found, string expected" + "/search: must be valid to one and only one schema, but 0 are valid", + "/search: property 'byName' is not defined in the schema and the schema does not allow additional properties", + "/search/byName/name: integer found, string expected" ] }, { @@ -100,9 +100,9 @@ }, "valid": false, "validationMessages": [ - "$.search: must be valid to one and only one schema, but 0 are valid", - "$.search: property 'byName' is not defined in the schema and the schema does not allow additional properties", - "$.search.byName.name: must be at most 20 characters long" + "/search: must be valid to one and only one schema, but 0 are valid", + "/search: property 'byName' is not defined in the schema and the schema does not allow additional properties", + "/search/byName/name: must be at most 20 characters long" ] }, { @@ -116,9 +116,9 @@ }, "valid": false, "validationMessages": [ - "$.search: must be valid to one and only one schema, but 0 are valid", - "$.search.byAge.age: string found, integer expected", - "$.search: property 'byAge' is not defined in the schema and the schema does not allow additional properties" + "/search: must be valid to one and only one schema, but 0 are valid", + "/search/byAge/age: string found, integer expected", + "/search: property 'byAge' is not defined in the schema and the schema does not allow additional properties" ] }, { @@ -132,9 +132,9 @@ }, "valid": false, "validationMessages": [ - "$.search: must be valid to one and only one schema, but 0 are valid", - "$.search.byAge.age: must have a maximum value of 150", - "$.search: property 'byAge' is not defined in the schema and the schema does not allow additional properties" + "/search: must be valid to one and only one schema, but 0 are valid", + "/search/byAge/age: must have a maximum value of 150", + "/search: property 'byAge' is not defined in the schema and the schema does not allow additional properties" ] } ] diff --git a/src/test/resources/draft7/issue491.json b/src/test/resources/draft7/issue491.json index b18463dde..68562bdb5 100644 --- a/src/test/resources/draft7/issue491.json +++ b/src/test/resources/draft7/issue491.json @@ -82,9 +82,9 @@ }, "valid": false, "validationMessages": [ - "$.search: required property 'name' not found", - "$.search.searchAge.age: string found, integer expected", - "$.search: must be valid to one and only one schema, but 0 are valid" + "/search: required property 'name' not found", + "/search/searchAge/age: string found, integer expected", + "/search: must be valid to one and only one schema, but 0 are valid" ] }, { @@ -96,9 +96,9 @@ }, "valid": false, "validationMessages": [ - "$.search.name: integer found, string expected", - "$.search: required property 'searchAge' not found", - "$.search: must be valid to one and only one schema, but 0 are valid" + "/search/name: integer found, string expected", + "/search: required property 'searchAge' not found", + "/search: must be valid to one and only one schema, but 0 are valid" ] } ] @@ -184,9 +184,9 @@ }, "valid": false, "validationMessages": [ - "$.search: required property 'name' not found", - "$.search.byAge.age: string found, integer expected", - "$.search: must be valid to one and only one schema, but 0 are valid" + "/search: required property 'name' not found", + "/search/byAge/age: string found, integer expected", + "/search: must be valid to one and only one schema, but 0 are valid" ] }, { @@ -198,9 +198,9 @@ }, "valid": false, "validationMessages": [ - "$.search.name: integer found, string expected", - "$.search: required property 'byAge' not found", - "$.search: must be valid to one and only one schema, but 0 are valid" + "/search/name: integer found, string expected", + "/search: required property 'byAge' not found", + "/search: must be valid to one and only one schema, but 0 are valid" ] } ] @@ -276,9 +276,9 @@ }, "valid": false, "validationMessages": [ - "$.search: required property 'name' not found", - "$.search.age: string found, integer expected", - "$.search: must be valid to one and only one schema, but 0 are valid" + "/search: required property 'name' not found", + "/search/age: string found, integer expected", + "/search: must be valid to one and only one schema, but 0 are valid" ] }, { @@ -290,9 +290,9 @@ }, "valid": false, "validationMessages": [ - "$.search.name: integer found, string expected", - "$.search: required property 'age' not found", - "$.search: must be valid to one and only one schema, but 0 are valid" + "/search/name: integer found, string expected", + "/search: required property 'age' not found", + "/search: must be valid to one and only one schema, but 0 are valid" ] }, { @@ -304,9 +304,9 @@ }, "valid": false, "validationMessages": [ - "$.search: required property 'name' not found", - "$.search.age: must have a maximum value of 150", - "$.search: must be valid to one and only one schema, but 0 are valid" + "/search: required property 'name' not found", + "/search/age: must have a maximum value of 150", + "/search: must be valid to one and only one schema, but 0 are valid" ] }, { @@ -318,9 +318,9 @@ }, "valid": false, "validationMessages": [ - "$.search.name: must be at most 20 characters long", - "$.search: required property 'age' not found", - "$.search: must be valid to one and only one schema, but 0 are valid" + "/search/name: must be at most 20 characters long", + "/search: required property 'age' not found", + "/search: must be valid to one and only one schema, but 0 are valid" ] } ] diff --git a/src/test/resources/draft7/issue516.json b/src/test/resources/draft7/issue516.json index f7a9de4b3..9556574b0 100644 --- a/src/test/resources/draft7/issue516.json +++ b/src/test/resources/draft7/issue516.json @@ -119,24 +119,24 @@ }, "valid": false, "validationMessages": [ - "$.activities[0]: must be valid to one and only one schema, but 0 are valid", - "$.activities[0]: property 'age' is not defined in the schema and the schema does not allow additional properties", - "$.activities[0]: required property 'chemicalCharacteristic' not found", - "$.activities[0]: property 'height' is not defined in the schema and the schema does not allow additional properties", - "$.activities[0]: required property 'weight' not found", - "$.activities[1]: must be valid to one and only one schema, but 0 are valid", - "$.activities[1]: property 'chemicalCharacteristic' is not defined in the schema and the schema does not allow additional properties", - "$.activities[1]: required property 'height' not found", - "$.activities[1]: property 'toxic' is not defined in the schema and the schema does not allow additional properties", - "$.activities[1]: required property 'weight' not found", - "$.activities[2]: must be valid to one and only one schema, but 0 are valid", - "$.activities[2]: property 'chemicalCharacteristic' is not defined in the schema and the schema does not allow additional properties", - "$.activities[2].chemicalCharacteristic: must be valid to one and only one schema, but 0 are valid", - "$.activities[2].chemicalCharacteristic: property 'categoryName' is not defined in the schema and the schema does not allow additional properties", - "$.activities[2].chemicalCharacteristic: property 'name' is not defined in the schema and the schema does not allow additional properties", - "$.activities[2]: required property 'height' not found", - "$.activities[2]: property 'toxic' is not defined in the schema and the schema does not allow additional properties", - "$.activities[2]: required property 'weight' not found" + "/activities/0: must be valid to one and only one schema, but 0 are valid", + "/activities/0: property 'age' is not defined in the schema and the schema does not allow additional properties", + "/activities/0: required property 'chemicalCharacteristic' not found", + "/activities/0: property 'height' is not defined in the schema and the schema does not allow additional properties", + "/activities/0: required property 'weight' not found", + "/activities/1: must be valid to one and only one schema, but 0 are valid", + "/activities/1: property 'chemicalCharacteristic' is not defined in the schema and the schema does not allow additional properties", + "/activities/1: required property 'height' not found", + "/activities/1: property 'toxic' is not defined in the schema and the schema does not allow additional properties", + "/activities/1: required property 'weight' not found", + "/activities/2: must be valid to one and only one schema, but 0 are valid", + "/activities/2: property 'chemicalCharacteristic' is not defined in the schema and the schema does not allow additional properties", + "/activities/2/chemicalCharacteristic: must be valid to one and only one schema, but 0 are valid", + "/activities/2/chemicalCharacteristic: property 'categoryName' is not defined in the schema and the schema does not allow additional properties", + "/activities/2/chemicalCharacteristic: property 'name' is not defined in the schema and the schema does not allow additional properties", + "/activities/2: required property 'height' not found", + "/activities/2: property 'toxic' is not defined in the schema and the schema does not allow additional properties", + "/activities/2: required property 'weight' not found" ] } ] diff --git a/src/test/resources/draft7/issue653.json b/src/test/resources/draft7/issue653.json index 4ee9e29d5..6e7f7180b 100644 --- a/src/test/resources/draft7/issue653.json +++ b/src/test/resources/draft7/issue653.json @@ -80,9 +80,9 @@ }, "valid": false, "validationMessages": [ - "$.pets[0]: must be valid to one and only one schema, but 0 are valid", - "$.pets[0]: required property 'age' not found", - "$.pets[0]: schema for 'else' is false" + "/pets/0: must be valid to one and only one schema, but 0 are valid", + "/pets/0: required property 'age' not found", + "/pets/0: schema for 'else' is false" ] } ] diff --git a/src/test/resources/draft7/issue678.json b/src/test/resources/draft7/issue678.json index b220968bf..d743f6263 100644 --- a/src/test/resources/draft7/issue678.json +++ b/src/test/resources/draft7/issue678.json @@ -49,10 +49,10 @@ }, "valid": false, "validationMessages": [ - "$.outerObject.innerObject: object found, string expected", - "$.outerObject.innerObject: required property 'value' not found", - "$.outerObject.innerObject: required property 'unit' not found", - "$.outerObject.innerObject: must be valid to one and only one schema, but 0 are valid" + "/outerObject/innerObject: object found, string expected", + "/outerObject/innerObject: required property 'value' not found", + "/outerObject/innerObject: required property 'unit' not found", + "/outerObject/innerObject: must be valid to one and only one schema, but 0 are valid" ] } ] diff --git a/src/test/resources/schema/customMessageTests/custom-message-disabled-tests.json b/src/test/resources/schema/customMessageTests/custom-message-disabled-tests.json index 9af1c5385..a8df5a767 100644 --- a/src/test/resources/schema/customMessageTests/custom-message-disabled-tests.json +++ b/src/test/resources/schema/customMessageTests/custom-message-disabled-tests.json @@ -57,7 +57,7 @@ }, "valid": false, "validationMessages": [ - "$.bar: integer found, string expected" + "/bar: integer found, string expected" ] }, { @@ -71,7 +71,7 @@ }, "valid": false, "validationMessages": [ - "$.foo[2]: string found, number expected" + "/foo/2: string found, number expected" ] }, { @@ -85,7 +85,7 @@ }, "valid": false, "validationMessages": [ - "$.foo: must have at most 3 items but found 4" + "/foo: must have at most 3 items but found 4" ] }, { @@ -99,8 +99,8 @@ }, "valid": false, "validationMessages": [ - "$.foo[2]: string found, number expected", - "$.bar: integer found, string expected" + "/foo/2: string found, number expected", + "/bar: integer found, string expected" ] }, { @@ -114,9 +114,9 @@ }, "valid": false, "validationMessages": [ - "$.foo: must have at most 3 items but found 4", - "$.foo[2]: string found, number expected", - "$.bar: integer found, string expected" + "/foo: must have at most 3 items but found 4", + "/foo/2: string found, number expected", + "/bar: integer found, string expected" ] } ] diff --git a/src/test/resources/schema/customMessageTests/custom-message-tests.json b/src/test/resources/schema/customMessageTests/custom-message-tests.json index 1c3e90629..18e1ef138 100644 --- a/src/test/resources/schema/customMessageTests/custom-message-tests.json +++ b/src/test/resources/schema/customMessageTests/custom-message-tests.json @@ -42,6 +42,9 @@ }, { "description": "Custom error message for keyword failing at top level - type keyword", + "config": { + "isCustomMessageSupported": true + }, "data": { "foo": [1, 2, 3], "bar": 123 @@ -53,6 +56,9 @@ }, { "description": "Custom error message for keyword failing at nested property level - type keyword", + "config": { + "isCustomMessageSupported": true + }, "data": { "foo": [1, 2, "text"], "bar": "Bar 1" @@ -64,6 +70,9 @@ }, { "description": "Custom error message for keyword failing at nested property level - maxItems keyword", + "config": { + "isCustomMessageSupported": true + }, "data": { "foo": [1, 2, 3, 4], "bar": "Bar 1" @@ -75,6 +84,9 @@ }, { "description": "Custom error message for keyword failing at both top level and nested property level - type keyword", + "config": { + "isCustomMessageSupported": true + }, "data": { "foo": [1, 2, "text"], "bar": 123 @@ -87,6 +99,9 @@ }, { "description": "Custom error message for keyword failing at both top level and nested property level - type keyword", + "config": { + "isCustomMessageSupported": true + }, "data": { "foo": [1, 2, "text", 3], "bar": 123 @@ -124,32 +139,41 @@ "tests": [ { "description": "bar is required", + "config": { + "isCustomMessageSupported": true + }, "data": { "foo": 1 }, "valid": false, "validationMessages": [ - "$: 'bar' is required" + ": 'bar' is required" ] }, { "description": "foo is required", + "config": { + "isCustomMessageSupported": true + }, "data": { "bar": "bar" }, "valid": false, "validationMessages": [ - "$: 'foo' is required" + ": 'foo' is required" ] }, { "description": "both foo and bar are required", + "config": { + "isCustomMessageSupported": true + }, "data": { }, "valid": false, "validationMessages": [ - "$: 'foo' is required", - "$: 'bar' is required" + ": 'foo' is required", + ": 'bar' is required" ] } ] @@ -179,6 +203,9 @@ "tests": [ { "description": "should not be empty", + "config": { + "isCustomMessageSupported": true + }, "data": { "requestedItems": [ { @@ -188,7 +215,7 @@ }, "valid": false, "validationMessages": [ - "$.requestedItems[0].item: Item should not be empty" + "/requestedItems/0/item: Item should not be empty" ] } ] diff --git a/src/test/resources/schema/unevaluatedTests/unevaluated-tests.json b/src/test/resources/schema/unevaluatedTests/unevaluated-tests.json index ea263fc03..277db639f 100644 --- a/src/test/resources/schema/unevaluatedTests/unevaluated-tests.json +++ b/src/test/resources/schema/unevaluatedTests/unevaluated-tests.json @@ -89,7 +89,7 @@ }, "valid": false, "validationMessages": [ - "$: property 'invalid' is not evaluated and the schema does not allow unevaluated properties" + ": property 'invalid' is not evaluated and the schema does not allow unevaluated properties" ] }, { @@ -106,7 +106,7 @@ }, "valid": false, "validationMessages": [ - "$.address: property 'invalid' is not evaluated and the schema does not allow unevaluated properties" + "/address: property 'invalid' is not evaluated and the schema does not allow unevaluated properties" ] }, { @@ -123,7 +123,7 @@ }, "valid": false, "validationMessages": [ - "$.address: property 'invalid2' is not evaluated and the schema does not allow unevaluated properties" + "/address: property 'invalid2' is not evaluated and the schema does not allow unevaluated properties" ] }, { @@ -142,7 +142,7 @@ }, "valid": false, "validationMessages": [ - "$.address.residence: property 'invalid' is not evaluated and the schema does not allow unevaluated properties" + "/address/residence: property 'invalid' is not evaluated and the schema does not allow unevaluated properties" ] } ] @@ -237,7 +237,7 @@ }, "valid": false, "validationMessages": [ - "$.vehicle: property 'wheels' is not evaluated and the schema does not allow unevaluated properties" + "/vehicle: property 'wheels' is not evaluated and the schema does not allow unevaluated properties" ] }, { @@ -253,9 +253,9 @@ }, "valid": false, "validationMessages": [ - "$.vehicle: must be valid to one and only one schema, but 2 are valid with indexes '1, 2'", - "$.vehicle: required property 'wheels' not found", - "$.vehicle: required property 'headlights' not found" + "/vehicle: must be valid to one and only one schema, but 2 are valid with indexes '1, 2'", + "/vehicle: required property 'wheels' not found", + "/vehicle: required property 'headlights' not found" ] }, { @@ -272,10 +272,10 @@ }, "valid": false, "validationMessages": [ - "$.vehicle: must be valid to one and only one schema, but 2 are valid with indexes '1, 2'", - "$.vehicle: property 'invalid' is not evaluated and the schema does not allow unevaluated properties", - "$.vehicle: required property 'wheels' not found", - "$.vehicle: required property 'headlights' not found" + "/vehicle: must be valid to one and only one schema, but 2 are valid with indexes '1, 2'", + "/vehicle: property 'invalid' is not evaluated and the schema does not allow unevaluated properties", + "/vehicle: required property 'wheels' not found", + "/vehicle: required property 'headlights' not found" ] }, { @@ -290,8 +290,8 @@ }, "valid": false, "validationMessages": [ - "$.vehicle: must be valid to one and only one schema, but 0 are valid", - "$.vehicle: property 'invalid' is not evaluated and the schema does not allow unevaluated properties" + "/vehicle: must be valid to one and only one schema, but 0 are valid", + "/vehicle: property 'invalid' is not evaluated and the schema does not allow unevaluated properties" ] } ] @@ -386,7 +386,7 @@ }, "valid": false, "validationMessages": [ - "$.vehicle: property 'unevaluated' is not evaluated and the schema does not allow unevaluated properties" + "/vehicle: property 'unevaluated' is not evaluated and the schema does not allow unevaluated properties" ] }, { @@ -401,7 +401,7 @@ }, "valid": false, "validationMessages": [ - "$.vehicle: property 'unevaluated' is not evaluated and the schema does not allow unevaluated properties" + "/vehicle: property 'unevaluated' is not evaluated and the schema does not allow unevaluated properties" ] }, { @@ -418,7 +418,7 @@ }, "valid": false, "validationMessages": [ - "$.vehicle: property 'unevaluated' is not evaluated and the schema does not allow unevaluated properties" + "/vehicle: property 'unevaluated' is not evaluated and the schema does not allow unevaluated properties" ] } ] @@ -515,8 +515,8 @@ }, "valid": false, "validationMessages": [ - "$.vehicle: required property 'wings' not found", - "$.vehicle: property 'unevaluated' is not evaluated and the schema does not allow unevaluated properties" + "/vehicle: required property 'wings' not found", + "/vehicle: property 'unevaluated' is not evaluated and the schema does not allow unevaluated properties" ] } ] @@ -578,8 +578,8 @@ }, "valid": false, "validationMessages": [ - "$: property 'age' is not evaluated and the schema does not allow unevaluated properties", - "$: property 'unevaluated' is not evaluated and the schema does not allow unevaluated properties" + ": property 'age' is not evaluated and the schema does not allow unevaluated properties", + ": property 'unevaluated' is not evaluated and the schema does not allow unevaluated properties" ] } ]