diff --git a/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java b/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java index 67fb6cc31..2384f0ad6 100644 --- a/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java +++ b/src/main/java/com/networknt/schema/AdditionalPropertiesValidator.java @@ -47,7 +47,7 @@ public AdditionalPropertiesValidator(String schemaPath, JsonNode schemaNode, Jso additionalPropertiesSchema = null; } else if (schemaNode.isObject()) { allowAdditionalProperties = true; - additionalPropertiesSchema = new JsonSchema(validationContext, getValidatorType().getValue(), schemaNode, parentSchema); + additionalPropertiesSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode, parentSchema); } else { allowAdditionalProperties = false; additionalPropertiesSchema = null; diff --git a/src/main/java/com/networknt/schema/AllOfValidator.java b/src/main/java/com/networknt/schema/AllOfValidator.java index b95d3120d..52c23824c 100644 --- a/src/main/java/com/networknt/schema/AllOfValidator.java +++ b/src/main/java/com/networknt/schema/AllOfValidator.java @@ -35,7 +35,7 @@ public AllOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ALL_OF, validationContext); int size = schemaNode.size(); for (int i = 0; i < size; i++) { - schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), schemaNode.get(i), parentSchema)); + schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode.get(i), parentSchema)); } } diff --git a/src/main/java/com/networknt/schema/AnyOfValidator.java b/src/main/java/com/networknt/schema/AnyOfValidator.java index 7cda0345f..1d1a95464 100644 --- a/src/main/java/com/networknt/schema/AnyOfValidator.java +++ b/src/main/java/com/networknt/schema/AnyOfValidator.java @@ -36,7 +36,7 @@ public AnyOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ANY_OF, validationContext); int size = schemaNode.size(); for (int i = 0; i < size; i++) { - schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), schemaNode.get(i), parentSchema)); + schemas.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode.get(i), parentSchema)); } } diff --git a/src/main/java/com/networknt/schema/DependenciesValidator.java b/src/main/java/com/networknt/schema/DependenciesValidator.java index ccb85d56c..1eb016810 100644 --- a/src/main/java/com/networknt/schema/DependenciesValidator.java +++ b/src/main/java/com/networknt/schema/DependenciesValidator.java @@ -44,7 +44,7 @@ public DependenciesValidator(String schemaPath, JsonNode schemaNode, JsonSchema depsProps.add(pvalue.get(i).asText()); } } else if (pvalue.isObject()) { - schemaDeps.put(pname, new JsonSchema(validationContext, pname, pvalue, parentSchema)); + schemaDeps.put(pname, new JsonSchema(validationContext, pname, parentSchema.getCurrentUrl(), pvalue, parentSchema)); } } diff --git a/src/main/java/com/networknt/schema/ItemsValidator.java b/src/main/java/com/networknt/schema/ItemsValidator.java index 7c3b148ac..b164c58c4 100644 --- a/src/main/java/com/networknt/schema/ItemsValidator.java +++ b/src/main/java/com/networknt/schema/ItemsValidator.java @@ -38,11 +38,11 @@ public class ItemsValidator extends BaseJsonValidator implements JsonValidator { public ItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) { super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.ITEMS, validationContext); if (schemaNode.isObject()) { - schema = new JsonSchema(validationContext, getValidatorType().getValue(), schemaNode, parentSchema); + schema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode, parentSchema); } else { tupleSchema = new ArrayList(); for (JsonNode s : schemaNode) { - tupleSchema.add(new JsonSchema(validationContext, getValidatorType().getValue(), s, parentSchema)); + tupleSchema.add(new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), s, parentSchema)); } JsonNode addItemNode = getParentSchema().getSchemaNode().get(PROPERTY_ADDITIONAL_ITEMS); @@ -50,7 +50,7 @@ public ItemsValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS if (addItemNode.isBoolean()) { additionalItems = addItemNode.asBoolean(); } else if (addItemNode.isObject()) { - additionalSchema = new JsonSchema(validationContext, addItemNode); + additionalSchema = new JsonSchema(validationContext, parentSchema.getCurrentUrl(), addItemNode); } } } diff --git a/src/main/java/com/networknt/schema/JsonSchema.java b/src/main/java/com/networknt/schema/JsonSchema.java index 63c409aaf..cafd6b7e9 100644 --- a/src/main/java/com/networknt/schema/JsonSchema.java +++ b/src/main/java/com/networknt/schema/JsonSchema.java @@ -16,14 +16,22 @@ package com.networknt.schema; -import com.fasterxml.jackson.databind.JsonNode; - import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; import java.net.URLDecoder; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.fasterxml.jackson.databind.JsonNode; +import com.networknt.schema.url.URLFactory; + /** * This is the core of json constraint implementation. It parses json constraint * file and generates JsonValidators. The class is thread safe, once it is @@ -34,27 +42,60 @@ public class JsonSchema extends BaseJsonValidator { protected final Map validators; private final ValidationContext validationContext; + /** + * This is the current url of this schema. This url could refer to the url of this schema's file + * or it could potentially be a url that has been altered by an id. An 'id' is able to completely overwrite + * the current url or add onto it. This is necessary so that '$ref's are able to be relative to a + * combination of the current schema file's url and 'id' urls visible to this schema. + * + * This can be null. If it is null, then the creation of relative urls will fail. However, an absolute + * 'id' would still be able to specify an absolute url. + */ + private final URL currentUrl; + private JsonValidator requiredValidator = null; - public JsonSchema(ValidationContext validationContext, JsonNode schemaNode) { - this(validationContext, "#", schemaNode, null); + public JsonSchema(ValidationContext validationContext, URL baseUrl, JsonNode schemaNode) { + this(validationContext, "#", baseUrl, schemaNode, null); } - public JsonSchema(ValidationContext validationContext, String schemaPath, JsonNode schemaNode, + public JsonSchema(ValidationContext validationContext, String schemaPath, URL currentUrl, JsonNode schemaNode, JsonSchema parent) { - this(validationContext, schemaPath, schemaNode, parent, false); + this(validationContext, schemaPath, currentUrl, schemaNode, parent, false); } - public JsonSchema(ValidationContext validationContext, String schemaPath, JsonNode schemaNode, + public JsonSchema(ValidationContext validationContext, URL baseUrl, JsonNode schemaNode, boolean suppressSubSchemaRetrieval) { + this(validationContext, "#", baseUrl, schemaNode, null, suppressSubSchemaRetrieval); + } + + private JsonSchema(ValidationContext validationContext, String schemaPath, URL currentUrl, JsonNode schemaNode, JsonSchema parent, boolean suppressSubSchemaRetrieval) { super(schemaPath, schemaNode, parent, null, suppressSubSchemaRetrieval); this.validationContext = validationContext; this.config = validationContext.getConfig(); + this.currentUrl = this.combineCurrentUrlWithIds(currentUrl, schemaNode); this.validators = Collections.unmodifiableMap(this.read(schemaNode)); } - - public JsonSchema(ValidationContext validationContext, JsonNode schemaNode, boolean suppressSubSchemaRetrieval) { - this(validationContext, "#", schemaNode, null, suppressSubSchemaRetrieval); + + private URL combineCurrentUrlWithIds(URL currentUrl, JsonNode schemaNode) { + final JsonNode idNode = schemaNode.get("id"); + if (idNode == null) { + return currentUrl; + } else { + try + { + return URLFactory.toURL(currentUrl, idNode.asText()); + } + catch (MalformedURLException e) + { + throw new IllegalArgumentException(String.format("Invalid 'id' in schema: %s", schemaNode), e); + } + } + } + + public URL getCurrentUrl() + { + return this.currentUrl; } /** diff --git a/src/main/java/com/networknt/schema/JsonSchemaFactory.java b/src/main/java/com/networknt/schema/JsonSchemaFactory.java index dcda5bd6d..85a196309 100644 --- a/src/main/java/com/networknt/schema/JsonSchemaFactory.java +++ b/src/main/java/com/networknt/schema/JsonSchemaFactory.java @@ -94,7 +94,12 @@ public JsonSchemaFactory build() { private final Map jsonMetaSchemas; private final Map urlMap; - private JsonSchemaFactory(ObjectMapper mapper, URLFetcher urlFetcher, String defaultMetaSchemaURI, Map jsonMetaSchemas, Map urlMap) { + private JsonSchemaFactory( + ObjectMapper mapper, + URLFetcher urlFetcher, + String defaultMetaSchemaURI, + Map jsonMetaSchemas, + Map urlMap) { if (mapper == null) { throw new IllegalArgumentException("ObjectMapper must not be null"); } @@ -152,10 +157,10 @@ public static Builder builder(JsonSchemaFactory blueprint) { .addUrlMappings(blueprint.urlMap); } - private JsonSchema newJsonSchema(JsonNode schemaNode, SchemaValidatorsConfig config) { + private JsonSchema newJsonSchema(URL schemaUrl, JsonNode schemaNode, SchemaValidatorsConfig config) { final ValidationContext validationContext = createValidationContext(schemaNode); validationContext.setConfig(config); - JsonSchema jsonSchema = new JsonSchema(validationContext, schemaNode); + JsonSchema jsonSchema = new JsonSchema(validationContext, schemaUrl, schemaNode); return jsonSchema; } @@ -173,11 +178,11 @@ private JsonMetaSchema findMetaSchemaForSchema(JsonNode schemaNode) { } return jsonMetaSchema; } - + public JsonSchema getSchema(String schema, SchemaValidatorsConfig config) { try { final JsonNode schemaNode = mapper.readTree(schema); - return newJsonSchema(schemaNode, config); + return newJsonSchema(null, schemaNode, config); } catch (IOException ioe) { logger.error("Failed to load json schema!", ioe); throw new JsonSchemaException(ioe); @@ -187,11 +192,11 @@ public JsonSchema getSchema(String schema, SchemaValidatorsConfig config) { public JsonSchema getSchema(String schema) { return getSchema(schema, null); } - + public JsonSchema getSchema(InputStream schemaStream, SchemaValidatorsConfig config) { try { final JsonNode schemaNode = mapper.readTree(schemaStream); - return newJsonSchema(schemaNode, config); + return newJsonSchema(null, schemaNode, config); } catch (IOException ioe) { logger.error("Failed to load json schema!", ioe); throw new JsonSchemaException(ioe); @@ -201,7 +206,7 @@ public JsonSchema getSchema(InputStream schemaStream, SchemaValidatorsConfig con public JsonSchema getSchema(InputStream schemaStream) { return getSchema(schemaStream, null); } - + public JsonSchema getSchema(URL schemaURL, SchemaValidatorsConfig config) { try { InputStream inputStream = null; @@ -214,11 +219,10 @@ public JsonSchema getSchema(URL schemaURL, SchemaValidatorsConfig config) { final JsonMetaSchema jsonMetaSchema = findMetaSchemaForSchema(schemaNode); if (idMatchesSourceUrl(jsonMetaSchema, schemaNode, schemaURL)) { - - return new JsonSchema(new ValidationContext(jsonMetaSchema, this), schemaNode, true /*retrieved via id, resolving will not change anything*/); + return new JsonSchema(new ValidationContext(jsonMetaSchema, this), mappedURL, schemaNode, true /*retrieved via id, resolving will not change anything*/); } - return newJsonSchema(schemaNode, config); + return newJsonSchema(mappedURL, schemaNode, config); } finally { if (inputStream != null) { inputStream.close(); @@ -231,15 +235,24 @@ public JsonSchema getSchema(URL schemaURL, SchemaValidatorsConfig config) { } public JsonSchema getSchema(URL schemaURL) { - return getSchema(schemaURL, null); + return getSchema(schemaURL, new SchemaValidatorsConfig()); } + public JsonSchema getSchema(URL schemaUrl, JsonNode jsonNode, SchemaValidatorsConfig config) { + return newJsonSchema(schemaUrl, jsonNode, config); + } + + public JsonSchema getSchema(JsonNode jsonNode, SchemaValidatorsConfig config) { - return newJsonSchema(jsonNode, config); + return newJsonSchema(null, jsonNode, config); } + public JsonSchema getSchema(URL schemaUrl, JsonNode jsonNode) { + return newJsonSchema(schemaUrl, jsonNode, null); + } + public JsonSchema getSchema(JsonNode jsonNode) { - return newJsonSchema(jsonNode, null); + return newJsonSchema(null, jsonNode, null); } private boolean idMatchesSourceUrl(JsonMetaSchema metaSchema, JsonNode schema, URL schemaUrl) { diff --git a/src/main/java/com/networknt/schema/NotValidator.java b/src/main/java/com/networknt/schema/NotValidator.java index 7917f368f..564d83301 100644 --- a/src/main/java/com/networknt/schema/NotValidator.java +++ b/src/main/java/com/networknt/schema/NotValidator.java @@ -30,7 +30,7 @@ public class NotValidator extends BaseJsonValidator implements JsonValidator { public NotValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) { super(schemaPath, schemaNode, parentSchema, ValidatorTypeCode.NOT, validationContext); - schema = new JsonSchema(validationContext, getValidatorType().getValue(), schemaNode, parentSchema); + schema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), schemaNode, parentSchema); parseErrorCode(getValidatorType().getErrorCodeKey()); } diff --git a/src/main/java/com/networknt/schema/OneOfValidator.java b/src/main/java/com/networknt/schema/OneOfValidator.java index 777cef4dd..e1905e15c 100644 --- a/src/main/java/com/networknt/schema/OneOfValidator.java +++ b/src/main/java/com/networknt/schema/OneOfValidator.java @@ -121,7 +121,7 @@ public OneOfValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentS int size = schemaNode.size(); for (int i = 0; i < size; i++) { JsonNode childNode = schemaNode.get(i); - JsonSchema childSchema = new JsonSchema(validationContext, getValidatorType().getValue(), childNode, parentSchema); + JsonSchema childSchema = new JsonSchema(validationContext, getValidatorType().getValue(), parentSchema.getCurrentUrl(), childNode, parentSchema); schemas.add(new ShortcutValidator(childNode, parentSchema, validationContext, childSchema)); } diff --git a/src/main/java/com/networknt/schema/PatternPropertiesValidator.java b/src/main/java/com/networknt/schema/PatternPropertiesValidator.java index 2efe4dc22..9feb4aa73 100644 --- a/src/main/java/com/networknt/schema/PatternPropertiesValidator.java +++ b/src/main/java/com/networknt/schema/PatternPropertiesValidator.java @@ -38,7 +38,7 @@ public PatternPropertiesValidator(String schemaPath, JsonNode schemaNode, JsonSc Iterator names = schemaNode.fieldNames(); while (names.hasNext()) { String name = names.next(); - schemas.put(Pattern.compile(name), new JsonSchema(validationContext, name, schemaNode.get(name), parentSchema)); + schemas.put(Pattern.compile(name), new JsonSchema(validationContext, name, parentSchema.getCurrentUrl(), schemaNode.get(name), parentSchema)); } } diff --git a/src/main/java/com/networknt/schema/PropertiesValidator.java b/src/main/java/com/networknt/schema/PropertiesValidator.java index 4a4f9e766..3daee20c0 100644 --- a/src/main/java/com/networknt/schema/PropertiesValidator.java +++ b/src/main/java/com/networknt/schema/PropertiesValidator.java @@ -32,7 +32,7 @@ public PropertiesValidator(String schemaPath, JsonNode schemaNode, JsonSchema pa schemas = new HashMap(); for (Iterator it = schemaNode.fieldNames(); it.hasNext(); ) { String pname = it.next(); - schemas.put(pname, new JsonSchema(validationContext, schemaPath + "/" + pname, schemaNode.get(pname), parentSchema)); + schemas.put(pname, new JsonSchema(validationContext, schemaPath + "/" + pname, parentSchema.getCurrentUrl(), schemaNode.get(pname), parentSchema)); } } diff --git a/src/main/java/com/networknt/schema/RefValidator.java b/src/main/java/com/networknt/schema/RefValidator.java index b1de2ca54..4d1c7af4d 100644 --- a/src/main/java/com/networknt/schema/RefValidator.java +++ b/src/main/java/com/networknt/schema/RefValidator.java @@ -16,7 +16,6 @@ package com.networknt.schema; -import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; import java.text.MessageFormat; @@ -34,9 +33,7 @@ public class RefValidator extends BaseJsonValidator implements JsonValidator { protected JsonSchema schema; - private static final String REF_DOMAIN = "/"; private static final String REF_CURRENT = "#"; - private static final String REF_RELATIVE = "../"; public RefValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSchema, ValidationContext validationContext) { @@ -50,23 +47,25 @@ public RefValidator(String schemaPath, JsonNode schemaNode, JsonSchema parentSch static JsonSchema getRefSchema(JsonSchema parentSchema, ValidationContext validationContext, String refValue) { if (!refValue.startsWith(REF_CURRENT)) { - // handle remote ref - String schemaUrl = refValue; - int index = refValue.indexOf(REF_CURRENT); + // This will be the url extracted from the refValue (this may be a relative or absolute Url). + final String refUrl; + final int index = refValue.indexOf(REF_CURRENT); if (index > 0) { - schemaUrl = schemaUrl.substring(0, index); - } - if(isRelativePath(schemaUrl)){ - schemaUrl = obtainAbsolutePath(parentSchema, schemaUrl); + refUrl = refValue.substring(0, index); + } else { + refUrl = refValue; } - try { - URL url = URLFactory.toURL(schemaUrl); - parentSchema = validationContext.getJsonSchemaFactory().getSchema(url, validationContext.getConfig()); - } catch (MalformedURLException e) { - InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(schemaUrl); - parentSchema = validationContext.getJsonSchemaFactory().getSchema(is); + // This will determine the correct absolute url for the refUrl. This decision will take into + // account the current url of the parent schema. + URL schemaUrl = determineSchemaUrl(parentSchema, refUrl); + if (schemaUrl == null) { + return null; } + + // This should retrieve schemas regardless of the protocol that is in the url. + parentSchema = validationContext.getJsonSchemaFactory().getSchema(schemaUrl, validationContext.getConfig()); + if (index < 0) { return parentSchema.findAncestor(); } else { @@ -78,41 +77,28 @@ static JsonSchema getRefSchema(JsonSchema parentSchema, ValidationContext valida } else { JsonNode node = parentSchema.getRefSchemaNode(refValue); if (node != null) { - return new JsonSchema(validationContext, refValue, node, parentSchema); + return new JsonSchema(validationContext, refValue, parentSchema.getCurrentUrl(), node, parentSchema); } } return null; } - - private static boolean isRelativePath(String schemaUrl) { - return !schemaUrl.startsWith("http"); - } - - private static String obtainAbsolutePath(JsonSchema parentSchema, String schemaUrl) { - String baseSchemaUrl = parentSchema.findAncestor().getSchemaNode().get("id").textValue(); - int index = baseSchemaUrl.lastIndexOf("/"); - baseSchemaUrl = baseSchemaUrl.substring(0, index); - - String schemaRef = schemaUrl; - - if(schemaRef.startsWith(REF_DOMAIN)){ - // from domain add ref - try { - URL url = URLFactory.toURL(baseSchemaUrl); - baseSchemaUrl = url.getProtocol()+"//"+url.getHost(); - } catch (MalformedURLException e) { - e.printStackTrace(); - } - }else if(schemaRef.startsWith(REF_RELATIVE)){ - // relative from schema - while(schemaRef.startsWith(REF_RELATIVE)){ - index = baseSchemaUrl.lastIndexOf("/"); - baseSchemaUrl = baseSchemaUrl.substring(0, index); - schemaRef = schemaRef.replaceFirst(REF_RELATIVE, ""); - } - } - schemaRef = baseSchemaUrl +"/"+ schemaRef; - return schemaRef; + + private static URL determineSchemaUrl(JsonSchema parentSchema, String refUrl) { + URL schemaUrl; + try { + // If the refUrl is an absolute url, then this will succeed. + schemaUrl = URLFactory.toURL(refUrl); + } catch (MalformedURLException e) { + try { + // If the refUrl is a valid relative url in the context of the parent schema's url, + // then this will succeed. + schemaUrl = URLFactory.toURL(parentSchema.getCurrentUrl(), refUrl); + } catch (MalformedURLException e2) { + // We are unable to resolve the reference at this point. + schemaUrl = null; + } + } + return schemaUrl; } public Set validate(JsonNode node, JsonNode rootNode, String at) { diff --git a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java index 3aad2a14c..cec3135e7 100644 --- a/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java +++ b/src/main/java/com/networknt/schema/SchemaValidatorsConfig.java @@ -16,6 +16,7 @@ package com.networknt.schema; +import java.net.URL; import java.util.HashMap; import java.util.Map; @@ -60,7 +61,7 @@ public Map getUrlMappings() { public void setUrlMappings(Map urlMappings) { this.urlMappings = urlMappings; } - + public boolean isMissingNodeAsError() { return missingNodeAsError; } diff --git a/src/main/java/com/networknt/schema/UnionTypeValidator.java b/src/main/java/com/networknt/schema/UnionTypeValidator.java index 159f3648f..5d6d97729 100644 --- a/src/main/java/com/networknt/schema/UnionTypeValidator.java +++ b/src/main/java/com/networknt/schema/UnionTypeValidator.java @@ -50,7 +50,7 @@ public UnionTypeValidator(String schemaPath, JsonNode schemaNode, JsonSchema par sep = ", "; if (n.isObject()) - schemas.add(new JsonSchema(validationContext, ValidatorTypeCode.TYPE.getValue(), n, parentSchema)); + schemas.add(new JsonSchema(validationContext, ValidatorTypeCode.TYPE.getValue(), parentSchema.getCurrentUrl(), n, parentSchema)); else schemas.add(new TypeValidator(schemaPath + "/" + i, n, parentSchema, validationContext)); diff --git a/src/main/java/com/networknt/schema/url/URLFactory.java b/src/main/java/com/networknt/schema/url/URLFactory.java index d78deecc0..430980c1a 100644 --- a/src/main/java/com/networknt/schema/url/URLFactory.java +++ b/src/main/java/com/networknt/schema/url/URLFactory.java @@ -32,13 +32,24 @@ public class URLFactory { private static final ClasspathURLStreamHandler sClasspathURLStreamHandler = new ClasspathURLStreamHandler(); + /** + * Creates an {@link URL} based on the provided string and parent url. + * @param parentUrl the parent URL of the given url (if no parent exists, then this can be null). + * @param url the url + * @return a {@link URL} + * @throws MalformedURLException if the url is not a proper URL + */ + public static URL toURL(final URL parentUrl, final String url) throws MalformedURLException { + return new URL(parentUrl, url, sClasspathURLStreamHandler.supports(url) ? sClasspathURLStreamHandler : null); + } + /** * Creates an {@link URL} based on the provided string - * @param pURL the url + * @param url the url * @return a {@link URL} * @throws MalformedURLException if the url is not a proper URL */ - public static URL toURL(final String pURL) throws MalformedURLException { - return new URL(null, pURL, sClasspathURLStreamHandler.supports(pURL) ? sClasspathURLStreamHandler : null); + public static URL toURL(final String url) throws MalformedURLException { + return new URL(null, url, sClasspathURLStreamHandler.supports(url) ? sClasspathURLStreamHandler : null); } } \ No newline at end of file diff --git a/src/test/java/com/networknt/schema/JsonSchemaTest.java b/src/test/java/com/networknt/schema/JsonSchemaTest.java index 711d524da..e743e873a 100644 --- a/src/test/java/com/networknt/schema/JsonSchemaTest.java +++ b/src/test/java/com/networknt/schema/JsonSchemaTest.java @@ -16,15 +16,7 @@ package com.networknt.schema; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import io.undertow.Undertow; -import io.undertow.server.handlers.resource.FileResourceManager; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; +import static io.undertow.Handlers.resource; import java.io.File; import java.io.InputStream; @@ -32,7 +24,18 @@ import java.util.ArrayList; import java.util.List; -import static io.undertow.Handlers.resource; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.networknt.schema.url.URLFactory; + +import io.undertow.Undertow; +import io.undertow.server.handlers.resource.FileResourceManager; public class JsonSchemaTest { protected ObjectMapper mapper = new ObjectMapper(); @@ -67,6 +70,7 @@ public static void tearDown() throws Exception { } private void runTestFile(String testCaseFile) throws Exception { + final URL testCaseFileUrl = URLFactory.toURL("classpath:" + testCaseFile); InputStream in = Thread.currentThread().getContextClassLoader() .getResourceAsStream(testCaseFile); ArrayNode testCases = (ArrayNode) mapper.readTree(in); @@ -83,8 +87,8 @@ 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) ? false : typeLooseNode.asBoolean()); - JsonSchema schema = validatorFactory.getSchema(testCase.get("schema"), config); + config.setTypeLoose((typeLooseNode == null) ? true : typeLooseNode.asBoolean()); + JsonSchema schema = validatorFactory.getSchema(testCaseFileUrl, testCase.get("schema"), config); List errors = new ArrayList(); errors.addAll(schema.validate(node)); @@ -106,7 +110,7 @@ private void runTestFile(String testCaseFile) throws Exception { } } } catch (JsonSchemaException e) { - System.out.println("Bypass validation due to invalid schema: " + e.getMessage()); + throw new IllegalStateException(String.format("Current schema should not be invalid: %s", testCaseFile), e); } } } @@ -265,6 +269,11 @@ public void testRefRemoteValidator() throws Exception { runTestFile("tests/refRemote.json"); } + @Test + public void testRelativeRefRemoteValidator() throws Exception { + runTestFile("tests/relativeRefRemote.json"); + } + @Test public void testRequiredValidator() throws Exception { runTestFile("tests/required.json"); diff --git a/src/test/java/com/networknt/schema/SelfRefTest.java b/src/test/java/com/networknt/schema/SelfRefTest.java index 70f6e27de..b1165558f 100644 --- a/src/test/java/com/networknt/schema/SelfRefTest.java +++ b/src/test/java/com/networknt/schema/SelfRefTest.java @@ -16,16 +16,17 @@ package com.networknt.schema; -import com.fasterxml.jackson.databind.JsonNode; +import org.junit.Ignore; import org.junit.Test; /** * Created by stevehu on 2016-12-20. */ public class SelfRefTest extends BaseJsonSchemaValidatorTest { - @Test + @Ignore("This test currently is failing because of a StackOverflow caused by a recursive $ref.") + @Test() public void testSelfRef() throws Exception { - JsonNode node = getJsonNodeFromClasspath("selfref.json"); + JsonSchema node = getJsonSchemaFromClasspath("selfRef.json"); System.out.println("node = " + node); } -} +} \ No newline at end of file diff --git a/src/test/resources/selfref.json b/src/test/resources/selfRef.json similarity index 99% rename from src/test/resources/selfref.json rename to src/test/resources/selfRef.json index edd98edd5..385c25d84 100644 --- a/src/test/resources/selfref.json +++ b/src/test/resources/selfRef.json @@ -28,4 +28,4 @@ ] } } -} +} \ No newline at end of file diff --git a/src/test/resources/tests/folder/folderInteger.json b/src/test/resources/tests/folder/folderInteger.json new file mode 100644 index 000000000..85d2db663 --- /dev/null +++ b/src/test/resources/tests/folder/folderInteger.json @@ -0,0 +1,3 @@ +{ + "type": "integer" +} \ No newline at end of file diff --git a/src/test/resources/tests/refRemoteSchema.json b/src/test/resources/tests/refRemoteSchema.json new file mode 100644 index 000000000..7dc6b7593 --- /dev/null +++ b/src/test/resources/tests/refRemoteSchema.json @@ -0,0 +1,3 @@ +{ + "$ref" : "subSchemas.json#/integer" +} \ No newline at end of file diff --git a/src/test/resources/tests/relativeRefRemote.json b/src/test/resources/tests/relativeRefRemote.json new file mode 100644 index 000000000..81277d895 --- /dev/null +++ b/src/test/resources/tests/relativeRefRemote.json @@ -0,0 +1,105 @@ +[ + { + "description": "remote ref", + "schema": {"$ref": "integer.json"}, + "tests": [ + { + "description": "remote ref valid", + "data": 1, + "valid": true + }, + { + "description": "remote ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "fragment within remote ref", + "schema": {"$ref": "subSchemas.json#/integer"}, + "tests": [ + { + "description": "remote fragment valid", + "data": 1, + "valid": true + }, + { + "description": "remote fragment invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "ref within remote ref", + "schema": { + "$ref": "refRemoteSchema.json" + }, + "tests": [ + { + "description": "ref within ref valid", + "data": 1, + "valid": true + }, + { + "description": "ref within ref invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "change resolution scope", + "schema": { + "items": { + "id": "folder/", + "items": {"$ref": "folderInteger.json"} + } + }, + "tests": [ + { + "description": "changed scope ref valid", + "data": [[1]], + "valid": true + }, + { + "description": "changed scope ref invalid", + "data": [["a"]], + "valid": false + } + ] + }, + { + "description": "sub directory reference with ref", + "schema": {"$ref": "self_ref/../subSchemas.json#/integer"}, + "tests": [ + { + "description": "remote fragment valid", + "data": 1, + "valid": true + }, + { + "description": "remote fragment invalid", + "data": "a", + "valid": false + } + ] + }, + { + "description": "domain reference with ref", + "schema": {"$ref": "/tests/integer.json"}, + "tests": [ + { + "description": "remote ref valid", + "data": 1, + "valid": true + }, + { + "description": "remote ref invalid", + "data": "a", + "valid": false + } + ] + } +]