Skip to content

Commit

Permalink
Sort ValidationMessage by its type (#492)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsu216 committed Jan 5, 2022
1 parent 30d4b4b commit 8ada62c
Show file tree
Hide file tree
Showing 15 changed files with 391 additions and 2 deletions.
43 changes: 41 additions & 2 deletions src/main/java/com/networknt/schema/OneOfValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,10 @@ public Set<ValidationMessage> validate(JsonNode node, JsonNode rootNode, String
else if (numberOfValidSchema < 1) {
if (!childErrors.isEmpty()) {
if (childErrors.size() > 1) {
Set<ValidationMessage> notAdditionalPropertiesOnly = childErrors.stream()
Set<ValidationMessage> notAdditionalPropertiesOnly = new LinkedHashSet<>(childErrors.stream()
.filter((ValidationMessage validationMessage) -> !ValidatorTypeCode.ADDITIONAL_PROPERTIES.getValue().equals(validationMessage.getType()))
.collect(Collectors.toSet());
.sorted((vm1, vm2) -> compareValidationMessages(vm1, vm2))
.collect(Collectors.toList()));
if (notAdditionalPropertiesOnly.size() > 0) {
childErrors = notAdditionalPropertiesOnly;
}
Expand All @@ -210,6 +211,44 @@ else if (numberOfValidSchema < 1) {
return Collections.unmodifiableSet(errors);
}

/**
* Sort <code>ValidationMessage</code> by its type
* @return
*/
private static int compareValidationMessages(ValidationMessage vm1, ValidationMessage vm2) {
// ValidationMessage's type has smaller index in the list below has high priority
final List<String> typeCodes = Arrays.asList(
ValidatorTypeCode.TYPE.getValue(),
ValidatorTypeCode.DATETIME.getValue(),
ValidatorTypeCode.UUID.getValue(),
ValidatorTypeCode.ID.getValue(),
ValidatorTypeCode.EXCLUSIVE_MAXIMUM.getValue(),
ValidatorTypeCode.EXCLUSIVE_MINIMUM.getValue(),
ValidatorTypeCode.TRUE.getValue(),
ValidatorTypeCode.FALSE.getValue(),
ValidatorTypeCode.CONST.getValue(),
ValidatorTypeCode.CONTAINS.getValue(),
ValidatorTypeCode.PROPERTYNAMES.getValue()
);

final int index1 = typeCodes.indexOf(vm1.getType());
final int index2 = typeCodes.indexOf(vm2.getType());

if (index1 >= 0) {
if (index2 >= 0) {
return Integer.compare(index1, index2);
} else {
return -1;
}
} else {
if (index2 >= 0) {
return 1;
} else {
return vm1.getCode().compareTo(vm2.getCode());
}
}
}

private void resetValidatorState() {
ValidatorState state = (ValidatorState) CollectorContext.getInstance().get(ValidatorState.VALIDATOR_STATE_KEY);
state.setComplexValidator(false);
Expand Down
153 changes: 153 additions & 0 deletions src/test/java/com/networknt/schema/Issue491Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package com.networknt.schema;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.InputStream;
import java.util.Set;
import java.util.stream.Stream;

class Issue491Test {

private static JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
private static String schemaPath1 = "/schema/issue491-v7.json";
private static String schemaPath2 = "/schema/issue491_2-v7.json";
private static String schemaPath3 = "/schema/issue491_3-v7.json";

private JsonNode getJsonNodeFromJsonData(String jsonFilePath) throws Exception {
InputStream content = getClass().getResourceAsStream(jsonFilePath);
ObjectMapper mapper = new ObjectMapper();
return mapper.readTree(content);
}

@Test
@DisplayName("Test valid oneOf option 1")
void testValidJson1() throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-1.json");
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertTrue(errors.isEmpty());
}

@Test
@DisplayName("Test valid oneOf option 2")
void testValidJson2() throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-2.json");
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertTrue(errors.isEmpty());
}

@Test
@DisplayName("Test valid oneOf option 1")
void testValidJson3() throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath2);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-3.json");
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertTrue(errors.isEmpty());
}

@Test
@DisplayName("Test valid oneOf option 2")
void testValidJson4() throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath2);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-2.json");
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertTrue(errors.isEmpty());
}

@Test
@DisplayName("Test valid oneOf option 1")
void testValidJson5() throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath3);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-4.json");
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertTrue(errors.isEmpty());
}

@Test
@DisplayName("Test valid oneOf option 2")
void testValidJson6() throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath3);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData("/data/issue491-valid-2.json");
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertTrue(errors.isEmpty());
}

@Test
@DisplayName("Test invalid oneOf option 1 - wrong type")
void testInvalidJson1() throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData("/data/issue491-invalid-1.json");
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertEquals(2, errors.size());
Assertions.assertEquals("$.search.searchAge.age: string found, integer expected", errors.iterator().next().getMessage());
}

