partialRecursiv
}
}
-
/**
* Canonicalize the given content.
*
@@ -82,7 +148,8 @@ private static ContentHandle canonicalizeContent(String artifactType, ContentHan
return ARTIFACT_TYPE_UTIL.getArtifactTypeProvider(artifactType)
.getContentCanonicalizer()
.canonicalize(content, recursivelyResolvedReferences);
- } catch (Exception ex) {
+ }
+ catch (Exception ex) {
// TODO: We should consider explicitly failing when a content could not be canonicalized.
// throw new RegistryException("Failed to canonicalize content.", ex);
log.debug("Failed to canonicalize content: {}", content.content());
@@ -90,7 +157,6 @@ private static ContentHandle canonicalizeContent(String artifactType, ContentHan
}
}
-
/**
* Canonicalize the given content.
*
@@ -99,12 +165,12 @@ private static ContentHandle canonicalizeContent(String artifactType, ContentHan
public static ContentHandle canonicalizeContent(String artifactType, ContentAndReferencesDto data, Function loader) {
try {
return canonicalizeContent(artifactType, data.getContent(), recursivelyResolveReferences(data.getReferences(), loader));
- } catch (Exception ex) {
+ }
+ catch (Exception ex) {
throw new RegistryException("Failed to canonicalize content.", ex);
}
}
-
/**
* @param loader can be null *if and only if* references are empty.
*/
@@ -114,16 +180,17 @@ public static String canonicalContentHash(String artifactType, ContentAndReferen
String serializedReferences = serializeReferences(data.getReferences());
ContentHandle canonicalContent = canonicalizeContent(artifactType, data, loader);
return DigestUtils.sha256Hex(concatContentAndReferences(canonicalContent.bytes(), serializedReferences));
- } else {
+ }
+ else {
ContentHandle canonicalContent = canonicalizeContent(artifactType, data.getContent(), Map.of());
return DigestUtils.sha256Hex(canonicalContent.bytes());
}
- } catch (IOException ex) {
+ }
+ catch (IOException ex) {
throw new RegistryException("Failed to compute canonical content hash.", ex);
}
}
-
/**
* data.references may be null
*/
@@ -132,15 +199,16 @@ public static String contentHash(ContentAndReferencesDto data) {
if (notEmpty(data.getReferences())) {
String serializedReferences = serializeReferences(data.getReferences());
return DigestUtils.sha256Hex(concatContentAndReferences(data.getContent().bytes(), serializedReferences));
- } else {
+ }
+ else {
return data.getContent().getSha256Hash();
}
- } catch (IOException ex) {
+ }
+ catch (IOException ex) {
throw new RegistryException("Failed to compute content hash.", ex);
}
}
-
private static byte[] concatContentAndReferences(byte[] contentBytes, String serializedReferences) throws IOException {
if (serializedReferences != null && !serializedReferences.isEmpty()) {
var serializedReferencesBytes = ContentHandle.create(serializedReferences).bytes();
@@ -148,12 +216,12 @@ private static byte[] concatContentAndReferences(byte[] contentBytes, String ser
bytes.put(contentBytes);
bytes.put(serializedReferencesBytes);
return bytes.array();
- } else {
+ }
+ else {
throw new IllegalArgumentException("serializedReferences is null or empty");
}
}
-
/**
* Serializes the given collection of labels to a string for artifactStore in the DB.
*
@@ -168,12 +236,12 @@ public static String serializeLabels(List labels) {
return null;
}
return MAPPER.writeValueAsString(labels);
- } catch (JsonProcessingException e) {
+ }
+ catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
-
/**
* Deserialize the labels from their string form to a List<String>
form.
*
@@ -186,12 +254,12 @@ public static List deserializeLabels(String labelsStr) {
return null;
}
return MAPPER.readValue(labelsStr, List.class);
- } catch (JsonProcessingException e) {
+ }
+ catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
-
/**
* Serializes the given collection of properties to a string for artifactStore in the DB.
*
@@ -206,12 +274,12 @@ public static String serializeProperties(Map properties) {
return null;
}
return MAPPER.writeValueAsString(properties);
- } catch (JsonProcessingException e) {
+ }
+ catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
-
/**
* Deserialize the properties from their string form to a Map form.
*
@@ -224,12 +292,12 @@ public static Map deserializeProperties(String propertiesStr) {
return null;
}
return MAPPER.readValue(propertiesStr, Map.class);
- } catch (JsonProcessingException e) {
+ }
+ catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
-
/**
* Serializes the given collection of references to a string for artifactStore in the DB.
*
@@ -241,12 +309,12 @@ public static String serializeReferences(List references)
return null;
}
return MAPPER.writeValueAsString(references);
- } catch (JsonProcessingException e) {
+ }
+ catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
-
/**
* Deserialize the references from their string form to a List form.
*
@@ -259,12 +327,12 @@ public static List deserializeReferences(String references
}
return MAPPER.readValue(references, new TypeReference>() {
});
- } catch (JsonProcessingException e) {
+ }
+ catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
-
public static String normalizeGroupId(String groupId) {
if (groupId == null || "default".equals(groupId)) {
return NULL_GROUP_ID;
@@ -272,7 +340,6 @@ public static String normalizeGroupId(String groupId) {
return groupId;
}
-
public static String denormalizeGroupId(String groupId) {
if (NULL_GROUP_ID.equals(groupId)) {
return null;
@@ -280,8 +347,29 @@ public static String denormalizeGroupId(String groupId) {
return groupId;
}
-
public static boolean notEmpty(Collection> collection) {
return collection != null && !collection.isEmpty();
}
+
+ public static String concatArtifactVersionCoordinatesWithRefName(String groupId, String artifactId, String version, String referenceName) {
+ return groupId + ":" + artifactId + ":" + version + ":" + referenceName;
+ }
+
+ public static class RewrittenContentHolder {
+ final ContentHandle rewrittenContent;
+ final Map resolvedReferences;
+
+ public RewrittenContentHolder(ContentHandle rewrittenContent, Map resolvedReferences) {
+ this.rewrittenContent = rewrittenContent;
+ this.resolvedReferences = resolvedReferences;
+ }
+
+ public ContentHandle getRewrittenContent() {
+ return rewrittenContent;
+ }
+
+ public Map getResolvedReferences() {
+ return resolvedReferences;
+ }
+ }
}
diff --git a/app/src/test/java/io/apicurio/registry/noprofile/rest/v2/GroupsResourceTest.java b/app/src/test/java/io/apicurio/registry/noprofile/rest/v2/GroupsResourceTest.java
index 899206be83..ed988da7d9 100644
--- a/app/src/test/java/io/apicurio/registry/noprofile/rest/v2/GroupsResourceTest.java
+++ b/app/src/test/java/io/apicurio/registry/noprofile/rest/v2/GroupsResourceTest.java
@@ -47,10 +47,16 @@
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
import static io.restassured.RestAssured.given;
-import static java.net.HttpURLConnection.*;
+import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+import static java.net.HttpURLConnection.HTTP_OK;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.hamcrest.CoreMatchers.anything;
import static org.hamcrest.Matchers.*;
@@ -157,7 +163,7 @@ public void testDefaultGroup() throws Exception {
@Test
public void testUpdateArtifactOwner() throws Exception {
String oaiArtifactContent = resourceToString("openapi-empty.json");
- createArtifact("testUpdateArtifactOwner", "testUpdateArtifactOwner/EmptyAPI/1",ArtifactType.OPENAPI, oaiArtifactContent);
+ createArtifact("testUpdateArtifactOwner", "testUpdateArtifactOwner/EmptyAPI/1", ArtifactType.OPENAPI, oaiArtifactContent);
ArtifactOwner artifactOwner = new ArtifactOwner("newOwner");
@@ -175,7 +181,7 @@ public void testUpdateArtifactOwner() throws Exception {
@Test
public void testUpdateEmptyArtifactOwner() throws Exception {
String oaiArtifactContent = resourceToString("openapi-empty.json");
- createArtifact("testUpdateEmptyArtifactOwner", "testUpdateEmptyArtifactOwner/EmptyAPI/1",ArtifactType.OPENAPI, oaiArtifactContent);
+ createArtifact("testUpdateEmptyArtifactOwner", "testUpdateEmptyArtifactOwner/EmptyAPI/1", ArtifactType.OPENAPI, oaiArtifactContent);
ArtifactOwner artifactOwner = new ArtifactOwner("");
@@ -615,7 +621,7 @@ public void testUpdateArtifact() throws Exception {
@Test
public void testUpdateArtifactState() throws Exception {
String oaiArtifactContent = resourceToString("openapi-empty.json");
- createArtifact("testUpdateArtifactState", "testUpdateArtifactState/EmptyAPI/1",ArtifactType.OPENAPI, oaiArtifactContent);
+ createArtifact("testUpdateArtifactState", "testUpdateArtifactState/EmptyAPI/1", ArtifactType.OPENAPI, oaiArtifactContent);
UpdateState updateState = new UpdateState();
updateState.setState(ArtifactState.DEPRECATED);
@@ -652,17 +658,17 @@ public void testUpdateArtifactState() throws Exception {
.then()
.statusCode(200)
.header("X-Registry-Deprecated", "true");
- }
+ }
@Test
public void testUpdateArtifactVersionState() throws Exception {
String oaiArtifactContent = resourceToString("openapi-empty.json");
- createArtifact("testUpdateArtifactVersionState", "testUpdateArtifactVersionState/EmptyAPI",ArtifactType.OPENAPI, oaiArtifactContent);
+ createArtifact("testUpdateArtifactVersionState", "testUpdateArtifactVersionState/EmptyAPI", ArtifactType.OPENAPI, oaiArtifactContent);
UpdateState updateState = new UpdateState();
updateState.setState(ArtifactState.DEPRECATED);
- // Update the artifact state to DEPRECATED.
+ // Update the artifact state to DEPRECATED.
given()
.when()
.contentType(CT_JSON)
@@ -697,7 +703,7 @@ public void testUpdateArtifactVersionState() throws Exception {
.then()
.statusCode(200)
.header("X-Registry-Deprecated", "true");
- }
+ }
@Test
@DisabledIfEnvironmentVariable(named = CURRENT_ENV, matches = CURRENT_ENV_MAS_REGEX)
@@ -1040,7 +1046,7 @@ public void testListArtifactVersions() throws Exception {
.pathParam("artifactId", artifactId)
.get("/registry/v2/groups/{groupId}/artifacts/{artifactId}/versions")
.then()
-// .log().all()
+ // .log().all()
.statusCode(200)
.body("count", equalTo(6))
.body("versions[0].version", notNullValue());
@@ -1619,7 +1625,7 @@ public void testArtifactRules() throws Exception {
.pathParam("artifactId", artifactId)
.get("/registry/v2/groups/{groupId}/artifacts/{artifactId}/rules")
.then()
-// .log().all()
+ // .log().all()
.statusCode(200)
.contentType(ContentType.JSON)
.body("[0]", anyOf(equalTo("VALIDITY"), equalTo("COMPATIBILITY")))
@@ -1710,7 +1716,6 @@ public void testArtifactMetaData() throws Exception {
.then()
.statusCode(204);
-
// Get the (updated) artifact meta-data
TestUtils.retry(() -> {
List expectedLabels = Arrays.asList("Empty API label 1", "Empty API label 2");
@@ -1959,7 +1964,6 @@ public void testYamlContentType() throws Exception {
.body("info.title", equalTo("Empty API"));
}
-
@Test
public void testWsdlArtifact() throws Exception {
String artifactId = "testWsdlArtifact";
@@ -2491,7 +2495,6 @@ void testArtifactWithReferences() throws Exception {
var referencingMD = metadata;
assertEquals(references, metadata.getReferences());
-
// Trying to use different references with the same content is ok, but the contentId and contentHash is different.
List references2 = List.of(ArtifactReference.builder()
.groupId(metadata.getGroupId())
@@ -2539,7 +2542,8 @@ void testArtifactWithReferences() throws Exception {
final String referencesSerialized = RegistryContentUtils.serializeReferences(toReferenceDtos(references));
//We calculate the hash using the content itself and the references
- String contentHash = DigestUtils.sha256Hex(concatContentAndReferences(artifactContent.getBytes(StandardCharsets.UTF_8), referencesSerialized.getBytes(StandardCharsets.UTF_8)));
+ String contentHash = DigestUtils.sha256Hex(
+ concatContentAndReferences(artifactContent.getBytes(StandardCharsets.UTF_8), referencesSerialized.getBytes(StandardCharsets.UTF_8)));
assertEquals(references, referenceResponse);
@@ -2604,15 +2608,14 @@ void testArtifactWithReferences() throws Exception {
assertEquals(referencingMD.getId(), referenceResponse.get(0).getArtifactId());
assertEquals(referencingMD.getVersion(), referenceResponse.get(0).getVersion());
-
- // Dereferencing JSON Schema fails with 400 as it's not implemented.
+ // Dereferencing JSON Schema works
given()
.when()
.pathParam("globalId", metadata.getGlobalId())
.queryParam("dereference", true)
.get("/registry/v2/ids/globalIds/{globalId}")
.then()
- .statusCode(HTTP_BAD_REQUEST);
+ .statusCode(HTTP_OK);
}
private byte[] concatContentAndReferences(byte[] contentBytes, byte[] referencesBytes) throws IOException {
@@ -2621,7 +2624,7 @@ private byte[] concatContentAndReferences(byte[] contentBytes, byte[] references
outputStream.write(referencesBytes);
return outputStream.toByteArray();
}
-
+
@Test
public void testArtifactComments() throws Exception {
String artifactId = "testArtifactComments/EmptyAPI";
@@ -2641,7 +2644,7 @@ public void testArtifactComments() throws Exception {
.extract().as(new TypeRef>() {
});
assertEquals(0, comments.size());
-
+
// Create a new comment
NewComment nc = NewComment.builder().value("COMMENT_1").build();
Comment comment1 = given()
@@ -2659,7 +2662,7 @@ public void testArtifactComments() throws Exception {
assertNotNull(comment1.getValue());
assertNotNull(comment1.getCreatedOn());
assertEquals("COMMENT_1", comment1.getValue());
-
+
// Create another new comment
nc = NewComment.builder().value("COMMENT_2").build();
Comment comment2 = given()
@@ -2677,7 +2680,7 @@ public void testArtifactComments() throws Exception {
assertNotNull(comment2.getValue());
assertNotNull(comment2.getCreatedOn());
assertEquals("COMMENT_2", comment2.getValue());
-
+
// Get the list of comments (should have 2)
comments = given()
.when()
diff --git a/app/src/test/java/io/apicurio/registry/noprofile/serde/JsonSchemaSerdeTest.java b/app/src/test/java/io/apicurio/registry/noprofile/serde/JsonSchemaSerdeTest.java
index 9bc0e8fc3a..2e9e647cb6 100644
--- a/app/src/test/java/io/apicurio/registry/noprofile/serde/JsonSchemaSerdeTest.java
+++ b/app/src/test/java/io/apicurio/registry/noprofile/serde/JsonSchemaSerdeTest.java
@@ -40,6 +40,8 @@
import io.apicurio.registry.support.Citizen;
import io.apicurio.registry.support.CitizenIdentifier;
import io.apicurio.registry.support.City;
+import io.apicurio.registry.support.CityQualification;
+import io.apicurio.registry.support.IdentifierQualification;
import io.apicurio.registry.support.Person;
import io.apicurio.registry.support.Qualification;
import io.apicurio.registry.types.ArtifactType;
@@ -96,7 +98,7 @@ public void testJsonSchemaSerde() throws Exception {
Person person = new Person("Ales", "Justin", 23);
try (JsonSchemaKafkaSerializer serializer = new JsonSchemaKafkaSerializer<>(restClient, true);
- Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
+ Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
Map config = new HashMap<>();
config.put(SerdeConfig.EXPLICIT_ARTIFACT_GROUP_ID, groupId);
@@ -119,7 +121,8 @@ public void testJsonSchemaSerde() throws Exception {
try {
serializer.serialize(artifactId, new RecordHeaders(), person);
Assertions.fail();
- } catch (Exception ignored) {
+ }
+ catch (Exception ignored) {
}
serializer.setValidationEnabled(false); // disable validation
@@ -129,7 +132,8 @@ public void testJsonSchemaSerde() throws Exception {
try {
deserializer.deserialize(artifactId, headers, bytes);
Assertions.fail();
- } catch (Exception ignored) {
+ }
+ catch (Exception ignored) {
}
}
}
@@ -142,7 +146,7 @@ public void testJsonSchemaSerdeAutoRegister() throws Exception {
Person person = new Person("Carles", "Arnal", 30);
try (JsonSchemaKafkaSerializer serializer = new JsonSchemaKafkaSerializer<>(restClient, true);
- Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
+ Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
Map config = new HashMap<>();
config.put(SerdeConfig.EXPLICIT_ARTIFACT_GROUP_ID, groupId);
@@ -167,7 +171,8 @@ public void testJsonSchemaSerdeAutoRegister() throws Exception {
try {
serializer.serialize(artifactId, new RecordHeaders(), person);
Assertions.fail();
- } catch (Exception ignored) {
+ }
+ catch (Exception ignored) {
}
serializer.setValidationEnabled(false); // disable validation
@@ -177,7 +182,8 @@ public void testJsonSchemaSerdeAutoRegister() throws Exception {
try {
deserializer.deserialize(artifactId, headers, bytes);
Assertions.fail();
- } catch (Exception ignored) {
+ }
+ catch (Exception ignored) {
}
}
}
@@ -197,7 +203,7 @@ public void testJsonSchemaSerdeHeaders() throws Exception {
Person person = new Person("Ales", "Justin", 23);
try (JsonSchemaKafkaSerializer serializer = new JsonSchemaKafkaSerializer<>(restClient, true);
- Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
+ Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
Map config = new HashMap<>();
config.put(SerdeConfig.EXPLICIT_ARTIFACT_GROUP_ID, groupId);
@@ -243,7 +249,7 @@ public void testJsonSchemaSerdeMagicByte() throws Exception {
Person person = new Person("Ales", "Justin", 23);
try (JsonSchemaKafkaSerializer serializer = new JsonSchemaKafkaSerializer<>(restClient, true);
- Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
+ Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
Map config = new HashMap<>();
config.put(SerdeConfig.EXPLICIT_ARTIFACT_GROUP_ID, groupId);
@@ -269,9 +275,9 @@ public void testJsonSchemaSerdeMagicByte() throws Exception {
@Test
public void testJsonSchemaSerdeWithReferences() throws Exception {
- InputStream citySchema = getClass().getResourceAsStream("/io/apicurio/registry/util/city.json");
- InputStream citizenSchema = getClass().getResourceAsStream("/io/apicurio/registry/util/citizen.json");
- InputStream citizenIdentifier = getClass().getResourceAsStream("/io/apicurio/registry/util/citizenIdentifier.json");
+ InputStream citySchema = getClass().getResourceAsStream("/io/apicurio/registry/util/city1.json");
+ InputStream citizenSchema = getClass().getResourceAsStream("/io/apicurio/registry/util/citizen1.json");
+ InputStream citizenIdentifier = getClass().getResourceAsStream("/io/apicurio/registry/util/citizenIdentifier1.json");
InputStream qualificationSchema = getClass().getResourceAsStream("/io/apicurio/registry/util/qualification.json");
InputStream addressSchema = getClass().getResourceAsStream("/io/apicurio/registry/util/sample.address.json");
@@ -282,14 +288,12 @@ public void testJsonSchemaSerdeWithReferences() throws Exception {
Assertions.assertNotNull(qualificationSchema);
Assertions.assertNotNull(addressSchema);
-
String groupId = TestUtils.generateGroupId();
String cityArtifactId = generateArtifactId();
String qualificationsId = generateArtifactId();
String identifierArtifactId = generateArtifactId();
String addressId = generateArtifactId();
-
final Integer cityDependencyGlobalId = createArtifact(groupId, cityArtifactId, ArtifactType.JSON, IoUtil.toString(citySchema));
this.waitForGlobalId(cityDependencyGlobalId);
@@ -315,7 +319,7 @@ public void testJsonSchemaSerdeWithReferences() throws Exception {
cityReference.setVersion("1");
cityReference.setGroupId(groupId);
cityReference.setArtifactId(cityArtifactId);
- cityReference.setName("city.json");
+ cityReference.setName("city1.json");
final Integer identifierDependencyGlobalId = createArtifact(groupId, identifierArtifactId, ArtifactType.JSON, IoUtil.toString(citizenIdentifier));
this.waitForGlobalId(identifierDependencyGlobalId);
@@ -324,11 +328,12 @@ public void testJsonSchemaSerdeWithReferences() throws Exception {
identifierReference.setVersion("1");
identifierReference.setGroupId(groupId);
identifierReference.setArtifactId(identifierArtifactId);
- identifierReference.setName("citizenIdentifier.json");
+ identifierReference.setName("citizenIdentifier1.json");
String artifactId = generateArtifactId();
- final Integer globalId = createArtifactWithReferences(groupId, artifactId, ArtifactType.JSON, IoUtil.toString(citizenSchema), List.of(qualificationsReference, cityReference, identifierReference, addressReference));
+ final Integer globalId = createArtifactWithReferences(groupId, artifactId, ArtifactType.JSON, IoUtil.toString(citizenSchema),
+ List.of(qualificationsReference, cityReference, identifierReference, addressReference));
this.waitForGlobalId(globalId);
City city = new City("New York", 10001);
@@ -336,7 +341,7 @@ public void testJsonSchemaSerdeWithReferences() throws Exception {
Citizen citizen = new Citizen("Carles", "Arnal", 23, city, identifier, Collections.emptyList());
try (JsonSchemaKafkaSerializer serializer = new JsonSchemaKafkaSerializer<>(restClient, true);
- Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
+ Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
Map config = new HashMap<>();
config.put(SerdeConfig.EXPLICIT_ARTIFACT_GROUP_ID, groupId);
@@ -360,7 +365,8 @@ public void testJsonSchemaSerdeWithReferences() throws Exception {
try {
serializer.serialize(artifactId, new RecordHeaders(), citizen);
Assertions.fail();
- } catch (Exception ignored) {
+ }
+ catch (Exception ignored) {
}
citizen.setAge(23);
@@ -370,9 +376,151 @@ public void testJsonSchemaSerdeWithReferences() throws Exception {
try {
serializer.serialize(artifactId, new RecordHeaders(), citizen);
Assertions.fail();
- } catch (Exception ignored) {
+ }
+ catch (Exception ignored) {
+ }
+
+ //invalid identifier present, should fail
+ identifier = new CitizenIdentifier(-1234356);
+ citizen.setIdentifier(identifier);
+
+ city = new City("Kansas CIty", 22222);
+ citizen.setCity(city);
+
+ try {
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
+ Assertions.fail();
+ }
+ catch (Exception ignored) {
+ }
+
+ //no identifier present, should pass
+ citizen.setIdentifier(null);
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
+
+ //valid qualification, should pass
+ citizen.setQualifications(List.of(new Qualification(UUID.randomUUID().toString(), 6), new Qualification(UUID.randomUUID().toString(), 7),
+ new Qualification(UUID.randomUUID().toString(), 8)));
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
+
+ //invalid qualification, should fail
+ citizen.setQualifications(List.of(new Qualification(UUID.randomUUID().toString(), 6), new Qualification(UUID.randomUUID().toString(), -7),
+ new Qualification(UUID.randomUUID().toString(), 8)));
+ try {
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
+ Assertions.fail();
+ }
+ catch (Exception ignored) {
+ }
+ }
+ }
+
+ @Test
+ public void testJsonSchemaSerdeWithReferencesDeserializerDereferenced() throws Exception {
+ InputStream citySchema = getClass().getResourceAsStream("/io/apicurio/registry/util/city1.json");
+ InputStream citizenSchema = getClass().getResourceAsStream("/io/apicurio/registry/util/citizen1.json");
+ InputStream citizenIdentifier = getClass().getResourceAsStream("/io/apicurio/registry/util/citizenIdentifier1.json");
+ InputStream qualificationSchema = getClass().getResourceAsStream("/io/apicurio/registry/util/qualification.json");
+
+ InputStream addressSchema = getClass().getResourceAsStream("/io/apicurio/registry/util/sample.address.json");
+
+ Assertions.assertNotNull(citizenSchema);
+ Assertions.assertNotNull(citySchema);
+ Assertions.assertNotNull(citizenIdentifier);
+ Assertions.assertNotNull(qualificationSchema);
+ Assertions.assertNotNull(addressSchema);
+
+ String groupId = TestUtils.generateGroupId();
+ String cityArtifactId = generateArtifactId();
+ String qualificationsId = generateArtifactId();
+ String identifierArtifactId = generateArtifactId();
+ String addressId = generateArtifactId();
+
+ final Integer cityDependencyGlobalId = createArtifact(groupId, cityArtifactId, ArtifactType.JSON, IoUtil.toString(citySchema));
+ this.waitForGlobalId(cityDependencyGlobalId);
+
+ final Integer qualificationsGlobalId = createArtifact(groupId, qualificationsId, ArtifactType.JSON, IoUtil.toString(qualificationSchema));
+ this.waitForGlobalId(qualificationsGlobalId);
+
+ final ArtifactReference qualificationsReference = new ArtifactReference();
+ qualificationsReference.setVersion("1");
+ qualificationsReference.setGroupId(groupId);
+ qualificationsReference.setArtifactId(qualificationsId);
+ qualificationsReference.setName("qualification.json");
+
+ final Integer addressGlobalID = createArtifact(groupId, addressId, ArtifactType.JSON, IoUtil.toString(addressSchema));
+ this.waitForGlobalId(addressGlobalID);
+
+ final ArtifactReference addressReference = new ArtifactReference();
+ addressReference.setVersion("1");
+ addressReference.setGroupId(groupId);
+ addressReference.setArtifactId(addressId);
+ addressReference.setName("sample.address.json");
+
+ final ArtifactReference cityReference = new ArtifactReference();
+ cityReference.setVersion("1");
+ cityReference.setGroupId(groupId);
+ cityReference.setArtifactId(cityArtifactId);
+ cityReference.setName("city1.json");
+
+ final Integer identifierDependencyGlobalId = createArtifact(groupId, identifierArtifactId, ArtifactType.JSON, IoUtil.toString(citizenIdentifier));
+ this.waitForGlobalId(identifierDependencyGlobalId);
+
+ final ArtifactReference identifierReference = new ArtifactReference();
+ identifierReference.setVersion("1");
+ identifierReference.setGroupId(groupId);
+ identifierReference.setArtifactId(identifierArtifactId);
+ identifierReference.setName("citizenIdentifier1.json");
+
+ String artifactId = generateArtifactId();
+
+ final Integer globalId = createArtifactWithReferences(groupId, artifactId, ArtifactType.JSON, IoUtil.toString(citizenSchema),
+ List.of(qualificationsReference, cityReference, identifierReference, addressReference));
+ this.waitForGlobalId(globalId);
+
+ City city = new City("New York", 10001);
+ CitizenIdentifier identifier = new CitizenIdentifier(123456789);
+ Citizen citizen = new Citizen("Carles", "Arnal", 23, city, identifier, Collections.emptyList());
+
+ try (JsonSchemaKafkaSerializer serializer = new JsonSchemaKafkaSerializer<>(restClient, true);
+ Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
+
+ Map config = new HashMap<>();
+ config.put(SerdeConfig.EXPLICIT_ARTIFACT_GROUP_ID, groupId);
+ config.put(SerdeConfig.ARTIFACT_RESOLVER_STRATEGY, SimpleTopicIdStrategy.class.getName());
+ serializer.configure(config, false);
+
+ deserializer.configure(Map.of(SchemaResolverConfig.DESERIALIZER_DEREFERENCE_SCHEMA, "true"), false);
+
+ Headers headers = new RecordHeaders();
+ byte[] bytes = serializer.serialize(artifactId, headers, citizen);
+
+ citizen = deserializer.deserialize(artifactId, headers, bytes);
+
+ Assertions.assertEquals("Carles", citizen.getFirstName());
+ Assertions.assertEquals("Arnal", citizen.getLastName());
+ Assertions.assertEquals(23, citizen.getAge());
+ Assertions.assertEquals("New York", citizen.getCity().getName());
+
+ citizen.setAge(-1);
+
+ try {
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
+ Assertions.fail();
+ }
+ catch (Exception ignored) {
}
+ citizen.setAge(23);
+ city = new City("Kansas CIty", -31);
+ citizen.setCity(city);
+
+ try {
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
+ Assertions.fail();
+ }
+ catch (Exception ignored) {
+ }
//invalid identifier present, should fail
identifier = new CitizenIdentifier(-1234356);
@@ -384,7 +532,8 @@ public void testJsonSchemaSerdeWithReferences() throws Exception {
try {
serializer.serialize(artifactId, new RecordHeaders(), citizen);
Assertions.fail();
- } catch (Exception ignored) {
+ }
+ catch (Exception ignored) {
}
//no identifier present, should pass
@@ -392,16 +541,181 @@ public void testJsonSchemaSerdeWithReferences() throws Exception {
serializer.serialize(artifactId, new RecordHeaders(), citizen);
//valid qualification, should pass
- citizen.setQualifications(List.of(new Qualification(UUID.randomUUID().toString(), 6), new Qualification(UUID.randomUUID().toString(), 7), new Qualification(UUID.randomUUID().toString(), 8)));
+ citizen.setQualifications(List.of(new Qualification(UUID.randomUUID().toString(), 6), new Qualification(UUID.randomUUID().toString(), 7),
+ new Qualification(UUID.randomUUID().toString(), 8)));
serializer.serialize(artifactId, new RecordHeaders(), citizen);
//invalid qualification, should fail
- citizen.setQualifications(List.of(new Qualification(UUID.randomUUID().toString(), 6), new Qualification(UUID.randomUUID().toString(), -7), new Qualification(UUID.randomUUID().toString(), 8)));
+ citizen.setQualifications(List.of(new Qualification(UUID.randomUUID().toString(), 6), new Qualification(UUID.randomUUID().toString(), -7),
+ new Qualification(UUID.randomUUID().toString(), 8)));
+ try {
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
+ Assertions.fail();
+ }
+ catch (Exception ignored) {
+ }
+ }
+ }
+
+ @Test
+ public void testWithReferencesDeserializerDereferencedComplexUsecase() throws Exception {
+ InputStream citySchema = getClass().getResourceAsStream("/io/apicurio/registry/util/types/city/city.json");
+ InputStream citizenSchema = getClass().getResourceAsStream("/io/apicurio/registry/util/citizen.json");
+ InputStream citizenIdentifier = getClass().getResourceAsStream("/io/apicurio/registry/util/types/identifier/citizenIdentifier.json");
+ InputStream qualificationSchema = getClass().getResourceAsStream("/io/apicurio/registry/util/qualification.json");
+ InputStream addressSchema = getClass().getResourceAsStream("/io/apicurio/registry/util/sample.address.json");
+
+ InputStream identifierQuarlification = getClass().getResourceAsStream("/io/apicurio/registry/util/types/identifier/qualification.json");
+ InputStream cityQualification = getClass().getResourceAsStream("/io/apicurio/registry/util/types/city/qualification.json");
+
+ Assertions.assertNotNull(citizenSchema);
+ Assertions.assertNotNull(citySchema);
+ Assertions.assertNotNull(citizenIdentifier);
+ Assertions.assertNotNull(qualificationSchema);
+ Assertions.assertNotNull(addressSchema);
+ Assertions.assertNotNull(identifierQuarlification);
+ Assertions.assertNotNull(cityQualification);
+
+ String groupId = TestUtils.generateGroupId();
+ String cityArtifactId = generateArtifactId();
+ String qualificationsId = generateArtifactId();
+ String identifierArtifactId = generateArtifactId();
+ String addressId = generateArtifactId();
+ String identifierQualificationId = generateArtifactId();
+ String cityQualificationId = generateArtifactId();
+
+ //Create the two nested qualification schemas, one for the city, and one for the identifier
+ final Integer identifierQualificationDependencyGlobalId = createArtifact(groupId, identifierQualificationId, ArtifactType.JSON, IoUtil.toString(identifierQuarlification));
+ this.waitForGlobalId(identifierQualificationDependencyGlobalId);
+ final Integer cityQualificationDependencyGlobalId = createArtifact(groupId, cityQualificationId, ArtifactType.JSON, IoUtil.toString(cityQualification));
+ this.waitForGlobalId(cityQualificationDependencyGlobalId);
+
+ final ArtifactReference cityQualificationReference = new ArtifactReference();
+ cityQualificationReference.setVersion("1");
+ cityQualificationReference.setGroupId(groupId);
+ cityQualificationReference.setArtifactId(cityQualificationId);
+ cityQualificationReference.setName("qualification.json");
+
+ //create the city schema with the reference to its qualification
+ final Integer cityDependencyGlobalId = createArtifactWithReferences(groupId, cityArtifactId, ArtifactType.JSON, IoUtil.toString(citySchema),
+ List.of(cityQualificationReference));
+ this.waitForGlobalId(cityDependencyGlobalId);
+
+ final ArtifactReference identifierQualificationReference = new ArtifactReference();
+ identifierQualificationReference.setVersion("1");
+ identifierQualificationReference.setGroupId(groupId);
+ identifierQualificationReference.setArtifactId(identifierQualificationId);
+ identifierQualificationReference.setName("qualification.json");
+
+ //create the identifier schema with the reference to its qualification
+ final Integer identifierDependencyGlobalId = createArtifactWithReferences(groupId, identifierArtifactId, ArtifactType.JSON, IoUtil.toString(citizenIdentifier),
+ List.of(identifierQualificationReference));
+ this.waitForGlobalId(identifierDependencyGlobalId);
+
+ //create the main qualification schema, used for the citizen
+ final Integer qualificationsGlobalId = createArtifact(groupId, qualificationsId, ArtifactType.JSON, IoUtil.toString(qualificationSchema));
+ this.waitForGlobalId(qualificationsGlobalId);
+
+ final ArtifactReference qualificationsReference = new ArtifactReference();
+ qualificationsReference.setVersion("1");
+ qualificationsReference.setGroupId(groupId);
+ qualificationsReference.setArtifactId(qualificationsId);
+ qualificationsReference.setName("qualification.json");
+
+ final Integer addressGlobalID = createArtifact(groupId, addressId, ArtifactType.JSON, IoUtil.toString(addressSchema));
+ this.waitForGlobalId(addressGlobalID);
+
+ final ArtifactReference addressReference = new ArtifactReference();
+ addressReference.setVersion("1");
+ addressReference.setGroupId(groupId);
+ addressReference.setArtifactId(addressId);
+ addressReference.setName("sample.address.json");
+
+ final ArtifactReference cityReference = new ArtifactReference();
+ cityReference.setVersion("1");
+ cityReference.setGroupId(groupId);
+ cityReference.setArtifactId(cityArtifactId);
+ cityReference.setName("types/city/city.json");
+
+ final ArtifactReference identifierReference = new ArtifactReference();
+ identifierReference.setVersion("1");
+ identifierReference.setGroupId(groupId);
+ identifierReference.setArtifactId(identifierArtifactId);
+ identifierReference.setName("types/identifier/citizenIdentifier.json");
+
+ String artifactId = generateArtifactId();
+
+ //create the citizen schema, with references to qualifications, city, identifier and address
+ final Integer globalId = createArtifactWithReferences(groupId, artifactId, ArtifactType.JSON, IoUtil.toString(citizenSchema),
+ List.of(qualificationsReference, cityReference, identifierReference, addressReference));
+ this.waitForGlobalId(globalId);
+
+ City city = new City("New York", 10001);
+ CitizenIdentifier identifier = new CitizenIdentifier(123456789);
+ Citizen citizen = new Citizen("Carles", "Arnal", 23, city, identifier, Collections.emptyList());
+
+ try (JsonSchemaKafkaSerializer serializer = new JsonSchemaKafkaSerializer<>(restClient, true);
+ Deserializer deserializer = new JsonSchemaKafkaDeserializer<>(restClient, true)) {
+
+ Map config = new HashMap<>();
+ config.put(SerdeConfig.EXPLICIT_ARTIFACT_GROUP_ID, groupId);
+ config.put(SerdeConfig.ARTIFACT_RESOLVER_STRATEGY, SimpleTopicIdStrategy.class.getName());
+ config.put(SchemaResolverConfig.SERIALIZER_DEREFERENCE_SCHEMA, "true");
+ serializer.configure(config, false);
+
+ deserializer.configure(Map.of(SchemaResolverConfig.DESERIALIZER_DEREFERENCE_SCHEMA, "true"), false);
+
+ Headers headers = new RecordHeaders();
+ byte[] bytes = serializer.serialize(artifactId, headers, citizen);
+
+ citizen = deserializer.deserialize(artifactId, headers, bytes);
+
+ Assertions.assertEquals("Carles", citizen.getFirstName());
+ Assertions.assertEquals("Arnal", citizen.getLastName());
+ Assertions.assertEquals(23, citizen.getAge());
+ Assertions.assertEquals("New York", citizen.getCity().getName());
+
+ //invalid qualification, should fail
+ citizen.setQualifications(List.of(new Qualification(UUID.randomUUID().toString(), 6), new Qualification(UUID.randomUUID().toString(), -7),
+ new Qualification(UUID.randomUUID().toString(), 8)));
+ try {
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
+ Assertions.fail();
+ }
+ catch (Exception ignored) {
+ }
+
+ //invalid city qualification, minimum is 10 should fail
+ city.setQualification(new CityQualification("city_qualification", 9));
+ citizen.setCity(city);
+ citizen.setQualifications(Collections.emptyList());
try {
serializer.serialize(artifactId, new RecordHeaders(), citizen);
Assertions.fail();
- } catch (Exception ignored) {
}
+ catch (Exception ignored) {
+ }
+
+ //valid city qualification, should pass
+ city.setQualification(new CityQualification("city_qualification", 11));
+ citizen.setCity(city);
+ citizen.setQualifications(Collections.emptyList());
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
+
+ //invalid identifier qualification, minimum is 20, should fail
+ identifier.setIdentifierQualification(new IdentifierQualification("test_subject", 19));
+ citizen.setIdentifier(identifier);
+ try {
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
+ Assertions.fail();
+ }
+ catch (Exception ignored) {
+ }
+
+ //valid identifier qualification
+ identifier.setIdentifierQualification(new IdentifierQualification("test_subject", 20));
+ citizen.setIdentifier(identifier);
+ serializer.serialize(artifactId, new RecordHeaders(), citizen);
}
}
@@ -442,7 +756,6 @@ public void complexObjectValidation() throws Exception {
ContentTypes.APPLICATION_CREATE_EXTENDED, null, null, phone,
null);
-
final ArtifactReference addressReference = new ArtifactReference();
addressReference.setVersion(amdAddress.getVersion());
addressReference.setGroupId(amdAddress.getGroupId());
diff --git a/app/src/test/java/io/apicurio/registry/support/CitizenIdentifier.java b/app/src/test/java/io/apicurio/registry/support/CitizenIdentifier.java
index 5534fbadc7..2de8f59b23 100644
--- a/app/src/test/java/io/apicurio/registry/support/CitizenIdentifier.java
+++ b/app/src/test/java/io/apicurio/registry/support/CitizenIdentifier.java
@@ -23,6 +23,9 @@ public class CitizenIdentifier {
@JsonProperty("identifier")
private Integer identifier;
+ @JsonProperty("qualification")
+ private IdentifierQualification identifierQualification;
+
public CitizenIdentifier() {
}
@@ -30,6 +33,11 @@ public CitizenIdentifier(Integer identifier) {
this.identifier = identifier;
}
+ public CitizenIdentifier(Integer identifier, IdentifierQualification identifierQualification) {
+ this.identifier = identifier;
+ this.identifierQualification = identifierQualification;
+ }
+
public Integer getIdentifier() {
return identifier;
}
@@ -37,4 +45,12 @@ public Integer getIdentifier() {
public void setIdentifier(Integer identifier) {
this.identifier = identifier;
}
+
+ public IdentifierQualification getIdentifierQualification() {
+ return identifierQualification;
+ }
+
+ public void setIdentifierQualification(IdentifierQualification identifierQualification) {
+ this.identifierQualification = identifierQualification;
+ }
}
diff --git a/app/src/test/java/io/apicurio/registry/support/City.java b/app/src/test/java/io/apicurio/registry/support/City.java
index 6d8233856a..f2eb4387bd 100644
--- a/app/src/test/java/io/apicurio/registry/support/City.java
+++ b/app/src/test/java/io/apicurio/registry/support/City.java
@@ -26,6 +26,9 @@ public class City {
@JsonProperty("zipCode")
private Integer zipCode;
+ @JsonProperty("qualification")
+ private CityQualification qualification;
+
public City() {
}
@@ -34,6 +37,12 @@ public City(String name, Integer zipCode) {
this.zipCode = zipCode;
}
+ public City(String name, Integer zipCode, CityQualification cityQualification) {
+ this.name = name;
+ this.zipCode = zipCode;
+ this.qualification = cityQualification;
+ }
+
public String getName() {
return name;
}
@@ -49,4 +58,12 @@ public Integer getZipCode() {
public void setZipCode(Integer zipCode) {
this.zipCode = zipCode;
}
+
+ public CityQualification getQualification() {
+ return qualification;
+ }
+
+ public void setQualification(CityQualification qualification) {
+ this.qualification = qualification;
+ }
}
diff --git a/app/src/test/java/io/apicurio/registry/support/CityQualification.java b/app/src/test/java/io/apicurio/registry/support/CityQualification.java
new file mode 100644
index 0000000000..04aaa65799
--- /dev/null
+++ b/app/src/test/java/io/apicurio/registry/support/CityQualification.java
@@ -0,0 +1,36 @@
+package io.apicurio.registry.support;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class CityQualification {
+
+ @JsonProperty("subject_name")
+ private String subjectName;
+
+ @JsonProperty("qualification")
+ private int qualification;
+
+ public CityQualification() {
+ }
+
+ public CityQualification(String subjectName, int qualification) {
+ this.subjectName = subjectName;
+ this.qualification = qualification;
+ }
+
+ public String getSubjectName() {
+ return subjectName;
+ }
+
+ public void setSubjectName(String subjectName) {
+ this.subjectName = subjectName;
+ }
+
+ public int getQualification() {
+ return qualification;
+ }
+
+ public void setQualification(int qualification) {
+ this.qualification = qualification;
+ }
+}
diff --git a/app/src/test/java/io/apicurio/registry/support/IdentifierQualification.java b/app/src/test/java/io/apicurio/registry/support/IdentifierQualification.java
new file mode 100644
index 0000000000..731c4d4ffb
--- /dev/null
+++ b/app/src/test/java/io/apicurio/registry/support/IdentifierQualification.java
@@ -0,0 +1,36 @@
+package io.apicurio.registry.support;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class IdentifierQualification {
+
+ @JsonProperty("subject_name")
+ private String subjectName;
+
+ @JsonProperty("qualification")
+ private int qualification;
+
+ public IdentifierQualification() {
+ }
+
+ public IdentifierQualification(String subjectName, int qualification) {
+ this.subjectName = subjectName;
+ this.qualification = qualification;
+ }
+
+ public String getSubjectName() {
+ return subjectName;
+ }
+
+ public void setSubjectName(String subjectName) {
+ this.subjectName = subjectName;
+ }
+
+ public int getQualification() {
+ return qualification;
+ }
+
+ public void setQualification(int qualification) {
+ this.qualification = qualification;
+ }
+}
diff --git a/app/src/test/resources/io/apicurio/registry/util/citizen.json b/app/src/test/resources/io/apicurio/registry/util/citizen.json
index 700fb09a83..daa67f08e4 100644
--- a/app/src/test/resources/io/apicurio/registry/util/citizen.json
+++ b/app/src/test/resources/io/apicurio/registry/util/citizen.json
@@ -18,10 +18,10 @@
"minimum": 0
},
"city": {
- "$ref": "city.json"
+ "$ref": "types/city/city.json"
},
"identifier": {
- "$ref": "citizenIdentifier.json"
+ "$ref": "types/identifier/citizenIdentifier.json"
},
"qualifications": {
"type": "array",
diff --git a/app/src/test/resources/io/apicurio/registry/util/citizen1.json b/app/src/test/resources/io/apicurio/registry/util/citizen1.json
new file mode 100644
index 0000000000..abea24149f
--- /dev/null
+++ b/app/src/test/resources/io/apicurio/registry/util/citizen1.json
@@ -0,0 +1,36 @@
+{
+ "$id": "https://example.com/citizen1.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Citizen",
+ "type": "object",
+ "properties": {
+ "firstName": {
+ "type": "string",
+ "description": "The citizen's first name."
+ },
+ "lastName": {
+ "type": "string",
+ "description": "The citizen's last name."
+ },
+ "age": {
+ "description": "Age in years which must be equal to or greater than zero.",
+ "type": "integer",
+ "minimum": 0
+ },
+ "city": {
+ "$ref": "city1.json"
+ },
+ "identifier": {
+ "$ref": "citizenIdentifier1.json"
+ },
+ "qualifications": {
+ "type": "array",
+ "items": {
+ "$ref": "qualification.json"
+ }
+ }
+ },
+ "required": [
+ "city"
+ ]
+}
\ No newline at end of file
diff --git a/app/src/test/resources/io/apicurio/registry/util/citizenIdentifier.json b/app/src/test/resources/io/apicurio/registry/util/citizenIdentifier1.json
similarity index 81%
rename from app/src/test/resources/io/apicurio/registry/util/citizenIdentifier.json
rename to app/src/test/resources/io/apicurio/registry/util/citizenIdentifier1.json
index 3a896e55f0..2b4c20118a 100644
--- a/app/src/test/resources/io/apicurio/registry/util/citizenIdentifier.json
+++ b/app/src/test/resources/io/apicurio/registry/util/citizenIdentifier1.json
@@ -1,5 +1,5 @@
{
- "$id": "https://example.com/citizenIdentifier.json",
+ "$id": "https://example.com/citizenIdentifier1.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Identifier",
"type": "object",
diff --git a/app/src/test/resources/io/apicurio/registry/util/city.json b/app/src/test/resources/io/apicurio/registry/util/city1.json
similarity index 87%
rename from app/src/test/resources/io/apicurio/registry/util/city.json
rename to app/src/test/resources/io/apicurio/registry/util/city1.json
index a86e3e473c..a02d7ef37e 100644
--- a/app/src/test/resources/io/apicurio/registry/util/city.json
+++ b/app/src/test/resources/io/apicurio/registry/util/city1.json
@@ -1,5 +1,5 @@
{
- "$id": "https://example.com/city.json",
+ "$id": "https://example.com/city1.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "City",
"type": "object",
diff --git a/app/src/test/resources/io/apicurio/registry/util/types/city/city.json b/app/src/test/resources/io/apicurio/registry/util/types/city/city.json
new file mode 100644
index 0000000000..66a1105c0a
--- /dev/null
+++ b/app/src/test/resources/io/apicurio/registry/util/types/city/city.json
@@ -0,0 +1,20 @@
+{
+ "$id": "https://example.com/types/city/city.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "City",
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The city's name."
+ },
+ "zipCode": {
+ "type": "integer",
+ "description": "The zip code.",
+ "minimum": 0
+ },
+ "qualification": {
+ "$ref": "qualification.json"
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/resources/io/apicurio/registry/util/types/city/qualification.json b/app/src/test/resources/io/apicurio/registry/util/types/city/qualification.json
new file mode 100644
index 0000000000..4f19d81a31
--- /dev/null
+++ b/app/src/test/resources/io/apicurio/registry/util/types/city/qualification.json
@@ -0,0 +1,17 @@
+{
+ "$id": "https://example.com/types/city/qualification.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Qualification",
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The subject's name"
+ },
+ "qualification": {
+ "type": "integer",
+ "description": "The city qualification",
+ "minimum": 10
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/resources/io/apicurio/registry/util/types/identifier/citizenIdentifier.json b/app/src/test/resources/io/apicurio/registry/util/types/identifier/citizenIdentifier.json
new file mode 100644
index 0000000000..0c4677f84a
--- /dev/null
+++ b/app/src/test/resources/io/apicurio/registry/util/types/identifier/citizenIdentifier.json
@@ -0,0 +1,16 @@
+{
+ "$id": "https://example.com/types/identifier/citizenIdentifier.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Identifier",
+ "type": "object",
+ "properties": {
+ "identifier": {
+ "type": "integer",
+ "description": "The citizen identifier.",
+ "minimum": 0
+ },
+ "qualification": {
+ "$ref": "qualification.json"
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/resources/io/apicurio/registry/util/types/identifier/qualification.json b/app/src/test/resources/io/apicurio/registry/util/types/identifier/qualification.json
new file mode 100644
index 0000000000..931557b9d1
--- /dev/null
+++ b/app/src/test/resources/io/apicurio/registry/util/types/identifier/qualification.json
@@ -0,0 +1,17 @@
+{
+ "$id": "https://example.com/types/identifier/qualification.json",
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Qualification",
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "The subject's name"
+ },
+ "qualification": {
+ "type": "integer",
+ "description": "The identifier qualification",
+ "minimum": 20
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/modules/ROOT/partials/getting-started/proc-managing-artifact-references-using-rest-api.adoc b/docs/modules/ROOT/partials/getting-started/proc-managing-artifact-references-using-rest-api.adoc
index b079c88d34..6f4fd361f2 100644
--- a/docs/modules/ROOT/partials/getting-started/proc-managing-artifact-references-using-rest-api.adoc
+++ b/docs/modules/ROOT/partials/getting-started/proc-managing-artifact-references-using-rest-api.adoc
@@ -150,9 +150,11 @@ $ curl -H "Authorization: Bearer $ACCESS_TOKEN" MY-REGISTRY-URL/apis/registry/v2
There are some cases where returning artifact content with referenced content inline might be helpful. For these cases, the Core Registry API v2 supports the `dereference` query parameter in certain operations.
-This support is currently implemented only for Avro and Protobuf artifacts when the `dereference` parameter is specified in the API operation. This parameter is not supported for any other artifact types.
+This support is currently implemented only for Avro, Protobuf, and JSON Schema artifacts when the `dereference` parameter is specified in the API operation. This parameter is not supported for any other artifact types.
-NOTE: For Protobuf artifacts, dereferencing content is supported only when all of the schemas belong to the same package.
+NOTE: For Protobuf artifacts, dereferencing content is supported only when all the schemas belong to the same package.
+
+NOTE: For JSON Schema artifacts, dereferencing content is supported only for artifacts that reference the full content of a separate artifact. Dereferencing when an artifact references a part of a second artifact is *not* supported
[role="_additional-resources"]
diff --git a/pom.xml b/pom.xml
index 427d63edf6..b6f3891043 100644
--- a/pom.xml
+++ b/pom.xml
@@ -159,6 +159,7 @@
1.11.3
1.4.0
+ 4.5.7
4.9.9
4.9.9
3.9.0
@@ -562,6 +563,11 @@
json-schema-validator
${json-schema-validator.version}