summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Tay <49700559+justin-tay@users.noreply.github.com>2024-03-05 21:02:17 +0800
committerGitHub <noreply@github.com>2024-03-05 08:02:17 -0500
commiteea61d6b630ee1277371bf7ffc0e171ac4cbff54 (patch)
tree33a29be4ecf4f7ad2d6ce18885eac1a50758d0f5
parent95911ba2d4b1c375da00e9d3534ea116a3d89316 (diff)
downloadjson-schema-validator-eea61d6b630ee1277371bf7ffc0e171ac4cbff54.tar.gz
Fixes uri, uri-reference, iri, iri-reference formats and does iri to uri conversion (#983)
* Fix uri format and uri-reference format * Fix iri format and iri-reference format * Convert iri to uri
-rw-r--r--src/main/java/com/networknt/schema/format/IriFormat.java8
-rw-r--r--src/main/java/com/networknt/schema/format/IriReferenceFormat.java13
-rw-r--r--src/main/java/com/networknt/schema/format/UriFormat.java16
-rw-r--r--src/main/java/com/networknt/schema/format/UriReferenceFormat.java13
-rw-r--r--src/main/java/com/networknt/schema/resource/UriSchemaLoader.java36
-rw-r--r--src/main/java/com/networknt/schema/utils/AbsoluteIris.java172
-rw-r--r--src/test/java/com/networknt/schema/format/IriFormatTest.java74
-rw-r--r--src/test/java/com/networknt/schema/format/IriReferenceFormatTest.java74
-rw-r--r--src/test/java/com/networknt/schema/format/UriFormatTest.java74
-rw-r--r--src/test/java/com/networknt/schema/format/UriReferenceFormatTest.java74
-rw-r--r--src/test/java/com/networknt/schema/resource/UriSchemaLoaderTest.java66
-rw-r--r--src/test/java/com/networknt/schema/utils/AbsoluteIrisTest.java86
12 files changed, 702 insertions, 4 deletions
diff --git a/src/main/java/com/networknt/schema/format/IriFormat.java b/src/main/java/com/networknt/schema/format/IriFormat.java
index ff24779..150741d 100644
--- a/src/main/java/com/networknt/schema/format/IriFormat.java
+++ b/src/main/java/com/networknt/schema/format/IriFormat.java
@@ -16,6 +16,14 @@ public class IriFormat extends AbstractRFC3986Format {
return false;
}
}
+
+ String query = uri.getQuery();
+ if (query != null) {
+ // [ and ] must be percent encoded
+ if (query.indexOf('[') != -1 || query.indexOf(']') != -1) {
+ return false;
+ }
+ }
}
return result;
}
diff --git a/src/main/java/com/networknt/schema/format/IriReferenceFormat.java b/src/main/java/com/networknt/schema/format/IriReferenceFormat.java
index 4a0cf0e..45f8bd9 100644
--- a/src/main/java/com/networknt/schema/format/IriReferenceFormat.java
+++ b/src/main/java/com/networknt/schema/format/IriReferenceFormat.java
@@ -8,6 +8,19 @@ import java.net.URI;
public class IriReferenceFormat extends AbstractRFC3986Format {
@Override
protected boolean validate(URI uri) {
+ String authority = uri.getAuthority();
+ if (authority != null) {
+ if (IPv6Format.PATTERN.matcher(authority).matches() ) {
+ return false;
+ }
+ }
+ String query = uri.getQuery();
+ if (query != null) {
+ // [ and ] must be percent encoded
+ if (query.indexOf('[') != -1 || query.indexOf(']') != -1) {
+ return false;
+ }
+ }
return true;
}
diff --git a/src/main/java/com/networknt/schema/format/UriFormat.java b/src/main/java/com/networknt/schema/format/UriFormat.java
index 50fc911..81d9034 100644
--- a/src/main/java/com/networknt/schema/format/UriFormat.java
+++ b/src/main/java/com/networknt/schema/format/UriFormat.java
@@ -8,7 +8,21 @@ import java.net.URI;
public class UriFormat extends AbstractRFC3986Format {
@Override
protected boolean validate(URI uri) {
- return uri.isAbsolute();
+ boolean result = uri.isAbsolute();
+ if (result) {
+ // Java URI accepts non ASCII characters and this is not a valid in RFC3986
+ result = uri.toString().codePoints().allMatch(ch -> ch < 0x7F);
+ if (result) {
+ String query = uri.getQuery();
+ if (query != null) {
+ // [ and ] must be percent encoded
+ if (query.indexOf('[') != -1 || query.indexOf(']') != -1) {
+ return false;
+ }
+ }
+ }
+ }
+ return result;
}
@Override
diff --git a/src/main/java/com/networknt/schema/format/UriReferenceFormat.java b/src/main/java/com/networknt/schema/format/UriReferenceFormat.java
index 943afaf..d48650f 100644
--- a/src/main/java/com/networknt/schema/format/UriReferenceFormat.java
+++ b/src/main/java/com/networknt/schema/format/UriReferenceFormat.java
@@ -8,7 +8,18 @@ import java.net.URI;
public class UriReferenceFormat extends AbstractRFC3986Format {
@Override
protected boolean validate(URI uri) {
- return true;
+ // Java URI accepts non ASCII characters and this is not a valid in RFC3986
+ boolean result = uri.toString().codePoints().allMatch(ch -> ch < 0x7F);
+ if (result) {
+ String query = uri.getQuery();
+ if (query != null) {
+ // [ and ] must be percent encoded
+ if (query.indexOf('[') != -1 || query.indexOf(']') != -1) {
+ return false;
+ }
+ }
+ }
+ return result;
}
@Override
diff --git a/src/main/java/com/networknt/schema/resource/UriSchemaLoader.java b/src/main/java/com/networknt/schema/resource/UriSchemaLoader.java
index 671c086..0825353 100644
--- a/src/main/java/com/networknt/schema/resource/UriSchemaLoader.java
+++ b/src/main/java/com/networknt/schema/resource/UriSchemaLoader.java
@@ -18,11 +18,13 @@ package com.networknt.schema.resource;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import com.networknt.schema.AbsoluteIri;
+import com.networknt.schema.utils.AbsoluteIris;
/**
* Loads from uri.
@@ -30,13 +32,43 @@ import com.networknt.schema.AbsoluteIri;
public class UriSchemaLoader implements SchemaLoader {
@Override
public InputStreamSource getSchema(AbsoluteIri absoluteIri) {
- URI uri = URI.create(absoluteIri.toString());
+ URI uri = toURI(absoluteIri);
+ URL url = toURL(uri);
return () -> {
- URLConnection conn = uri.toURL().openConnection();
+ URLConnection conn = url.openConnection();
return this.openConnectionCheckRedirects(conn);
};
}
+ /**
+ * Converts an AbsoluteIRI to a URI.
+ * <p>
+ * Internationalized domain names will be converted using java.net.IDN.toASCII.
+ *
+ * @param absoluteIri the absolute IRI
+ * @return the URI
+ */
+ protected URI toURI(AbsoluteIri absoluteIri) {
+ return URI.create(AbsoluteIris.toUri(absoluteIri));
+ }
+
+ /**
+ * Converts a URI to a URL.
+ * <p>
+ * This will throw if the URI is not a valid URL. For instance if the URI is not
+ * absolute.
+ *
+ * @param uri the URL
+ * @return the URL
+ */
+ protected URL toURL(URI uri) {
+ try {
+ return uri.toURL();
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
// https://www.cs.mun.ca/java-api-1.5/guide/deployment/deployment-guide/upgrade-guide/article-17.html
protected InputStream openConnectionCheckRedirects(URLConnection c) throws IOException {
boolean redir;
diff --git a/src/main/java/com/networknt/schema/utils/AbsoluteIris.java b/src/main/java/com/networknt/schema/utils/AbsoluteIris.java
new file mode 100644
index 0000000..1c06afc
--- /dev/null
+++ b/src/main/java/com/networknt/schema/utils/AbsoluteIris.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.networknt.schema.utils;
+
+import java.io.UnsupportedEncodingException;
+import java.net.IDN;
+import java.net.URI;
+import java.net.URLEncoder;
+
+import com.networknt.schema.AbsoluteIri;
+
+/**
+ * Utility functions for AbsoluteIri.
+ */
+public class AbsoluteIris {
+ /**
+ * Converts an IRI to a URI.
+ *
+ * @param iri the IRI to convert
+ * @return the URI string
+ */
+ public static String toUri(AbsoluteIri iri) {
+ String iriString = iri.toString();
+ boolean ascii = isAscii(iriString);
+ if (ascii) {
+ int index = iriString.indexOf('?');
+ if (index == -1) {
+ return iriString;
+ }
+ String rest = iriString.substring(0, index + 1);
+ String query = iriString.substring(index + 1);
+ StringBuilder result = new StringBuilder(rest);
+ handleQuery(result, query);
+ return result.toString();
+ }
+ String[] parts = iriString.split(":"); // scheme + rest
+ if (parts.length == 2) {
+ StringBuilder result = new StringBuilder(parts[0]);
+ result.append(":");
+
+ String rest = parts[1];
+ if (rest.startsWith("//")) {
+ rest = rest.substring(2);
+ result.append("//");
+ } else if (rest.startsWith("/")) {
+ rest = rest.substring(1);
+ result.append("/");
+ }
+ String[] query = rest.split("\\?"); // rest ? query
+ String[] restParts = query[0].split("/");
+ for (int x = 0; x < restParts.length; x++) {
+ String p = restParts[x];
+ if (x == 0) {
+ // Domain
+ if (isAscii(p)) {
+ result.append(p);
+ } else {
+ result.append(unicodeToASCII(p));
+ }
+ } else {
+ result.append(p);
+ }
+ if (x != restParts.length - 1) {
+ result.append("/");
+ }
+ }
+ if (query[0].endsWith("/")) {
+ result.append("/");
+ }
+ if (query.length == 2) {
+ // handle query string
+ result.append("?");
+ handleQuery(result, query[1]);
+ }
+
+ return URI.create(result.toString()).toASCIIString();
+ }
+ return iriString;
+ }
+
+ /**
+ * Determine if a string is US ASCII.
+ *
+ * @param value to test
+ * @return true if ASCII
+ */
+ static boolean isAscii(String value) {
+ return value.codePoints().allMatch(ch -> ch < 0x7F);
+ }
+
+ /**
+ * Ensures that the query parameters are properly URL encoded.
+ *
+ * @param result the string builder to add to
+ * @param query the query string
+ */
+ static void handleQuery(StringBuilder result, String query) {
+ String[] queryParts = query.split("&");
+ for (int y = 0; y < queryParts.length; y++) {
+ String queryPart = queryParts[y];
+
+ String[] nameValue = queryPart.split("=");
+ try {
+ result.append(URLEncoder.encode(nameValue[0], "UTF-8"));
+ if (nameValue.length == 2) {
+ result.append("=");
+ result.append(URLEncoder.encode(nameValue[1], "UTF-8"));
+ }
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalArgumentException(e);
+ }
+ if (y != queryParts.length - 1) {
+ result.append("&");
+ }
+ }
+ }
+
+ // The following routines are from apache commons validator routines
+ // DomainValidator
+ static String unicodeToASCII(final String input) {
+ try {
+ final String ascii = IDN.toASCII(input);
+ if (IDNBUGHOLDER.IDN_TOASCII_PRESERVES_TRAILING_DOTS) {
+ return ascii;
+ }
+ final int length = input.length();
+ if (length == 0) { // check there is a last character
+ return input;
+ }
+ // RFC3490 3.1. 1)
+ // Whenever dots are used as label separators, the following
+ // characters MUST be recognized as dots: U+002E (full stop), U+3002
+ // (ideographic full stop), U+FF0E (fullwidth full stop), U+FF61
+ // (halfwidth ideographic full stop).
+ final char lastChar = input.charAt(length - 1);// fetch original last char
+ switch (lastChar) {
+ case '\u002E': // "." full stop
+ case '\u3002': // ideographic full stop
+ case '\uFF0E': // fullwidth full stop
+ case '\uFF61': // halfwidth ideographic full stop
+ return ascii + "."; // restore the missing stop
+ default:
+ return ascii;
+ }
+ } catch (final IllegalArgumentException e) { // input is not valid
+ return input;
+ }
+ }
+
+ private static class IDNBUGHOLDER {
+ private static final boolean IDN_TOASCII_PRESERVES_TRAILING_DOTS = keepsTrailingDot();
+
+ private static boolean keepsTrailingDot() {
+ final String input = "a."; // must be a valid name
+ return input.equals(IDN.toASCII(input));
+ }
+ }
+
+}
diff --git a/src/test/java/com/networknt/schema/format/IriFormatTest.java b/src/test/java/com/networknt/schema/format/IriFormatTest.java
new file mode 100644
index 0000000..d8655bf
--- /dev/null
+++ b/src/test/java/com/networknt/schema/format/IriFormatTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.networknt.schema.format;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import com.networknt.schema.InputFormat;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SchemaValidatorsConfig;
+import com.networknt.schema.SpecVersion.VersionFlag;
+import com.networknt.schema.ValidationMessage;
+
+class IriFormatTest {
+ @Test
+ void uriShouldPass() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"iri\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/product.pdf\"",
+ InputFormat.JSON);
+ assertTrue(messages.isEmpty());
+ }
+
+ @Test
+ void queryWithBracketsShouldFail() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"iri\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/product.pdf?filter[test]=1\"",
+ InputFormat.JSON);
+ assertFalse(messages.isEmpty());
+ }
+
+ @Test
+ void iriShouldPass() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"iri\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/produktdatenblätter.pdf\"",
+ InputFormat.JSON);
+ assertTrue(messages.isEmpty());
+ }
+
+}
diff --git a/src/test/java/com/networknt/schema/format/IriReferenceFormatTest.java b/src/test/java/com/networknt/schema/format/IriReferenceFormatTest.java
new file mode 100644
index 0000000..4bbaeda
--- /dev/null
+++ b/src/test/java/com/networknt/schema/format/IriReferenceFormatTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.networknt.schema.format;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import com.networknt.schema.InputFormat;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SchemaValidatorsConfig;
+import com.networknt.schema.SpecVersion.VersionFlag;
+import com.networknt.schema.ValidationMessage;
+
+class IriReferenceFormatTest {
+ @Test
+ void uriShouldPass() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"iri-reference\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/product.pdf\"",
+ InputFormat.JSON);
+ assertTrue(messages.isEmpty());
+ }
+
+ @Test
+ void queryWithBracketsShouldFail() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"iri-reference\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/product.pdf?filter[test]=1\"",
+ InputFormat.JSON);
+ assertFalse(messages.isEmpty());
+ }
+
+ @Test
+ void iriShouldPass() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"iri-reference\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/produktdatenblätter.pdf\"",
+ InputFormat.JSON);
+ assertTrue(messages.isEmpty());
+ }
+
+}
diff --git a/src/test/java/com/networknt/schema/format/UriFormatTest.java b/src/test/java/com/networknt/schema/format/UriFormatTest.java
new file mode 100644
index 0000000..52a850e
--- /dev/null
+++ b/src/test/java/com/networknt/schema/format/UriFormatTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.networknt.schema.format;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import com.networknt.schema.InputFormat;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SchemaValidatorsConfig;
+import com.networknt.schema.SpecVersion.VersionFlag;
+import com.networknt.schema.ValidationMessage;
+
+class UriFormatTest {
+ @Test
+ void uriShouldPass() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"uri\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/product.pdf\"",
+ InputFormat.JSON);
+ assertTrue(messages.isEmpty());
+ }
+
+ @Test
+ void queryWithBracketsShouldFail() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"uri\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/product.pdf?filter[test]=1\"",
+ InputFormat.JSON);
+ assertFalse(messages.isEmpty());
+ }
+
+ @Test
+ void iriShouldFail() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"uri\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/produktdatenblätter.pdf\"",
+ InputFormat.JSON);
+ assertFalse(messages.isEmpty());
+ }
+
+}
diff --git a/src/test/java/com/networknt/schema/format/UriReferenceFormatTest.java b/src/test/java/com/networknt/schema/format/UriReferenceFormatTest.java
new file mode 100644
index 0000000..787ba65
--- /dev/null
+++ b/src/test/java/com/networknt/schema/format/UriReferenceFormatTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.networknt.schema.format;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.Set;
+
+import org.junit.jupiter.api.Test;
+
+import com.networknt.schema.InputFormat;
+import com.networknt.schema.JsonSchema;
+import com.networknt.schema.JsonSchemaFactory;
+import com.networknt.schema.SchemaValidatorsConfig;
+import com.networknt.schema.SpecVersion.VersionFlag;
+import com.networknt.schema.ValidationMessage;
+
+class UriReferenceFormatTest {
+ @Test
+ void uriShouldPass() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"uri-reference\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/product.pdf\"",
+ InputFormat.JSON);
+ assertTrue(messages.isEmpty());
+ }
+
+ @Test
+ void queryWithBracketsShouldFail() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"uri-reference\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/product.pdf?filter[test]=1\"",
+ InputFormat.JSON);
+ assertFalse(messages.isEmpty());
+ }
+
+ @Test
+ void iriShouldFail() {
+ String schemaData = "{\r\n"
+ + " \"format\": \"uri-reference\"\r\n"
+ + "}";
+
+ SchemaValidatorsConfig config = new SchemaValidatorsConfig();
+ config.setFormatAssertionsEnabled(true);
+ JsonSchema schema = JsonSchemaFactory.getInstance(VersionFlag.V202012).getSchema(schemaData, config);
+ Set<ValidationMessage> messages = schema.validate("\"https://test.com/assets/produktdatenblätter.pdf\"",
+ InputFormat.JSON);
+ assertFalse(messages.isEmpty());
+ }
+
+}
diff --git a/src/test/java/com/networknt/schema/resource/UriSchemaLoaderTest.java b/src/test/java/com/networknt/schema/resource/UriSchemaLoaderTest.java
new file mode 100644
index 0000000..dc3e3f7
--- /dev/null
+++ b/src/test/java/com/networknt/schema/resource/UriSchemaLoaderTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.networknt.schema.resource;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import com.networknt.schema.AbsoluteIri;
+
+/**
+ * Tests for URI schema Loader.
+ */
+class UriSchemaLoaderTest {
+ /**
+ * This test should only be run manually so as not to always hit the remote
+ * server.
+ *
+ * @throws IOException the exception
+ */
+ @Test
+ @Disabled("manual")
+ void shouldLoadAbsoluteIri() throws IOException {
+ UriSchemaLoader schemaLoader = new UriSchemaLoader();
+ InputStreamSource inputStreamSource = schemaLoader.getSchema(AbsoluteIri.of("https://私の団体も.jp/"));
+ try (InputStream inputStream = inputStreamSource.getInputStream()) {
+ String result = new BufferedReader(new InputStreamReader(inputStream)).lines()
+ .collect(Collectors.joining("\n"));
+ assertNotNull(result);
+ }
+ }
+
+ @Test
+ void shouldNotThrowAbsoluteIri() throws IOException {
+ UriSchemaLoader schemaLoader = new UriSchemaLoader();
+ assertDoesNotThrow(() -> schemaLoader.getSchema(AbsoluteIri.of("https://私の団体も.jp/")));
+ }
+
+ @Test
+ void shouldThrowRelativeIri() throws IOException {
+ UriSchemaLoader schemaLoader = new UriSchemaLoader();
+ assertThrows(IllegalArgumentException.class, () -> schemaLoader.getSchema(AbsoluteIri.of("私の団体も.jp/")));
+ }
+}
diff --git a/src/test/java/com/networknt/schema/utils/AbsoluteIrisTest.java b/src/test/java/com/networknt/schema/utils/AbsoluteIrisTest.java
new file mode 100644
index 0000000..088fa87
--- /dev/null
+++ b/src/test/java/com/networknt/schema/utils/AbsoluteIrisTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.networknt.schema.utils;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+
+import org.junit.jupiter.api.Test;
+
+import com.networknt.schema.AbsoluteIri;
+
+/**
+ * Tests for AbsoluteIris.
+ */
+class AbsoluteIrisTest {
+ @Test
+ void uri() {
+ String result = AbsoluteIris.toUri(AbsoluteIri.of("https://www.example.org/test"));
+ assertEquals("https://www.example.org/test", result);
+ }
+
+ @Test
+ void uriWithQueryString() {
+ String result = AbsoluteIris.toUri(AbsoluteIri.of("https://www.example.org/test/?filter[test]=hello"));
+ assertEquals("https://www.example.org/test/?filter%5Btest%5D=hello", result);
+ }
+
+ @Test
+ void iriDomain() {
+ String result = AbsoluteIris.toUri(AbsoluteIri.of("https://Bücher.example"));
+ assertEquals("https://xn--bcher-kva.example", result);
+ }
+
+ @Test
+ void iriDomainWithPath() {
+ String result = AbsoluteIris.toUri(AbsoluteIri.of("https://Bücher.example/assets/produktdatenblätter.pdf"));
+ result = URI.create(result).toASCIIString();
+ assertEquals("https://xn--bcher-kva.example/assets/produktdatenbl%C3%A4tter.pdf", result);
+ }
+
+ @Test
+ void uriDomainWithPath() {
+ String result = AbsoluteIris.toUri(AbsoluteIri.of("https://www.example.org/assets/produktdatenblätter.pdf"));
+ result = URI.create(result).toASCIIString();
+ assertEquals("https://www.example.org/assets/produktdatenbl%C3%A4tter.pdf", result);
+ }
+
+ @Test
+ void iriDomainWithPathTrailingSlash() {
+ String result = AbsoluteIris.toUri(AbsoluteIri.of("https://Bücher.example/assets/produktdatenblätter/"));
+ assertEquals("https://xn--bcher-kva.example/assets/produktdatenbl%C3%A4tter/", result);
+ }
+
+ @Test
+ void iriDomainWithQueryString() throws MalformedURLException {
+ String result = AbsoluteIris.toUri(AbsoluteIri.of("https://Bücher.example/assets/produktdatenblätter/?filter[test]=hello"));
+ assertEquals("https://xn--bcher-kva.example/assets/produktdatenbl%C3%A4tter/?filter%5Btest%5D=hello", result);
+ URL url = URI.create(result).toURL();
+ assertEquals("https", url.getProtocol());
+ assertEquals("xn--bcher-kva.example", url.getHost());
+ assertEquals("/assets/produktdatenbl%C3%A4tter/", url.getPath());
+ assertEquals("filter%5Btest%5D=hello", url.getQuery());
+ }
+
+ @Test
+ void invalid() {
+ String result = AbsoluteIris.toUri(AbsoluteIri.of("www.example.org/test"));
+ assertEquals("www.example.org/test", result);
+ }
+}