@Test
@DisplayName("Test invalid oneOf option 2 - wrong type")
void testInvalidJson2() throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath1);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData("/data/issue491-invalid-2.json");
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertEquals(2, errors.size());
Assertions.assertEquals("$.search.name: integer found, string expected", errors.iterator().next().getMessage());
}

@Test
@DisplayName("Test invalid oneOf option 1 - wrong type")
void testInvalidJson3() throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath2);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData("/data/issue491-invalid-3.json");
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertEquals(2, errors.size());
Assertions.assertEquals("$.search.byAge.age: string found, integer expected", errors.iterator().next().getMessage());
}

@Test
@DisplayName("Test invalid oneOf option 2 - wrong type")
void testInvalidJson4() throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath2);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData("/data/issue491-invalid-2.json");
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertEquals(2, errors.size());
Assertions.assertEquals("$.search.name: integer found, string expected", errors.iterator().next().getMessage());
}

@ParameterizedTest
@MethodSource("parametersProvider")
@DisplayName("Test invalid oneOf option - wrong types or values")
void testInvalidJson5(String jsonPath, String expectedError) throws Exception {
InputStream schemaInputStream = Issue491Test.class.getResourceAsStream(schemaPath3);
JsonSchema schema = factory.getSchema(schemaInputStream);
JsonNode node = getJsonNodeFromJsonData(jsonPath);
Set<ValidationMessage> errors = schema.validate(node);
Assertions.assertEquals(2, errors.size());
Assertions.assertEquals(expectedError, errors.iterator().next().getMessage());
}

private static Stream<Arguments> parametersProvider() {
return Stream.of(
Arguments.of("/data/issue491-invalid-4.json", "$.search.age: string found, integer expected"),
Arguments.of("/data/issue491-invalid-2.json", "$.search.name: integer found, string expected"),
Arguments.of("/data/issue491-invalid-5.json", "$.search.age: must have a maximum value of 150"),
Arguments.of("/data/issue491-invalid-6.json", "$.search.name: may only be 20 characters long")
);
}
}
7 changes: 7 additions & 0 deletions src/test/resources/data/issue491-invalid-1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"search": {
"searchAge": {
"age": "Steve"
}
}
}
5 changes: 5 additions & 0 deletions src/test/resources/data/issue491-invalid-2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"search": {
"name": 123
}
}
7 changes: 7 additions & 0 deletions src/test/resources/data/issue491-invalid-3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"search": {
"byAge": {
"age": "Steve"
}
}
}
5 changes: 5 additions & 0 deletions src/test/resources/data/issue491-invalid-4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"search": {
"age": "Steve"
}
}
5 changes: 5 additions & 0 deletions src/test/resources/data/issue491-invalid-5.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"search": {
"age": 200
}
}
5 changes: 5 additions & 0 deletions src/test/resources/data/issue491-invalid-6.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"search": {
"name": "TooLoooooooooooooooooooooooooooooooooongName"
}
}
7 changes: 7 additions & 0 deletions src/test/resources/data/issue491-valid-1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"search": {
"searchAge": {
"age": 50
}
}
}
5 changes: 5 additions & 0 deletions src/test/resources/data/issue491-valid-2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"search": {
"name": "Steve"
}
}
7 changes: 7 additions & 0 deletions src/test/resources/data/issue491-valid-3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"search": {
"byAge": {
"age": 50
}
}
}
5 changes: 5 additions & 0 deletions src/test/resources/data/issue491-valid-4.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"search": {
"age": 50
}
}
49 changes: 49 additions & 0 deletions src/test/resources/schema/issue491-v7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/issue-470.json",
"title": "OneOf validation message",
"description": "Test description",
"type": "object",
"properties": {
"search": {
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"searchAge": {
"type": "object",
"properties": {
"age": {
"type": "integer",
"maximum": 150,
"minimum": 1
}
},
"required": [
"age"
]
}
},
"required": [
"searchAge"
]
},
{
"type": "object",
"properties": {
"name": {
"type": "string",
"maxLength": 20,
"minLength": 1
}
},
"required": [
"name"
]
}
]
}
},
"additionalProperties": false
}
49 changes: 49 additions & 0 deletions src/test/resources/schema/issue491_2-v7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://example.com/issue-470.json",
"title": "OneOf validation message",
"description": "Test description",
"type": "object",
"properties": {
"search": {
"type": "object",
"oneOf": [
{
"type": "object",
"properties": {
"byAge": {
"type": "object",
"properties": {
"age": {
"type": "integer",
"maximum": 150,
"minimum": 1
}
},
"required": [
"age"
]
}
},
"required": [
"byAge"
]
},
{
"type": "object",
"properties": {
"name": {
"type": "string",
"maxLength": 20,
"minLength": 1
}
},
"required": [
"name"
]
}
]
}
},
"additionalProperties": false
}
Loading

0 comments on commit 8ada62c

Please sign in to comment.