Repository: networknt/json-schema-validator
Pull request #661 (2023-02-15 06:49:40)

[#660] Make DependentRequired error message more helpful
As described in issue #660, the error message for dependentRequired validations dumps out all dependentRequired rules, which is not helpful if there are many. It's not clear which particular rule has been violated. This PR changes the error message from something like
```
has a missing property which is dependent required {optional=[requiredWhenOptionalPresent], otherOptional=[otherDependentRequired]}
```
to a more useful
```
has a missing property "requiredWhenOptionalPresent" which is dependent required because "optional" is present"
```


Patch:
diff --git a/src/main/java/com/networknt/schema/DependentRequired.java b/src/main/java/com/networknt/schema/DependentRequired.java
index d08870c..65ac22c 100644
--- a/src/main/java/com/networknt/schema/DependentRequired.java
+++ b/src/main/java/com/networknt/schema/DependentRequired.java
@@ -56,7 +56,7 @@ public class DependentRequired extends BaseJsonValidator implements JsonValidato
             if (dependencies != null && !dependencies.isEmpty()) {
                 for (String field : dependencies) {
                     if (node.get(field) == null) {
-                        errors.add(buildValidationMessage(at, propertyDependencies.toString()));
+                        errors.add(buildValidationMessage(at, field, pname));
                     }
                 }
             }
diff --git a/src/main/resources/jsv-messages.properties b/src/main/resources/jsv-messages.properties
index 01d4f1e..b7aa915 100644
--- a/src/main/resources/jsv-messages.properties
+++ b/src/main/resources/jsv-messages.properties
@@ -7,7 +7,7 @@ contains = {0}: does not contain an element that passes these validations: {1}
 crossEdits = {0}: has an error with 'cross edits'
 dateTime = {0}: {1} is an invalid {2}
 dependencies = {0}: has an error with dependencies {1}
-dependentRequired = {0}: has a missing property which is dependent required {1}
+dependentRequired = {0}: has a missing property "{1}" which is dependent required because "{2}" is present
 dependentSchemas = {0}: has an error with dependentSchemas {1}
 edits = {0}: has an error with 'edits'
 enum = {0}: does not have a value in the enumeration {1}


Test patch:
diff --git a/src/test/java/com/networknt/schema/DependentRequiredTest.java b/src/test/java/com/networknt/schema/DependentRequiredTest.java
new file mode 100644
index 0000000..4ea1501
--- /dev/null
+++ b/src/test/java/com/networknt/schema/DependentRequiredTest.java
@@ -0,0 +1,66 @@
+package com.networknt.schema;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.empty;
+
+class DependentRequiredTest {
+
+    public static final String SCHEMA =
+        "{ " +
+            "   \"$schema\":\"http://json-schema.org/draft/2019-09/schema\"," +
+            "   \"type\": \"object\"," +
+            "   \"properties\": {" +
+            "       \"optional\": \"string\"," +
+            "       \"requiredWhenOptionalPresent\": \"string\"" +
+            "   }," +
+            "   \"dependentRequired\": {" +
+            "       \"optional\": [ \"requiredWhenOptionalPresent\" ]," +
+            "       \"otherOptional\": [ \"otherDependentRequired\" ]" +
+            "   }" +
+            "}";
+
+    private static final JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909);
+    private static final JsonSchema schema = factory.getSchema(SCHEMA);
+    private static final ObjectMapper mapper = new ObjectMapper();
+
+    @Test
+    void shouldReturnNoErrorMessagesForObjectWithoutOptionalField() throws IOException {
+
+        Set<ValidationMessage> messages = whenValidate("{}");
+
+        assertThat(messages, empty());
+    }
+
+    @Test
+    void shouldReturnErrorMessageForObjectWithoutDependentRequiredField() throws IOException {
+
+        Set<ValidationMessage> messages = whenValidate("{ \"optional\": \"present\" }");
+
+        assertThat(
+            messages.stream().map(ValidationMessage::getMessage).collect(Collectors.toList()),
+            contains("$: has a missing property \"requiredWhenOptionalPresent\" which is dependent required because \"optional\" is present"));
+    }
+
+    @Test
+    void shouldReturnNoErrorMessagesForObjectWithOptionalAndDependentRequiredFieldSet() throws JsonProcessingException {
+
+        Set<ValidationMessage> messages =
+            whenValidate("{ \"optional\": \"present\", \"requiredWhenOptionalPresent\": \"present\" }");
+
+        assertThat(messages, empty());
+    }
+
+    private static Set<ValidationMessage> whenValidate(String content) throws JsonProcessingException {
+        return schema.validate(mapper.readTree(content));
+    }
+
+}
\ No newline at end of file