Skip to content

Commit

Permalink
Preserve # suffix during metaschema URI normalization (networknt#519)
Browse files Browse the repository at this point in the history
* Add test reproducing issue networknt#518

* fixes networknt#518 add flag for preserving '#' during metaschema URI normalisation
  • Loading branch information
pondzix authored Feb 24, 2022
1 parent 035f63c commit 67bff81
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 11 deletions.
26 changes: 20 additions & 6 deletions src/main/java/com/networknt/schema/JsonSchemaFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static class Builder {
private final Map<String, JsonMetaSchema> jsonMetaSchemas = new HashMap<String, JsonMetaSchema>();
private final Map<String, String> uriMap = new HashMap<String, String>();
private boolean forceHttps = true;
private boolean removeEmptyFragmentSuffix = true;

public Builder() {
// Adds support for creating {@link URL}s.
Expand Down Expand Up @@ -145,6 +145,11 @@ public Builder forceHttps(boolean forceHttps) {
return this;
}

public Builder removeEmptyFragmentSuffix(boolean removeEmptyFragmentSuffix) {
this.removeEmptyFragmentSuffix = removeEmptyFragmentSuffix;
return this;
}

public JsonSchemaFactory build() {
// create builtin keywords with (custom) formats.
return new JsonSchemaFactory(
Expand All @@ -155,7 +160,8 @@ public JsonSchemaFactory build() {
urnFactory,
jsonMetaSchemas,
uriMap,
forceHttps
forceHttps,
removeEmptyFragmentSuffix
);
}
}
Expand All @@ -169,6 +175,7 @@ public JsonSchemaFactory build() {
private final Map<String, String> uriMap;
private final ConcurrentMap<URI, JsonSchema> uriSchemaCache = new ConcurrentHashMap<URI, JsonSchema>();
private final boolean forceHttps;
private final boolean removeEmptyFragmentSuffix;


private JsonSchemaFactory(
Expand All @@ -179,7 +186,8 @@ private JsonSchemaFactory(
final URNFactory urnFactory,
final Map<String, JsonMetaSchema> jsonMetaSchemas,
final Map<String, String> uriMap,
final boolean forceHttps) {
final boolean forceHttps,
final boolean removeEmptyFragmentSuffix) {
if (mapper == null) {
throw new IllegalArgumentException("ObjectMapper must not be null");
} else if (defaultMetaSchemaURI == null || defaultMetaSchemaURI.trim().isEmpty()) {
Expand All @@ -203,6 +211,7 @@ private JsonSchemaFactory(
this.jsonMetaSchemas = jsonMetaSchemas;
this.uriMap = uriMap;
this.forceHttps = forceHttps;
this.removeEmptyFragmentSuffix = removeEmptyFragmentSuffix;
}

/**
Expand Down Expand Up @@ -281,7 +290,7 @@ protected ValidationContext createValidationContext(final JsonNode schemaNode) {

private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode) {
final JsonNode uriNode = schemaNode.get("$schema");
final String uri = uriNode == null || uriNode.isNull() ? defaultMetaSchemaURI : normalizeMetaSchemaUri(uriNode.textValue(), forceHttps);
final String uri = uriNode == null || uriNode.isNull() ? defaultMetaSchemaURI : normalizeMetaSchemaUri(uriNode.textValue(), forceHttps, removeEmptyFragmentSuffix);
final JsonMetaSchema jsonMetaSchema = jsonMetaSchemas.get(uri);
if (jsonMetaSchema == null) {
throw new JsonSchemaException("Unknown MetaSchema: " + uri);
Expand Down Expand Up @@ -409,12 +418,17 @@ private boolean idMatchesSourceUri(final JsonMetaSchema metaSchema, final JsonNo
return result;
}

static protected String normalizeMetaSchemaUri(String u, boolean forceHttps) {
static protected String normalizeMetaSchemaUri(String u, boolean forceHttps, boolean removeEmptyFragmentSuffix) {
try {
URI uri = new URI(u);
String scheme = forceHttps ? "https" : uri.getScheme();
URI newUri = new URI(scheme, uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), null, null);
return newUri.toString();

if (!removeEmptyFragmentSuffix && u.endsWith("#")) {
return newUri + "#";
} else {
return newUri.toString();
}
} catch (URISyntaxException e) {
throw new JsonSchemaException("Wrong MetaSchema URI: " + u);
}
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/com/networknt/schema/SpecVersionDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ public static SpecVersion.VersionFlag detect(JsonNode jsonNode) {
if (!jsonNode.has(SCHEMA_TAG))
throw new JsonSchemaException("Schema tag not present");

String schemaUri = JsonSchemaFactory.normalizeMetaSchemaUri(jsonNode.get(SCHEMA_TAG).asText(), true);
final boolean forceHttps = true;
final boolean removeEmptyFragmentSuffix = true;

String schemaUri = JsonSchemaFactory.normalizeMetaSchemaUri(jsonNode.get(SCHEMA_TAG).asText(), forceHttps, removeEmptyFragmentSuffix);
if (schemaUri.equals(JsonMetaSchema.getV4().getUri()))
return SpecVersion.VersionFlag.V4;
else if (schemaUri.equals(JsonMetaSchema.getV6().getUri()))
Expand Down
30 changes: 30 additions & 0 deletions src/test/java/com/networknt/schema/Issue518Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.networknt.schema;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.InputStream;

public class Issue518Test {
private static final JsonMetaSchema igluMetaSchema =
JsonMetaSchema
.builder("http://iglucentral.com/schemas/com.snowplowanalytics.self-desc/schema/jsonschema/1-0-0#", JsonMetaSchema.getV7())
.build();

private static final JsonSchemaFactory FACTORY =
JsonSchemaFactory
.builder(JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V7))
.addMetaSchema(igluMetaSchema)
.forceHttps(false)
.removeEmptyFragmentSuffix(false)
.build();

@Test
public void testPreservingEmptyFragmentSuffix() {
String schemaPath = "/schema/issue518-v7.json";
InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
JsonSchema schema = FACTORY.getSchema(schemaInputStream);

Assertions.assertNotNull(schema);
}
}
46 changes: 42 additions & 4 deletions src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,53 @@ public void testSchema3() throws IOException {

@Test
public void testNormalize() throws JsonSchemaException {
final boolean forceHttps = true;
final boolean removeEmptyFragmentSuffix = true;

String uri01 = "http://json-schema.org/draft-07/schema";
String uri02 = "http://json-schema.org/draft-07/schema#";
String uri03 = "http://json-schema.org/draft-07/schema?key=value";
String uri04 = "http://json-schema.org/draft-07/schema?key=value&key2=value2";
String expected = "https://json-schema.org/draft-07/schema";
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, true));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, true));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, true));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri04, true));

Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, forceHttps, removeEmptyFragmentSuffix));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, forceHttps, removeEmptyFragmentSuffix));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, forceHttps, removeEmptyFragmentSuffix));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri04, forceHttps, removeEmptyFragmentSuffix));

}

@Test
public void testNormalizeForceHttpsDisabled() throws JsonSchemaException {
final boolean forceHttps = false;
final boolean removeEmptyFragmentSuffix = true;

String uri01 = "http://json-schema.org/draft-07/schema";
String uri02 = "http://json-schema.org/draft-07/schema#";
String uri03 = "http://json-schema.org/draft-07/schema?key=value";
String uri04 = "http://json-schema.org/draft-07/schema?key=value&key2=value2";
String expected = "http://json-schema.org/draft-07/schema";

Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, forceHttps, removeEmptyFragmentSuffix));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, forceHttps, removeEmptyFragmentSuffix));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, forceHttps, removeEmptyFragmentSuffix));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri04, forceHttps, removeEmptyFragmentSuffix));

}

@Test
public void testNormalizeRemovingEmptyFragmentSuffixDisabled() throws JsonSchemaException {
final boolean forceHttps = true;
final boolean removeEmptyFragmentSuffix = false;

String uri01 = "http://json-schema.org/draft-07/schema#";
String uri02 = "http://json-schema.org/draft-07/schema?key=value#";
String uri03 = "http://json-schema.org/draft-07/schema?key=value&key2=value2#";
String expected = "https://json-schema.org/draft-07/schema#";

Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, forceHttps, removeEmptyFragmentSuffix));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, forceHttps, removeEmptyFragmentSuffix));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, forceHttps, removeEmptyFragmentSuffix));

}
}
4 changes: 4 additions & 0 deletions src/test/resources/schema/issue518-v7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "http://iglucentral.com/schemas/com.snowplowanalytics.self-desc/schema/jsonschema/1-0-0#",
"type": "object"
}

0 comments on commit 67bff81

Please sign in to comment.