Skip to content

Commit b1572db

Browse files
stacywsmithStacy W. Smith
and
Stacy W. Smith
authored
Always normalize uri keys of JsonSchemaFactory.jsonMetaSchemas on both read and write. (#834)
Previously, when writing to the JsonSchemaFactory.jsonMetaSchemas hash map, the uri key was not normalize, but upon reading from the hash map, the key was normalized. In addition, if a key is not found in this hash map, the standard meta schema for the uri is loaded. The result was that an attempt to augment a standard meta schema with custom formatters did not work because the standard meta schema was always used instead of the custom augmented meta schema. This change normalizes the uri keys of JsonSchemaFactory.jsonMetaSchemas on both read and write. Normalizing the uri keys on both reads and writes also eliminates the need for the forceHttps and removeEmptyFragmentSuffix arguments to the normalizeMetaSchemaUri() method. Co-authored-by: Stacy W. Smith <[email protected]>
1 parent 84a7137 commit b1572db

File tree

6 files changed

+101
-69
lines changed

6 files changed

+101
-69
lines changed

src/main/java/com/networknt/schema/JsonSchemaFactory.java

+17-24
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,6 @@ public static class Builder {
5050
private URNFactory urnFactory;
5151
private final Map<String, JsonMetaSchema> jsonMetaSchemas = new HashMap<String, JsonMetaSchema>();
5252
private final Map<String, String> uriMap = new HashMap<String, String>();
53-
private boolean forceHttps = true;
54-
private boolean removeEmptyFragmentSuffix = true;
5553
private boolean enableUriSchemaCache = true;
5654
private final CompositeURITranslator uriTranslators = new CompositeURITranslator();
5755

@@ -129,13 +127,13 @@ public Builder uriFetcher(final URIFetcher uriFetcher, final Iterable<String> sc
129127
}
130128

131129
public Builder addMetaSchema(final JsonMetaSchema jsonMetaSchema) {
132-
this.jsonMetaSchemas.put(jsonMetaSchema.getUri(), jsonMetaSchema);
130+
this.jsonMetaSchemas.put(normalizeMetaSchemaUri(jsonMetaSchema.getUri()) , jsonMetaSchema);
133131
return this;
134132
}
135133

136134
public Builder addMetaSchemas(final Collection<? extends JsonMetaSchema> jsonMetaSchemas) {
137135
for (JsonMetaSchema jsonMetaSchema : jsonMetaSchemas) {
138-
this.jsonMetaSchemas.put(jsonMetaSchema.getUri(), jsonMetaSchema);
136+
addMetaSchema(jsonMetaSchema);
139137
}
140138
return this;
141139
}
@@ -163,13 +161,21 @@ public Builder addUrnFactory(URNFactory urnFactory) {
163161
return this;
164162
}
165163

164+
/**
165+
* @deprecated No longer necessary.
166+
* @param forceHttps ignored.
167+
* @return this builder.
168+
*/
166169
public Builder forceHttps(boolean forceHttps) {
167-
this.forceHttps = forceHttps;
168170
return this;
169171
}
170172

173+
/**
174+
* @deprecated No longer necessary.
175+
* @param removeEmptyFragmentSuffix ignored.
176+
* @return this builder.
177+
*/
171178
public Builder removeEmptyFragmentSuffix(boolean removeEmptyFragmentSuffix) {
172-
this.removeEmptyFragmentSuffix = removeEmptyFragmentSuffix;
173179
return this;
174180
}
175181

@@ -189,8 +195,6 @@ public JsonSchemaFactory build() {
189195
urnFactory,
190196
jsonMetaSchemas,
191197
uriMap,
192-
forceHttps,
193-
removeEmptyFragmentSuffix,
194198
enableUriSchemaCache,
195199
uriTranslators
196200
);
@@ -207,8 +211,6 @@ public JsonSchemaFactory build() {
207211
private final Map<String, JsonMetaSchema> jsonMetaSchemas;
208212
private final Map<String, String> uriMap;
209213
private final ConcurrentMap<URI, JsonSchema> uriSchemaCache = new ConcurrentHashMap<URI, JsonSchema>();
210-
private final boolean forceHttps;
211-
private final boolean removeEmptyFragmentSuffix;
212214
private final boolean enableUriSchemaCache;
213215

214216

@@ -221,8 +223,6 @@ private JsonSchemaFactory(
221223
final URNFactory urnFactory,
222224
final Map<String, JsonMetaSchema> jsonMetaSchemas,
223225
final Map<String, String> uriMap,
224-
final boolean forceHttps,
225-
final boolean removeEmptyFragmentSuffix,
226226
final boolean enableUriSchemaCache,
227227
final CompositeURITranslator uriTranslators) {
228228
if (jsonMapper == null) {
@@ -237,7 +237,7 @@ private JsonSchemaFactory(
237237
throw new IllegalArgumentException("URIFetcher must not be null");
238238
} else if (jsonMetaSchemas == null || jsonMetaSchemas.isEmpty()) {
239239
throw new IllegalArgumentException("Json Meta Schemas must not be null or empty");
240-
} else if (jsonMetaSchemas.get(defaultMetaSchemaURI) == null) {
240+
} else if (jsonMetaSchemas.get(normalizeMetaSchemaUri(defaultMetaSchemaURI)) == null) {
241241
throw new IllegalArgumentException("Meta Schema for default Meta Schema URI must be provided");
242242
} else if (uriMap == null) {
243243
throw new IllegalArgumentException("URL Mappings must not be null");
@@ -252,8 +252,6 @@ private JsonSchemaFactory(
252252
this.urnFactory = urnFactory;
253253
this.jsonMetaSchemas = jsonMetaSchemas;
254254
this.uriMap = uriMap;
255-
this.forceHttps = forceHttps;
256-
this.removeEmptyFragmentSuffix = removeEmptyFragmentSuffix;
257255
this.enableUriSchemaCache = enableUriSchemaCache;
258256
this.uriTranslators = uriTranslators;
259257
}
@@ -348,7 +346,7 @@ private JsonMetaSchema findMetaSchemaForSchema(final JsonNode schemaNode) {
348346
if (uriNode != null && !uriNode.isNull() && !uriNode.isTextual()) {
349347
throw new JsonSchemaException("Unknown MetaSchema: " + uriNode.toString());
350348
}
351-
final String uri = uriNode == null || uriNode.isNull() ? defaultMetaSchemaURI : normalizeMetaSchemaUri(uriNode.textValue(), forceHttps, removeEmptyFragmentSuffix);
349+
final String uri = uriNode == null || uriNode.isNull() ? defaultMetaSchemaURI : normalizeMetaSchemaUri(uriNode.textValue());
352350
final JsonMetaSchema jsonMetaSchema = jsonMetaSchemas.computeIfAbsent(uri, this::fromId);
353351
return jsonMetaSchema;
354352
}
@@ -504,17 +502,12 @@ private boolean isYaml(final URI schemaUri) {
504502
return (".yml".equals(extension) || ".yaml".equals(extension));
505503
}
506504

507-
static protected String normalizeMetaSchemaUri(String u, boolean forceHttps, boolean removeEmptyFragmentSuffix) {
505+
static protected String normalizeMetaSchemaUri(String u) {
508506
try {
509507
URI uri = new URI(u);
510-
String scheme = forceHttps ? "https" : uri.getScheme();
511-
URI newUri = new URI(scheme, uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), null, null);
508+
URI newUri = new URI("https", uri.getUserInfo(), uri.getHost(), uri.getPort(), uri.getPath(), null, null);
512509

513-
if (!removeEmptyFragmentSuffix && u.endsWith("#")) {
514-
return newUri + "#";
515-
} else {
516-
return newUri.toString();
517-
}
510+
return newUri.toString();
518511
} catch (URISyntaxException e) {
519512
throw new JsonSchemaException("Wrong MetaSchema URI: " + u);
520513
}

src/main/java/com/networknt/schema/SpecVersionDetector.java

+1-5
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,8 @@ public static VersionFlag detect(JsonNode jsonNode) {
5858
public static Optional<VersionFlag> detectOptionalVersion(JsonNode jsonNode) {
5959
return Optional.ofNullable(jsonNode.get(SCHEMA_TAG)).map(schemaTag -> {
6060

61-
final boolean forceHttps = true;
62-
final boolean removeEmptyFragmentSuffix = true;
63-
6461
String schemaTagValue = schemaTag.asText();
65-
String schemaUri = JsonSchemaFactory.normalizeMetaSchemaUri(schemaTagValue, forceHttps,
66-
removeEmptyFragmentSuffix);
62+
String schemaUri = JsonSchemaFactory.normalizeMetaSchemaUri(schemaTagValue);
6763

6864
return VersionFlag.fromId(schemaUri)
6965
.orElseThrow(() -> new JsonSchemaException("'" + schemaTagValue + "' is unrecognizable schema"));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.networknt.schema;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.networknt.schema.format.AbstractFormat;
6+
import org.junit.jupiter.api.Assertions;
7+
import org.junit.jupiter.api.Test;
8+
9+
import java.io.IOException;
10+
import java.io.InputStream;
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
import java.util.Set;
14+
15+
public class Issue832Test {
16+
private class NoMatchFormat extends AbstractFormat {
17+
public NoMatchFormat() {
18+
super("no_match", "always fail match");
19+
}
20+
21+
@Override
22+
public boolean matches(String value) {
23+
return false;
24+
}
25+
}
26+
27+
private JsonSchemaFactory buildV7PlusNoFormatSchemaFactory() {
28+
List<Format> formats;
29+
formats = new ArrayList<>();
30+
formats.add(new NoMatchFormat());
31+
32+
JsonMetaSchema jsonMetaSchema = JsonMetaSchema.builder(
33+
JsonMetaSchema.getV7().getUri(),
34+
JsonMetaSchema.getV7())
35+
.addFormats(formats)
36+
.build();
37+
return new JsonSchemaFactory.Builder().defaultMetaSchemaURI(jsonMetaSchema.getUri()).addMetaSchema(jsonMetaSchema).build();
38+
}
39+
40+
protected JsonNode getJsonNodeFromStreamContent(InputStream content) throws IOException {
41+
ObjectMapper mapper = new ObjectMapper();
42+
return mapper.readTree(content);
43+
}
44+
45+
@Test
46+
public void testV7WithNonMatchingCustomFormat() throws IOException {
47+
String schemaPath = "/schema/issue832-v7.json";
48+
String dataPath = "/data/issue832.json";
49+
InputStream schemaInputStream = getClass().getResourceAsStream(schemaPath);
50+
JsonSchemaFactory factory = buildV7PlusNoFormatSchemaFactory();
51+
JsonSchema schema = factory.getSchema(schemaInputStream);
52+
InputStream dataInputStream = getClass().getResourceAsStream(dataPath);
53+
JsonNode node = getJsonNodeFromStreamContent(dataInputStream);
54+
Set<ValidationMessage> errors = schema.validate(node);
55+
// Both the custom no_match format and the standard email format should fail.
56+
// This ensures that both the standard and custom formatters have been invoked.
57+
Assertions.assertEquals(2, errors.size());
58+
}
59+
}

src/test/java/com/networknt/schema/UnknownMetaSchemaTest.java

+4-40
Original file line numberDiff line numberDiff line change
@@ -59,53 +59,17 @@ public void testSchema3() throws IOException {
5959

6060
@Test
6161
public void testNormalize() throws JsonSchemaException {
62-
final boolean forceHttps = true;
63-
final boolean removeEmptyFragmentSuffix = true;
6462

6563
String uri01 = "http://json-schema.org/draft-07/schema";
6664
String uri02 = "http://json-schema.org/draft-07/schema#";
6765
String uri03 = "http://json-schema.org/draft-07/schema?key=value";
6866
String uri04 = "http://json-schema.org/draft-07/schema?key=value&key2=value2";
6967
String expected = "https://json-schema.org/draft-07/schema";
7068

71-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, forceHttps, removeEmptyFragmentSuffix));
72-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, forceHttps, removeEmptyFragmentSuffix));
73-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, forceHttps, removeEmptyFragmentSuffix));
74-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri04, forceHttps, removeEmptyFragmentSuffix));
75-
76-
}
77-
78-
@Test
79-
public void testNormalizeForceHttpsDisabled() throws JsonSchemaException {
80-
final boolean forceHttps = false;
81-
final boolean removeEmptyFragmentSuffix = true;
82-
83-
String uri01 = "http://json-schema.org/draft-07/schema";
84-
String uri02 = "http://json-schema.org/draft-07/schema#";
85-
String uri03 = "http://json-schema.org/draft-07/schema?key=value";
86-
String uri04 = "http://json-schema.org/draft-07/schema?key=value&key2=value2";
87-
String expected = "http://json-schema.org/draft-07/schema";
88-
89-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, forceHttps, removeEmptyFragmentSuffix));
90-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, forceHttps, removeEmptyFragmentSuffix));
91-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, forceHttps, removeEmptyFragmentSuffix));
92-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri04, forceHttps, removeEmptyFragmentSuffix));
93-
94-
}
95-
96-
@Test
97-
public void testNormalizeRemovingEmptyFragmentSuffixDisabled() throws JsonSchemaException {
98-
final boolean forceHttps = true;
99-
final boolean removeEmptyFragmentSuffix = false;
100-
101-
String uri01 = "http://json-schema.org/draft-07/schema#";
102-
String uri02 = "http://json-schema.org/draft-07/schema?key=value#";
103-
String uri03 = "http://json-schema.org/draft-07/schema?key=value&key2=value2#";
104-
String expected = "https://json-schema.org/draft-07/schema#";
105-
106-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01, forceHttps, removeEmptyFragmentSuffix));
107-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02, forceHttps, removeEmptyFragmentSuffix));
108-
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03, forceHttps, removeEmptyFragmentSuffix));
69+
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri01));
70+
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri02));
71+
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri03));
72+
Assertions.assertEquals(expected, JsonSchemaFactory.normalizeMetaSchemaUri(uri04));
10973

11074
}
11175
}

src/test/resources/data/issue832.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"foo": "does not match",
3+
"contact": "not an email address"
4+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"additionalProperties": false,
4+
"properties": {
5+
"foo": {
6+
"type": "string",
7+
"format": "no_match"
8+
},
9+
"contact": {
10+
"type": "string",
11+
"format": "email"
12+
}
13+
},
14+
"required": ["foo", "contact"],
15+
"type": "object"
16+
}

0 commit comments

Comments
 (0)