From 75c5c39c7b13557e8d5e58234c513b7021327fa9 Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Wed, 9 Oct 2024 07:53:16 +0200 Subject: [PATCH] Port json schema objects, asyncapi and openapi dereferencing (#5315) * Add test for json schema object and property level reference * Add object and property level tests for json schema * Add test with multiple references to a single file * Port openapi dereference --- ...ng-artifact-references-using-rest-api.adoc | 7 +- ...ApicurioDataModelsContentDereferencer.java | 12 +- .../content/dereference/ReferenceInliner.java | 107 ++++++++ schema-util/util-provider/pom.xml | 5 + .../JsonSchemaContentDereferencerTest.java | 80 ++++++ .../OpenApiContentDereferencerTest.java | 22 ++ .../registry/content/dereference/address.json | 40 +++ .../content/dereference/all-types.json | 57 +++++ ...ted-testDereference-object-level-json.json | 96 ++++++++ .../expected-testDereference-openapi.json | 231 ++++++++++++++++++ ...d-testDereference-property-level-json.json | 70 ++++++ .../json-schema-to-deref-object-level.json | 36 +++ .../json-schema-to-deref-property-level.json | 36 +++ .../content/dereference/openapi-to-deref.json | 151 ++++++++++++ .../content/dereference/types/all-types.json | 38 +++ .../dereference/types/city/qualification.json | 17 ++ .../types/identifier/qualification.json | 17 ++ 17 files changed, 1013 insertions(+), 9 deletions(-) create mode 100644 schema-util/openapi/src/main/java/io/apicurio/registry/content/dereference/ReferenceInliner.java create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/address.json create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/all-types.json create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-object-level-json.json create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-openapi.json create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-property-level-json.json create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/json-schema-to-deref-object-level.json create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/json-schema-to-deref-property-level.json create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/openapi-to-deref.json create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/types/all-types.json create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/types/city/qualification.json create mode 100644 schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/types/identifier/qualification.json 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 7567b1b50d..0ca57f999b 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 @@ -156,10 +156,13 @@ $ curl -H "Authorization: Bearer $ACCESS_TOKEN" MY-REGISTRY-URL/apis/registry/v3 {"type":"record","name":"Item","namespace":"com.example.common","fields":[{"name":"itemId","type":{"type":"record","name":"ItemId","fields":[{"name":"id","type":"int"}]}}]} ---- -#In Protobuf dereferencing content is only supported when all the schemas in the try belong to the same package.# +This support is currently implemented only for Avro, Protobuf, OpenAPI, AsyncAPI, 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 the schemas belong to the same package. + +NOTE: Circular dependencies are allowed by some artifact types (e.g. JSON Schema) but are not supported by {registry}. [role="_additional-resources"] .Additional resources * For more details, see the {registry-rest-api}. -* For more examples of artifact references, see the section on configuring each artifact type in {registry-client-serdes-config}. +* For more examples of artifact references, see the section on configuring each artifact type in {registry-client-serdes-config}. \ No newline at end of file diff --git a/schema-util/openapi/src/main/java/io/apicurio/registry/content/dereference/ApicurioDataModelsContentDereferencer.java b/schema-util/openapi/src/main/java/io/apicurio/registry/content/dereference/ApicurioDataModelsContentDereferencer.java index 1b5844ab37..60b6008483 100644 --- a/schema-util/openapi/src/main/java/io/apicurio/registry/content/dereference/ApicurioDataModelsContentDereferencer.java +++ b/schema-util/openapi/src/main/java/io/apicurio/registry/content/dereference/ApicurioDataModelsContentDereferencer.java @@ -5,7 +5,6 @@ import io.apicurio.datamodels.Library; import io.apicurio.datamodels.TraverserDirection; import io.apicurio.datamodels.models.Document; -import io.apicurio.datamodels.refs.IReferenceResolver; import io.apicurio.registry.content.ContentHandle; import io.apicurio.registry.content.TypedContent; import io.apicurio.registry.content.util.ContentTypeUtil; @@ -20,12 +19,11 @@ public class ApicurioDataModelsContentDereferencer implements ContentDereference public TypedContent dereference(TypedContent content, Map resolvedReferences) { try { JsonNode node = ContentTypeUtil.parseJsonOrYaml(content); - Document document = Library.readDocument((ObjectNode) node); - IReferenceResolver resolver = new RegistryReferenceResolver(resolvedReferences); - Document dereferencedDoc = Library.dereferenceDocument(document, resolver, false); - String dereferencedContentStr = Library.writeDocumentToJSONString(dereferencedDoc); - return TypedContent.create(ContentHandle.create(dereferencedContentStr), - ContentTypes.APPLICATION_JSON); + Document doc = Library.readDocument((ObjectNode) node); + ReferenceInliner inliner = new ReferenceInliner(resolvedReferences); + Library.visitTree(doc, inliner, TraverserDirection.down); + String dereferencedContent = Library.writeDocumentToJSONString(doc); + return TypedContent.create(ContentHandle.create(dereferencedContent), content.getContentType()); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/schema-util/openapi/src/main/java/io/apicurio/registry/content/dereference/ReferenceInliner.java b/schema-util/openapi/src/main/java/io/apicurio/registry/content/dereference/ReferenceInliner.java new file mode 100644 index 0000000000..00f86c0e6c --- /dev/null +++ b/schema-util/openapi/src/main/java/io/apicurio/registry/content/dereference/ReferenceInliner.java @@ -0,0 +1,107 @@ +/* + * Copyright 2023 Red Hat Inc + * + * 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 io.apicurio.registry.content.dereference; + +import com.fasterxml.jackson.core.JsonPointer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.apicurio.datamodels.Library; +import io.apicurio.datamodels.models.Node; +import io.apicurio.datamodels.models.Referenceable; +import io.apicurio.datamodels.models.visitors.AllNodeVisitor; +import io.apicurio.registry.content.ContentHandle; +import io.apicurio.registry.content.TypedContent; +import io.apicurio.registry.content.refs.JsonPointerExternalReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +/** + * Inlines all references found in the data model. + * + * @author eric.wittmann@gmail.com + */ +public class ReferenceInliner extends AllNodeVisitor { + private static final Logger logger = LoggerFactory.getLogger(ReferenceInliner.class); + private static final ObjectMapper mapper = new ObjectMapper(); + private final Map resolvedReferences; + + /** + * Constructor. + * + * @param resolvedReferences + */ + public ReferenceInliner(Map resolvedReferences) { + this.resolvedReferences = resolvedReferences; + } + + /** + * @see AllNodeVisitor#visitNode(Node) + */ + @Override + protected void visitNode(Node node) { + if (node instanceof Referenceable) { + String $ref = ((Referenceable) node).get$ref(); + if ($ref != null && resolvedReferences.containsKey($ref)) { + inlineRef((Referenceable) node); + } + } + } + + /** + * Inlines the given reference. + * + * @param refNode + */ + private void inlineRef(Referenceable refNode) { + String $ref = refNode.get$ref(); + + JsonPointerExternalReference refPointer = new JsonPointerExternalReference($ref); + ContentHandle refContent = resolvedReferences.get($ref).getContent(); + + // Get the specific node within the content that this $ref points to + ObjectNode refContentNode = getRefNodeFromContent(refContent, refPointer.getComponent()); + if (refContentNode != null) { + // Read that content *into* the current node + Library.readNode(refContentNode, (Node) refNode); + + // Set the $ref to null (now that we've inlined the referenced node) + refNode.set$ref(null); + } + } + + private ObjectNode getRefNodeFromContent(ContentHandle refContent, String refComponent) { + try { + ObjectNode refContentRootNode = (ObjectNode) mapper.readTree(refContent.content()); + if (refComponent != null) { + JsonPointer pointer = JsonPointer.compile(refComponent.substring(1)); + JsonNode nodePointedTo = refContentRootNode.at(pointer); + if (!nodePointedTo.isMissingNode() && nodePointedTo.isObject()) { + return (ObjectNode) nodePointedTo; + } + } else { + return refContentRootNode; + } + } catch (Exception e) { + logger.error("Failed to get referenced node from $ref content.", e); + } + return null; + } + +} diff --git a/schema-util/util-provider/pom.xml b/schema-util/util-provider/pom.xml index 5c1a794e2b..1cf89fecce 100644 --- a/schema-util/util-provider/pom.xml +++ b/schema-util/util-provider/pom.xml @@ -68,6 +68,11 @@ junit-jupiter test + + io.apicurio + apicurio-registry-utils-tests + test + diff --git a/schema-util/util-provider/src/test/java/io/apicurio/registry/content/dereference/JsonSchemaContentDereferencerTest.java b/schema-util/util-provider/src/test/java/io/apicurio/registry/content/dereference/JsonSchemaContentDereferencerTest.java index 02adf439d9..9a0332ca7f 100644 --- a/schema-util/util-provider/src/test/java/io/apicurio/registry/content/dereference/JsonSchemaContentDereferencerTest.java +++ b/schema-util/util-provider/src/test/java/io/apicurio/registry/content/dereference/JsonSchemaContentDereferencerTest.java @@ -6,12 +6,16 @@ import io.apicurio.registry.content.refs.JsonSchemaReferenceFinder; import io.apicurio.registry.content.refs.ReferenceFinder; import io.apicurio.registry.rules.validity.ArtifactUtilProviderTestBase; +import io.apicurio.registry.types.ContentTypes; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; +import static io.apicurio.registry.utils.tests.TestUtils.normalizeMultiLineString; + public class JsonSchemaContentDereferencerTest extends ArtifactUtilProviderTestBase { @Test @@ -30,4 +34,80 @@ public void testRewriteReferences() { .contains(new JsonPointerExternalReference("https://www.example.org/schemas/ssn.json"))); } + @Test + public void testDereferenceObjectLevel() throws Exception { + TypedContent content = TypedContent.create( + resourceToContentHandle("json-schema-to-deref-object-level.json"), + ContentTypes.APPLICATION_JSON); + JsonSchemaDereferencer dereferencer = new JsonSchemaDereferencer(); + // Note: order is important. The JSON schema dereferencer needs to convert the ContentHandle Map + // to a JSONSchema map. So it *must* resolve the leaves of the dependency tree before the branches. + Map resolvedReferences = new LinkedHashMap<>(); + resolvedReferences.put("types/city/qualification.json", TypedContent.create( + resourceToContentHandle("types/city/qualification.json"), ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("city/qualification.json", TypedContent.create( + resourceToContentHandle("types/city/qualification.json"), ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("identifier/qualification.json", + TypedContent.create(resourceToContentHandle("types/identifier/qualification.json"), + ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("types/all-types.json#/definitions/City", TypedContent + .create(resourceToContentHandle("types/all-types.json"), ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("types/all-types.json#/definitions/Identifier", TypedContent + .create(resourceToContentHandle("types/all-types.json"), ContentTypes.APPLICATION_JSON)); + TypedContent modifiedContent = dereferencer.dereference(content, resolvedReferences); + String expectedContent = resourceToString("expected-testDereference-object-level-json.json"); + Assertions.assertEquals(normalizeMultiLineString(expectedContent), + normalizeMultiLineString(modifiedContent.getContent().content())); + } + + @Test + public void testDereferencePropertyLevel() throws Exception { + TypedContent content = TypedContent.create( + resourceToContentHandle("json-schema-to-deref-property-level.json"), + ContentTypes.APPLICATION_JSON); + JsonSchemaDereferencer dereferencer = new JsonSchemaDereferencer(); + // Note: order is important. The JSON schema dereferencer needs to convert the ContentHandle Map + // to a JSONSchema map. So it *must* resolve the leaves of the dependency tree before the branches. + Map resolvedReferences = new LinkedHashMap<>(); + resolvedReferences.put("types/city/qualification.json", TypedContent.create( + resourceToContentHandle("types/city/qualification.json"), ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("city/qualification.json", TypedContent.create( + resourceToContentHandle("types/city/qualification.json"), ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("identifier/qualification.json", + TypedContent.create(resourceToContentHandle("types/identifier/qualification.json"), + ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("types/all-types.json#/definitions/City/properties/name", TypedContent + .create(resourceToContentHandle("types/all-types.json"), ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("types/all-types.json#/definitions/Identifier", TypedContent + .create(resourceToContentHandle("types/all-types.json"), ContentTypes.APPLICATION_JSON)); + TypedContent modifiedContent = dereferencer.dereference(content, resolvedReferences); + String expectedContent = resourceToString("expected-testDereference-property-level-json.json"); + Assertions.assertEquals(normalizeMultiLineString(expectedContent), + normalizeMultiLineString(modifiedContent.getContent().content())); + } + + // Resolves multiple $refs using a single reference to a file with multiple definitions + @Test + public void testReferenceSingleFile() throws Exception { + TypedContent content = TypedContent.create( + resourceToContentHandle("json-schema-to-deref-property-level.json"), + ContentTypes.APPLICATION_JSON); + JsonSchemaDereferencer dereferencer = new JsonSchemaDereferencer(); + // Note: order is important. The JSON schema dereferencer needs to convert the ContentHandle Map + // to a JSONSchema map. So it *must* resolve the leaves of the dependency tree before the branches. + Map resolvedReferences = new LinkedHashMap<>(); + resolvedReferences.put("types/city/qualification.json", TypedContent.create( + resourceToContentHandle("types/city/qualification.json"), ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("city/qualification.json", TypedContent.create( + resourceToContentHandle("types/city/qualification.json"), ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("identifier/qualification.json", + TypedContent.create(resourceToContentHandle("types/identifier/qualification.json"), + ContentTypes.APPLICATION_JSON)); + resolvedReferences.put("types/all-types.json", TypedContent + .create(resourceToContentHandle("types/all-types.json"), ContentTypes.APPLICATION_JSON)); + TypedContent modifiedContent = dereferencer.dereference(content, resolvedReferences); + String expectedContent = resourceToString("expected-testDereference-property-level-json.json"); + Assertions.assertEquals(normalizeMultiLineString(expectedContent), + normalizeMultiLineString(modifiedContent.getContent().content())); + } } diff --git a/schema-util/util-provider/src/test/java/io/apicurio/registry/content/dereference/OpenApiContentDereferencerTest.java b/schema-util/util-provider/src/test/java/io/apicurio/registry/content/dereference/OpenApiContentDereferencerTest.java index b511b10edd..3ecb0ec17c 100644 --- a/schema-util/util-provider/src/test/java/io/apicurio/registry/content/dereference/OpenApiContentDereferencerTest.java +++ b/schema-util/util-provider/src/test/java/io/apicurio/registry/content/dereference/OpenApiContentDereferencerTest.java @@ -1,17 +1,21 @@ package io.apicurio.registry.content.dereference; +import io.apicurio.registry.content.ContentHandle; import io.apicurio.registry.content.TypedContent; import io.apicurio.registry.content.refs.ExternalReference; import io.apicurio.registry.content.refs.JsonPointerExternalReference; import io.apicurio.registry.content.refs.OpenApiReferenceFinder; import io.apicurio.registry.content.refs.ReferenceFinder; import io.apicurio.registry.rules.validity.ArtifactUtilProviderTestBase; +import io.apicurio.registry.types.ContentTypes; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Map; import java.util.Set; +import static io.apicurio.registry.utils.tests.TestUtils.normalizeMultiLineString; + public class OpenApiContentDereferencerTest extends ArtifactUtilProviderTestBase { @Test @@ -32,4 +36,22 @@ public void testRewriteReferences() { "https://www.example.org/schemas/foo-types.json#/components/schemas/Foo"))); } + @Test + public void testDereference() throws Exception { + ContentHandle content = resourceToContentHandle("openapi-to-deref.json"); + OpenApiDereferencer dereferencer = new OpenApiDereferencer(); + Map resolvedReferences = Map.of( + "http://types.example.org/all-types.json#/components/schemas/Foo", + TypedContent.create(resourceToContentHandle("all-types.json"), ContentTypes.APPLICATION_JSON), + "http://types.example.org/all-types.json#/components/schemas/Bar", + TypedContent.create(resourceToContentHandle("all-types.json"), ContentTypes.APPLICATION_JSON), + "http://types.example.org/address.json#/components/schemas/Address", + TypedContent.create(resourceToContentHandle("address.json"), ContentTypes.APPLICATION_JSON)); + TypedContent modifiedContent = dereferencer + .dereference(TypedContent.create(content, ContentTypes.APPLICATION_JSON), resolvedReferences); + String expectedContent = resourceToString("expected-testDereference-openapi.json"); + Assertions.assertEquals(normalizeMultiLineString(expectedContent), + normalizeMultiLineString(modifiedContent.getContent().content())); + } + } diff --git a/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/address.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/address.json new file mode 100644 index 0000000000..60ecb588e7 --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/address.json @@ -0,0 +1,40 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "address.json", + "version": "1.0.0", + "description": "" + }, + "paths": { + "/": {} + }, + "components": { + "schemas": { + "Address": { + "title": "Root Type for Address", + "description": "", + "type": "object", + "properties": { + "address1": { + "type": "string" + }, + "city": { + "type": "string" + }, + "state": { + "type": "string" + }, + "zip": { + "type": "string" + } + }, + "example": { + "address1": "225 West South St", + "city": "Springfield", + "state": "Massiginia", + "zip": "11556" + } + } + } + } +} \ No newline at end of file diff --git a/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/all-types.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/all-types.json new file mode 100644 index 0000000000..7c231e3a19 --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/all-types.json @@ -0,0 +1,57 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "all-types.json", + "version": "1.0.0", + "description": "" + }, + "paths": { + "/": {} + }, + "components": { + "schemas": { + "Foo": { + "description": "", + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "id": { + "description": "", + "type": "string" + }, + "type": { + "description": "", + "type": "string" + }, + "address": { + "$ref": "http://types.example.org/address.json#/components/schemas/Address" + } + } + }, + "Bar": { + "description": "", + "required": [ + "name", + "description" + ], + "type": "object", + "properties": { + "name": { + "description": "", + "type": "string" + }, + "description": { + "description": "", + "type": "string" + }, + "address": { + "$ref": "http://types.example.org/address.json#/components/schemas/Address" + } + } + } + } + } +} \ No newline at end of file diff --git a/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-object-level-json.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-object-level-json.json new file mode 100644 index 0000000000..049eeba137 --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-object-level-json.json @@ -0,0 +1,96 @@ +{ + "$schema" : "http://json-schema.org/draft-07/schema#", + "title" : "Citizen", + "type" : "object", + "properties" : { + "firstName" : { + "description" : "The citizen's first name.", + "type" : "string" + }, + "lastName" : { + "description" : "The citizen's last name.", + "type" : "string" + }, + "identifier" : { + "title" : "Identifier", + "type" : "object", + "properties" : { + "identifier" : { + "description" : "The citizen identifier.", + "type" : "integer", + "minimum" : 0 + }, + "qualification" : { + "title" : "Qualification", + "type" : "object", + "properties" : { + "qualification" : { + "description" : "The identifier qualification", + "type" : "integer", + "minimum" : 20 + }, + "name" : { + "description" : "The subject's name", + "type" : "string" + } + } + } + } + }, + "qualifications" : { + "type" : "array", + "items" : { + "title" : "Qualification", + "type" : "object", + "properties" : { + "qualification" : { + "description" : "The city qualification", + "type" : "integer", + "minimum" : 10 + }, + "name" : { + "description" : "The subject's name", + "type" : "string" + } + } + } + }, + "city" : { + "title" : "City", + "type" : "object", + "properties" : { + "zipCode" : { + "description" : "The zip code.", + "type" : "integer", + "minimum" : 0 + }, + "qualification" : { + "title" : "Qualification", + "type" : "object", + "properties" : { + "qualification" : { + "description" : "The city qualification", + "type" : "integer", + "minimum" : 10 + }, + "name" : { + "description" : "The subject's name", + "type" : "string" + } + } + }, + "name" : { + "description" : "The city's name.", + "type" : "string" + } + } + }, + "age" : { + "description" : "Age in years which must be equal to or greater than zero.", + "type" : "integer", + "minimum" : 0 + } + }, + "required" : [ "city" ], + "$id" : "https://example.com/citizen.json" +} diff --git a/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-openapi.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-openapi.json new file mode 100644 index 0000000000..b567d9f88a --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-openapi.json @@ -0,0 +1,231 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Widget API (with refs)", + "version": "1.0.0" + }, + "paths": { + "/widgets": { + "summary": "Path used to manage the list of widgets.", + "description": "The REST endpoint/path used to list and create zero or more `Widget` entities. This path contains a `GET` and `POST` operation to perform the list and create tasks, respectively.", + "get": { + "summary": "List All Widgets", + "description": "Gets a list of all `Widget` entities.", + "operationId": "getWidgets", + "responses": { + "200": { + "description": "Successful response - returns an array of `Widget` entities.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Widget" + } + } + } + } + } + } + }, + "post": { + "summary": "Create a Widget", + "description": "Creates a new instance of a `Widget`.", + "operationId": "createWidget", + "requestBody": { + "description": "A new `Widget` to be created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Widget" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful response." + } + } + } + }, + "/widgets/{widgetId}": { + "summary": "Path used to manage a single Widget.", + "description": "The REST endpoint/path used to get, update, and delete single instances of an `Widget`. This path contains `GET`, `PUT`, and `DELETE` operations used to perform the get, update, and delete tasks, respectively.", + "get": { + "summary": "Get a Widget", + "description": "Gets the details of a single instance of a `Widget`.", + "operationId": "getWidget", + "responses": { + "200": { + "description": "Successful response - returns a single `Widget`.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Widget" + } + } + } + } + } + }, + "put": { + "summary": "Update a Widget", + "description": "Updates an existing `Widget`.", + "operationId": "updateWidget", + "requestBody": { + "description": "Updated `Widget` information.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Widget" + } + } + }, + "required": true + }, + "responses": { + "202": { + "description": "Successful response." + } + } + }, + "delete": { + "summary": "Delete a Widget", + "description": "Deletes an existing `Widget`.", + "operationId": "deleteWidget", + "responses": { + "204": { + "description": "Successful response." + } + } + }, + "parameters": [ + { + "name": "widgetId", + "in": "path", + "description": "A unique identifier for a `Widget`.", + "required": true, + "schema": { + "type": "string" + } + } + ] + } + }, + "components": { + "schemas": { + "Widget": { + "title": "Root Type for Widget", + "description": "", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "foo": { + "$ref": "#/components/schemas/Foo" + } + }, + "example": { + "name": "...", + "description": "...", + "foo": { + "prop-1": "", + "prop-2": "" + } + } + }, + "Foo": { + "description": "", + "required": [ + "id", + "type" + ], + "type": "object", + "properties": { + "id": { + "description": "", + "type": "string" + }, + "type": { + "description": "", + "type": "string" + }, + "address": { + "title": "Root Type for Address", + "description": "", + "type": "object", + "properties": { + "address1": { + "type": "string" + }, + "city": { + "type": "string" + }, + "state": { + "type": "string" + }, + "zip": { + "type": "string" + } + }, + "example": { + "address1": "225 West South St", + "city": "Springfield", + "state": "Massiginia", + "zip": "11556" + } + } + } + }, + "Bar": { + "description": "", + "required": [ + "name", + "description" + ], + "type": "object", + "properties": { + "name": { + "description": "", + "type": "string" + }, + "description": { + "description": "", + "type": "string" + }, + "address": { + "title": "Root Type for Address", + "description": "", + "type": "object", + "properties": { + "address1": { + "type": "string" + }, + "city": { + "type": "string" + }, + "state": { + "type": "string" + }, + "zip": { + "type": "string" + } + }, + "example": { + "address1": "225 West South St", + "city": "Springfield", + "state": "Massiginia", + "zip": "11556" + } + } + } + } + } + } +} diff --git a/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-property-level-json.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-property-level-json.json new file mode 100644 index 0000000000..3c377c985d --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/expected-testDereference-property-level-json.json @@ -0,0 +1,70 @@ +{ + "$schema" : "http://json-schema.org/draft-07/schema#", + "title" : "Citizen", + "type" : "object", + "properties" : { + "firstName" : { + "description" : "The citizen's first name.", + "type" : "string" + }, + "lastName" : { + "description" : "The citizen's last name.", + "type" : "string" + }, + "identifier" : { + "title" : "Identifier", + "type" : "object", + "properties" : { + "identifier" : { + "description" : "The citizen identifier.", + "type" : "integer", + "minimum" : 0 + }, + "qualification" : { + "title" : "Qualification", + "type" : "object", + "properties" : { + "qualification" : { + "description" : "The identifier qualification", + "type" : "integer", + "minimum" : 20 + }, + "name" : { + "description" : "The subject's name", + "type" : "string" + } + } + } + } + }, + "qualifications" : { + "type" : "array", + "items" : { + "title" : "Qualification", + "type" : "object", + "properties" : { + "qualification" : { + "description" : "The city qualification", + "type" : "integer", + "minimum" : 10 + }, + "name" : { + "description" : "The subject's name", + "type" : "string" + } + } + } + }, + "city" : { + "description" : "The city's name.", + "type" : "string" + }, + "age" : { + "description" : "Age in years which must be equal to or greater than zero.", + "type" : "integer", + "minimum" : 0 + } + }, + "required" : [ "city" ], + "$id" : "https://example.com/citizen.json" +} diff --git a/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/json-schema-to-deref-object-level.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/json-schema-to-deref-object-level.json new file mode 100644 index 0000000000..c50038e107 --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/json-schema-to-deref-object-level.json @@ -0,0 +1,36 @@ +{ + "$id": "https://example.com/citizen.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": "types/all-types.json#/definitions/City" + }, + "identifier": { + "$ref": "types/all-types.json#/definitions/Identifier" + }, + "qualifications": { + "type": "array", + "items": { + "$ref": "types/city/qualification.json" + } + } + }, + "required": [ + "city" + ] +} \ No newline at end of file diff --git a/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/json-schema-to-deref-property-level.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/json-schema-to-deref-property-level.json new file mode 100644 index 0000000000..237ca3e140 --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/json-schema-to-deref-property-level.json @@ -0,0 +1,36 @@ +{ + "$id": "https://example.com/citizen.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": "types/all-types.json#/definitions/City/properties/name" + }, + "identifier": { + "$ref": "types/all-types.json#/definitions/Identifier" + }, + "qualifications": { + "type": "array", + "items": { + "$ref": "types/city/qualification.json" + } + } + }, + "required": [ + "city" + ] +} \ No newline at end of file diff --git a/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/openapi-to-deref.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/openapi-to-deref.json new file mode 100644 index 0000000000..595cb244ff --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/openapi-to-deref.json @@ -0,0 +1,151 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Widget API (with refs)", + "version": "1.0.0" + }, + "paths": { + "/widgets": { + "summary": "Path used to manage the list of widgets.", + "description": "The REST endpoint/path used to list and create zero or more `Widget` entities. This path contains a `GET` and `POST` operation to perform the list and create tasks, respectively.", + "get": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Widget" + } + } + } + }, + "description": "Successful response - returns an array of `Widget` entities." + } + }, + "operationId": "getWidgets", + "summary": "List All Widgets", + "description": "Gets a list of all `Widget` entities." + }, + "post": { + "requestBody": { + "description": "A new `Widget` to be created.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Widget" + } + } + }, + "required": true + }, + "responses": { + "201": { + "description": "Successful response." + } + }, + "operationId": "createWidget", + "summary": "Create a Widget", + "description": "Creates a new instance of a `Widget`." + } + }, + "/widgets/{widgetId}": { + "summary": "Path used to manage a single Widget.", + "description": "The REST endpoint/path used to get, update, and delete single instances of an `Widget`. This path contains `GET`, `PUT`, and `DELETE` operations used to perform the get, update, and delete tasks, respectively.", + "get": { + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Widget" + } + } + }, + "description": "Successful response - returns a single `Widget`." + } + }, + "operationId": "getWidget", + "summary": "Get a Widget", + "description": "Gets the details of a single instance of a `Widget`." + }, + "put": { + "requestBody": { + "description": "Updated `Widget` information.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Widget" + } + } + }, + "required": true + }, + "responses": { + "202": { + "description": "Successful response." + } + }, + "operationId": "updateWidget", + "summary": "Update a Widget", + "description": "Updates an existing `Widget`." + }, + "delete": { + "responses": { + "204": { + "description": "Successful response." + } + }, + "operationId": "deleteWidget", + "summary": "Delete a Widget", + "description": "Deletes an existing `Widget`." + }, + "parameters": [ + { + "name": "widgetId", + "description": "A unique identifier for a `Widget`.", + "schema": { + "type": "string" + }, + "in": "path", + "required": true + } + ] + } + }, + "components": { + "schemas": { + "Widget": { + "title": "Root Type for Widget", + "description": "", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "foo": { + "$ref": "#/components/schemas/Foo" + } + }, + "example": { + "name": "...", + "description": "...", + "foo": { + "prop-1": "", + "prop-2": "" + } + } + }, + "Foo": { + "$ref": "http://types.example.org/all-types.json#/components/schemas/Foo" + }, + "Bar": { + "$ref": "http://types.example.org/all-types.json#/components/schemas/Bar" + } + } + } +} \ No newline at end of file diff --git a/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/types/all-types.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/types/all-types.json new file mode 100644 index 0000000000..acfdaf5a90 --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/types/all-types.json @@ -0,0 +1,38 @@ +{ + "$id": "https://example.com/types/all-types.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "City": { + "title": "City", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The city's name." + }, + "zipCode": { + "type": "integer", + "description": "The zip code.", + "minimum": 0 + }, + "qualification": { + "$ref": "city/qualification.json" + } + } + }, + "Identifier": { + "title": "Identifier", + "type": "object", + "properties": { + "identifier": { + "type": "integer", + "description": "The citizen identifier.", + "minimum": 0 + }, + "qualification": { + "$ref": "identifier/qualification.json" + } + } + } + } +} \ No newline at end of file diff --git a/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/types/city/qualification.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/types/city/qualification.json new file mode 100644 index 0000000000..4f19d81a31 --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/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/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/types/identifier/qualification.json b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/types/identifier/qualification.json new file mode 100644 index 0000000000..931557b9d1 --- /dev/null +++ b/schema-util/util-provider/src/test/resources/io/apicurio/registry/content/dereference/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