Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Adding custom message support in the schema #438

Merged
merged 1 commit into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/main/java/com/networknt/schema/ErrorMessageType.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,8 @@ public interface ErrorMessageType {
*/
MessageFormat getMessageFormat();

default String getCustomMessage() {
return null;
}

}
7 changes: 7 additions & 0 deletions src/main/java/com/networknt/schema/FormatKeyword.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,11 @@ public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSc
public String getValue() {
return type.getValue();
}

@Override
public void setCustomMessage(String message) {
type.setCustomMessage(message);
}


}
7 changes: 5 additions & 2 deletions src/main/java/com/networknt/schema/JsonMetaSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,8 @@ public static JsonMetaSchema getInstance() {
new NonValidationKeyword("$comment"),
new NonValidationKeyword("contentMediaType"),
new NonValidationKeyword("contentEncoding"),
new NonValidationKeyword("examples")
new NonValidationKeyword("examples"),
new NonValidationKeyword("message")
))
.build();
}
Expand Down Expand Up @@ -374,8 +375,9 @@ public String getIdKeyword() {
return idKeyword;
}


public JsonValidator newValidator(ValidationContext validationContext, String schemaPath, String keyword /* keyword */, JsonNode schemaNode,
JsonSchema parentSchema) {
JsonSchema parentSchema, String customMessage) {

try {
Keyword kw = keywords.get(keyword);
Expand All @@ -385,6 +387,7 @@ public JsonValidator newValidator(ValidationContext validationContext, String sc
}
return null;
}
kw.setCustomMessage(customMessage);
return kw.newValidator(schemaPath, schemaNode, parentSchema, validationContext);
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof JsonSchemaException) {
Expand Down
28 changes: 24 additions & 4 deletions src/main/java/com/networknt/schema/JsonSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,19 +208,21 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
Map<String, JsonValidator> validators = new HashMap<String, JsonValidator>();
if (schemaNode.isBoolean()) {
if (schemaNode.booleanValue()) {
JsonValidator validator = validationContext.newValidator(getSchemaPath(), "true", schemaNode, this);
final String customMessage = getCustomMessage(schemaNode, "true");
JsonValidator validator = validationContext.newValidator(getSchemaPath(), "true", schemaNode, this, customMessage);
validators.put(getSchemaPath() + "/true", validator);
} else {
JsonValidator validator = validationContext.newValidator(getSchemaPath(), "false", schemaNode, this);
final String customMessage = getCustomMessage(schemaNode, "false");
JsonValidator validator = validationContext.newValidator(getSchemaPath(), "false", schemaNode, this, customMessage);
validators.put(getSchemaPath() + "/false", validator);
}
} else {
Iterator<String> pnames = schemaNode.fieldNames();
while (pnames.hasNext()) {
String pname = pnames.next();
JsonNode nodeToUse = pname.equals("if") ? schemaNode : schemaNode.get(pname);

JsonValidator validator = validationContext.newValidator(getSchemaPath(), pname, nodeToUse, this);
String customMessage = getCustomMessage(schemaNode, pname);
JsonValidator validator = validationContext.newValidator(getSchemaPath(), pname, nodeToUse, this, customMessage);
if (validator != null) {
validators.put(getSchemaPath() + "/" + pname, validator);

Expand All @@ -234,6 +236,24 @@ private Map<String, JsonValidator> read(JsonNode schemaNode) {
return validators;
}

private String getCustomMessage(JsonNode schemaNode, String pname) {
final JsonSchema parentSchema = getParentSchema();
final JsonNode message = getMessageNode(schemaNode, parentSchema);
if(message != null && message.get(pname) != null) {
return message.get(pname).asText();
}
return null;
}

private JsonNode getMessageNode(JsonNode schemaNode, JsonSchema parentSchema) {
JsonNode nodeContainingMessage;
if (parentSchema == null)
nodeContainingMessage = schemaNode;
else
nodeContainingMessage = parentSchema.schemaNode;
return nodeContainingMessage.get("message");
}

/************************ START OF VALIDATE METHODS **********************************/

public Set<ValidationMessage> validate(JsonNode jsonNode, JsonNode rootNode, String at) {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/networknt/schema/Keyword.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,9 @@
public interface Keyword {
String getValue();

default void setCustomMessage(String message) {
//setCustom message
}

JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) throws JsonSchemaException, Exception;
}
4 changes: 2 additions & 2 deletions src/main/java/com/networknt/schema/ValidationContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ public ValidationContext(URIFactory uriFactory, URNFactory urnFactory, JsonMetaS
}

public JsonValidator newValidator(String schemaPath, String keyword /* keyword */, JsonNode schemaNode,
JsonSchema parentSchema) {
return metaSchema.newValidator(this, schemaPath, keyword, schemaNode, parentSchema);
JsonSchema parentSchema, String customMessage) {
return metaSchema.newValidator(this, schemaPath, keyword, schemaNode, parentSchema, customMessage);
}

public String resolveSchemaId(JsonNode schemaNode) {
Expand Down
18 changes: 16 additions & 2 deletions src/main/java/com/networknt/schema/ValidationMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package com.networknt.schema;

import org.apache.commons.lang3.StringUtils;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Map;
Expand Down Expand Up @@ -114,7 +116,8 @@ public void setType(String type) {
public static ValidationMessage of(String type, ErrorMessageType errorMessageType, String at, String... arguments) {
ValidationMessage.Builder builder = new ValidationMessage.Builder();
builder.code(errorMessageType.getErrorCode()).path(at).arguments(arguments)
.format(errorMessageType.getMessageFormat()).type(type);
.format(errorMessageType.getMessageFormat()).type(type)
.customMessage(errorMessageType.getCustomMessage());
return builder.build();
}

Expand All @@ -132,6 +135,7 @@ public static class Builder {
private String[] arguments;
private Map<String, Object> details;
private MessageFormat format;
private String customMessage;

public Builder type(String type) {
this.type = type;
Expand Down Expand Up @@ -163,6 +167,11 @@ public Builder format(MessageFormat format) {
return this;
}

public Builder customMessage(String customMessage) {
this.customMessage = customMessage;
return this;
}

public ValidationMessage build() {
ValidationMessage msg = new ValidationMessage();
msg.setType(type);
Expand All @@ -179,7 +188,12 @@ public ValidationMessage build() {
objs[i] = arguments[i - 1];
}
}
msg.setMessage(format.format(objs));
if(StringUtils.isNotBlank(customMessage)) {
msg.setMessage(customMessage);
} else {
msg.setMessage(format.format(objs));
}

}

return msg;
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/networknt/schema/ValidatorTypeCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public JsonValidator newValidator(String schemaPath, JsonNode schemaNode, JsonSc
private final String value;
private final String errorCode;
private final MessageFormat messageFormat;
private String customMessage;
private final String errorCodeKey;
private final Class validator;
private final long versionCode;
Expand All @@ -99,6 +100,7 @@ private ValidatorTypeCode(String value, String errorCode, MessageFormat messageF
this.errorCodeKey = value + "ErrorCode";
this.validator = validator;
this.versionCode = versionCode;
this.customMessage = null;
}

public static List<ValidatorTypeCode> getNonFormatKeywords(SpecVersion.VersionFlag versionFlag) {
Expand Down Expand Up @@ -148,6 +150,14 @@ public MessageFormat getMessageFormat() {
return messageFormat;
}

public void setCustomMessage(String message) {
this.customMessage = message;
}

public String getCustomMessage() {
return customMessage;
}

public String getErrorCodeKey() {
return errorCodeKey;
}
Expand Down
42 changes: 42 additions & 0 deletions src/test/java/com/networknt/schema/Issue426Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.networknt.schema;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;

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

/**
* Validating custom message
*/
public class Issue426Test {
protected JsonSchema getJsonSchemaFromStreamContentV7(InputStream schemaContent) {
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7);
return factory.getSchema(schemaContent);
}

protected JsonNode getJsonNodeFromStreamContent(InputStream content) throws Exception {
ObjectMapper mapper = new ObjectMapper();
return mapper.readTree(content);
}

@Test
public void shouldWorkV7() throws Exception {
String schemaPath = "/schema/issue426-v7.json";
String dataPath = "/data/issue426.json";
InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
JsonSchema schema = getJsonSchemaFromStreamContentV7(schemaInputStream);
InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
JsonNode node = getJsonNodeFromStreamContent(dataInputStream);
Set<ValidationMessage> errors = schema.validate(node);
Assert.assertEquals(2, errors.size());
final JsonNode message = schema.schemaNode.get("message");
for(ValidationMessage error : errors) {
//validating custom message
Assert.assertEquals(message.get(error.getType()).asText(), error.getMessage());
}
}
}

9 changes: 9 additions & 0 deletions src/test/resources/data/issue426.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"firstName": {},
"foo": [
1,
2,
3,
4
]
}
20 changes: 20 additions & 0 deletions src/test/resources/schema/issue426-v7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$id": "https://example.com/person.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
"properties": {
"firstName": {
"type": "string",
"description": "The person's first name."
},
"foo": {
"type": "array",
"maxItems": 3
}
},
"message": {
"maxItems" : "MaxItem must be 3 only",
"type" : "Invalid type"
}
}