Skip to content

Normalize uri keys of JsonSchemaFactory.jsonMetaSchemas on read and write #834

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

Merged
merged 1 commit into from
Jun 28, 2023
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
41 changes: 17 additions & 24 deletions src/main/java/com/networknt/schema/JsonSchemaFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ public static class Builder {
private URNFactory urnFactory;
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;
private boolean enableUriSchemaCache = true;
private final CompositeURITranslator uriTranslators = new CompositeURITranslator();

Expand Down Expand Up @@ -129,13 +127,13 @@ public Builder uriFetcher(final URIFetcher uriFetcher, final Iterable<String> sc
}

public Builder addMetaSchema(final JsonMetaSchema jsonMetaSchema) {
this.jsonMetaSchemas.put(jsonMetaSchema.getUri(), jsonMetaSchema);
this.jsonMetaSchemas.put(normalizeMetaSchemaUri(jsonMetaSchema.getUri()) , jsonMetaSchema);
return this;
}

public Builder addMetaSchemas(final Collection<? extends JsonMetaSchema> jsonMetaSchemas) {
for (JsonMetaSchema jsonMetaSchema : jsonMetaSchemas) {
this.jsonMetaSchemas.put(jsonMetaSchema.getUri(), jsonMetaSchema);
addMetaSchema(jsonMetaSchema);
}
return this;
}
Expand Down Expand Up @@ -163,13 +161,21 @@ public Builder addUrnFactory(URNFactory urnFactory) {
return this;
}

/**
* @deprecated No longer necessary.
* @param forceHttps ignored.
* @return this builder.
*/
public Builder forceHttps(boolean forceHttps) {
this.forceHttps = forceHttps;
return this;
}

/**
* @deprecated No longer necessary.
* @param removeEmptyFragmentSuffix ignored.
* @return this builder.
*/
public Builder removeEmptyFragmentSuffix(boolean removeEmptyFragmentSuffix) {
this.removeEmptyFragmentSuffix = removeEmptyFragmentSuffix;
return this;
}

Expand All @@ -189,8 +195,6 @@ public JsonSchemaFactory build() {
urnFactory,
jsonMetaSchemas,
uriMap,
forceHttps,
removeEmptyFragmentSuffix,
enableUriSchemaCache,
uriTranslators
);
Expand All @@ -207,8 +211,6 @@ public JsonSchemaFactory build() {
private final Map<String, JsonMetaSchema> jsonMetaSchemas;
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 final boolean enableUriSchemaCache;


Expand All @@ -221,8 +223,6 @@ private JsonSchemaFactory(
final URNFactory urnFactory,
final Map<String, JsonMetaSchema> jsonMetaSchemas,
final Map<String, String> uriMap,
final boolean forceHttps,
final boolean removeEmptyFragmentSuffix,
final boolean enableUriSchemaCache,
final CompositeURITranslator uriTranslators) {
if (jsonMapper == null) {
Expand All @@ -237,7 +237,7 @@ private JsonSchemaFactory(
throw new IllegalArgumentException("URIFetcher must not be null");
} else if (jsonMetaSchemas == null || jsonMetaSchemas.isEmpty()) {
throw new IllegalArgumentException("Json Meta Schemas must not be null or empty");
} else if (jsonMetaSchemas.get(defaultMetaSchemaURI) == null) {
} else if (jsonMetaSchemas.get(normalizeMetaSchemaUri(defaultMetaSchemaURI)) == null) {
throw new IllegalArgumentException("Meta Schema for default Meta Schema URI must be provided");
} else if (uriMap == null) {
throw new IllegalArgumentException("URL Mappings must not be null");
Expand All @@ -252,8 +252,6 @@ private JsonSchemaFactory(
this.urnFactory = urnFactory;
this.jsonMetaSchemas = jsonMetaSchemas;
this.uriMap = uriMap;
this.forceHttps = forceHttps;
this.removeEmptyFragmentSuffix = removeEmptyFragmentSuffix;
this.enableUriSchemaCache = enableUriSchemaCache;
this.uriTranslators = uriTranslators;
}
Expand Down Expand Up @@ -348,7 +346,7 @@ private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode) {
if (uriNode != null && !uriNode.isNull() && !uriNode.isTextual()) {
throw new JsonSchemaException("Unknown MetaSchema: " + uriNode.toString());
}
final String uri = uriNode == null || uriNode.isNull() ? defaultMetaSchemaURI : normalizeMetaSchemaUri(uriNode.textValue(), forceHttps, removeEmptyFragmentSuffix);
final String uri = uriNode == null || uriNode.isNull() ? defaultMetaSchemaURI : normalizeMetaSchemaUri(uriNode.textValue());
final JsonMetaSchema jsonMetaSchema = jsonMetaSchemas.computeIfAbsent(uri, this::fromId);
return jsonMetaSchema;
}
Expand Down Expand Up @@ -504,17 +502,12 @@ private boolean isYaml(final URI schemaUri) {
return (".yml".equals(extension) || ".yaml".equals(extension));
}

static protected String normalizeMetaSchemaUri(String u, boolean forceHttps, boolean removeEmptyFragmentSuffix) {
static protected String normalizeMetaSchemaUri(String u) {
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);
URI newUri = new URI("https", uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), null, null);

if (!removeEmptyFragmentSuffix && u.endsWith("#")) {
return newUri + "#";
} else {
return newUri.toString();
}
return newUri.toString();
} catch (URISyntaxException e) {
throw new JsonSchemaException("Wrong MetaSchema URI: " + u);
}
Expand Down
6 changes: 1 addition & 5 deletions src/main/java/com/networknt/schema/SpecVersionDetector.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,8 @@ public static VersionFlag detect(JsonNode jsonNode) {
public static Optional<VersionFlag> detectOptionalVersion(JsonNode jsonNode) {
return Optional.ofNullable(jsonNode.get(SCHEMA_TAG)).map(schemaTag -> {

final boolean forceHttps = true;
final boolean removeEmptyFragmentSuffix = true;

String schemaTagValue = schemaTag.asText();
String schemaUri = JsonSchemaFactory.normalizeMetaSchemaUri(schemaTagValue, forceHttps,
removeEmptyFragmentSuffix);
String schemaUri = JsonSchemaFactory.normalizeMetaSchemaUri(schemaTagValue);

return VersionFlag.fromId(schemaUri)
.orElseThrow(() -> new JsonSchemaException("'" + schemaTagValue + "' is unrecognizable schema"));
Expand Down
59 changes: 59 additions & 0 deletions src/test/java/com/networknt/schema/Issue832Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.networknt.schema;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.format.AbstractFormat;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

public class Issue832Test {
private class NoMatchFormat extends AbstractFormat {
public NoMatchFormat() {
super("no_match", "always fail match");
}

@Override
public boolean matches(String value) {
return false;
}
}

private JsonSchemaFactory buildV7PlusNoFormatSchemaFactory() {
List<Format> formats;
formats = new ArrayList<>();
formats.add(new NoMatchFormat());

JsonMetaSchema jsonMetaSchema = JsonMetaSchema.builder(
JsonMetaSchema.getV7().getUri(),
JsonMetaSchema.getV7())
.addFormats(formats)
.build();
return new JsonSchemaFactory.Builder().defaultMetaSchemaURI(jsonMetaSchema.getUri()).addMetaSchema(jsonMetaSchema).build();
}

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

@Test
public void testV7WithNonMatchingCustomFormat() throws IOException {
String schemaPath = "/schema/issue832-v7.json";
String dataPath = "/data/issue832.json";
InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
JsonSchemaFactory factory = buildV7PlusNoFormatSchemaFactory();
JsonSchema schema = factory.getSchema(schemaInputStream);
InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
JsonNode node = getJsonNodeFromStreamContent(dataInputStream);
Set<ValidationMessage> errors = schema.validate(node);
// Both the custom no_match format and the standard email format should fail.
// This ensures that both the standard and custom formatters have been invoked.
Assertions.assertEquals(2, errors.size());
}
}
44 changes: 4 additions & 40 deletions src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,53 +59,17 @@ 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, 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));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03));
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri04));

}
}
4 changes: 4 additions & 0 deletions src/test/resources/data/issue832.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"foo": "does not match",
"contact": "not an email address"
}
16 changes: 16 additions & 0 deletions src/test/resources/schema/issue832-v7.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"properties": {
"foo": {
"type": "string",
"format": "no_match"
},
"contact": {
"type": "string",
"format": "email"
}
},
"required": ["foo", "contact"],
"type": "object"
}