diff --git a/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/OpenAPISchema.java b/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/OpenAPISchema.java index 19c57b67..313094b0 100644 --- a/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/OpenAPISchema.java +++ b/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/OpenAPISchema.java @@ -2,12 +2,13 @@ import com.asyncapi.v3.jackson.schema.openapi.OpenAPISchemaAdditionalPropertiesDeserializer; import com.asyncapi.v3.jackson.schema.openapi.OpenAPISchemaAnyValueDeserializer; +import com.asyncapi.v3.schema.openapi.properties.Discriminator; +import com.asyncapi.v3.schema.openapi.properties.Extensions; +import com.asyncapi.v3.schema.openapi.properties.ExternalDocumentation; +import com.asyncapi.v3.schema.openapi.properties.XML; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.*; import org.jetbrains.annotations.Nullable; import java.math.BigDecimal; @@ -34,7 +35,8 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class OpenAPISchema { +@EqualsAndHashCode(callSuper = true) +public class OpenAPISchema extends Extensions { /** * Schema name. diff --git a/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/Discriminator.java b/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/Discriminator.java similarity index 97% rename from asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/Discriminator.java rename to asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/Discriminator.java index 5ebb1431..ca5ab32c 100644 --- a/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/Discriminator.java +++ b/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/Discriminator.java @@ -1,4 +1,4 @@ -package com.asyncapi.v3.schema.openapi; +package com.asyncapi.v3.schema.openapi.properties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; diff --git a/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/Extensions.java b/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/Extensions.java new file mode 100644 index 00000000..042bd076 --- /dev/null +++ b/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/Extensions.java @@ -0,0 +1,46 @@ +package com.asyncapi.v3.schema.openapi.properties; + +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * @see Specification Extensions + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonIgnoreProperties({"extensions"}) +public class Extensions { + + private static final Pattern extensionPropertyNamePattern = Pattern.compile("^x-.*"); + + /** + * Extension fields in the form x-extension-field-name for the exposed API. + */ + @Nullable + @JsonAnyGetter + protected Map extensions; + + @JsonAnySetter + protected final void readExtensionProperty(String name, Object value) { + if (extensionPropertyNamePattern.matcher(name).matches()) { + if (extensions == null) { + extensions = new HashMap<>(); + } + + extensions.put(name, value); + } else { + throw new IllegalArgumentException(String.format("\"%s\" is not valid extension property", name)); + } + } + +} diff --git a/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/ExternalDocumentation.java b/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/ExternalDocumentation.java similarity index 87% rename from asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/ExternalDocumentation.java rename to asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/ExternalDocumentation.java index 1f58895b..e3cb9e9d 100644 --- a/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/ExternalDocumentation.java +++ b/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/ExternalDocumentation.java @@ -1,11 +1,8 @@ -package com.asyncapi.v3.schema.openapi; +package com.asyncapi.v3.schema.openapi.properties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -22,7 +19,8 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class ExternalDocumentation { +@EqualsAndHashCode(callSuper = true) +public class ExternalDocumentation extends Extensions { /** * A short description of the target documentation. diff --git a/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/XML.java b/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/XML.java similarity index 94% rename from asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/XML.java rename to asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/XML.java index 3cb9d85f..cfd5a0de 100644 --- a/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/XML.java +++ b/asyncapi-core/src/main/java/com/asyncapi/v3/schema/openapi/properties/XML.java @@ -1,11 +1,8 @@ -package com.asyncapi.v3.schema.openapi; +package com.asyncapi.v3.schema.openapi.properties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.*; import org.jetbrains.annotations.Nullable; /** @@ -21,7 +18,8 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class XML { +@EqualsAndHashCode(callSuper = true) +public class XML extends Extensions { /** * Replaces the name of the element/attribute used for the described schema property. diff --git a/asyncapi-core/src/test/kotlin/com/asyncapi/v3/schema/openapi/OpenAPISchemaTest.kt b/asyncapi-core/src/test/kotlin/com/asyncapi/v3/schema/openapi/OpenAPISchemaTest.kt index 618fe5ad..13ccc763 100644 --- a/asyncapi-core/src/test/kotlin/com/asyncapi/v3/schema/openapi/OpenAPISchemaTest.kt +++ b/asyncapi-core/src/test/kotlin/com/asyncapi/v3/schema/openapi/OpenAPISchemaTest.kt @@ -2,8 +2,7 @@ package com.asyncapi.v3.schema.openapi import com.asyncapi.v3.ClasspathUtils import com.asyncapi.v3.schema.SchemaProvider -import com.asyncapi.v3.schema.openapi.properties.ExampleEnumDefaultArrayTest -import com.asyncapi.v3.schema.openapi.properties.ExampleEnumDefaultNullTest +import com.asyncapi.v3.schema.openapi.properties.* import com.fasterxml.jackson.annotation.JsonInclude import com.fasterxml.jackson.databind.ObjectMapper import org.junit.jupiter.api.Assertions @@ -79,10 +78,13 @@ class OpenAPISchemaTest { class XMLs: ArgumentsProvider { override fun provideArguments(context: ExtensionContext?): Stream { + val extendedXML = XML( + "animal", "http://example.com/schema/sample", "sample", true, true + ) + extendedXML.extensions = mapOf(Pair("x-extension-property", "value")) + return Stream.of( - Arguments.of("/json/v3/schema/openapi/xml.json", XML( - "animal", "http://example.com/schema/sample", "sample", true, true - )), + Arguments.of("/json/v3/schema/openapi/xml.json", extendedXML), Arguments.of("/json/v3/schema/openapi/xml-attribute.json", XML.builder().attribute(true).build()), Arguments.of("/json/v3/schema/openapi/xml-name-replacement.json", XML.builder().name("animal").build()), Arguments.of("/json/v3/schema/openapi/xml-prefix-and-namespace.json", XML.builder() @@ -115,10 +117,11 @@ class OpenAPISchemaTest { class ExternalDocumentations: ArgumentsProvider { override fun provideArguments(context: ExtensionContext?): Stream { + val extendedExternalDocumentation = ExternalDocumentation("Find more info here", "https://example.com") + extendedExternalDocumentation.extensions = mapOf(Pair("x-extension-property", "value")) + return Stream.of( - Arguments.of("/json/v3/schema/openapi/externaldocumentation.json", ExternalDocumentation( - "Find more info here", "https://example.com" - )), + Arguments.of("/json/v3/schema/openapi/externaldocumentation.json", extendedExternalDocumentation), Arguments.of("/json/v3/schema/openapi/externaldocumentation-url.json", ExternalDocumentation( null, "https://example.com" )), diff --git a/asyncapi-core/src/test/kotlin/com/asyncapi/v3/schema/openapi/SchemaTest.kt b/asyncapi-core/src/test/kotlin/com/asyncapi/v3/schema/openapi/SchemaTest.kt index e75fa004..c0c9f91f 100644 --- a/asyncapi-core/src/test/kotlin/com/asyncapi/v3/schema/openapi/SchemaTest.kt +++ b/asyncapi-core/src/test/kotlin/com/asyncapi/v3/schema/openapi/SchemaTest.kt @@ -1,12 +1,15 @@ package com.asyncapi.v3.schema.openapi import com.asyncapi.v3.schema.SchemaProvider +import com.asyncapi.v3.schema.openapi.properties.Discriminator +import com.asyncapi.v3.schema.openapi.properties.ExternalDocumentation +import com.asyncapi.v3.schema.openapi.properties.XML import java.math.BigDecimal class SchemaTest: SchemaProvider { override fun openAPISchema(): OpenAPISchema { - return OpenAPISchema.builder() + val schema = OpenAPISchema.builder() .name("schema name") .title("schema title") .multipleOf(BigDecimal(2.5)) @@ -163,6 +166,9 @@ class SchemaTest: SchemaProvider { ) )) .build() + schema.extensions = mapOf(Pair("x-extension-property", "value")) + + return schema } } \ No newline at end of file diff --git a/asyncapi-core/src/test/resources/json/v3/schema/openapi/externaldocumentation.json b/asyncapi-core/src/test/resources/json/v3/schema/openapi/externaldocumentation.json index 19ba32ce..20aa0964 100644 --- a/asyncapi-core/src/test/resources/json/v3/schema/openapi/externaldocumentation.json +++ b/asyncapi-core/src/test/resources/json/v3/schema/openapi/externaldocumentation.json @@ -1,4 +1,5 @@ { "description": "Find more info here", - "url": "https://example.com" + "url": "https://example.com", + "x-extension-property": "value" } \ No newline at end of file diff --git a/asyncapi-core/src/test/resources/json/v3/schema/openapi/schema.json b/asyncapi-core/src/test/resources/json/v3/schema/openapi/schema.json index f5f9add9..0c529e6b 100644 --- a/asyncapi-core/src/test/resources/json/v3/schema/openapi/schema.json +++ b/asyncapi-core/src/test/resources/json/v3/schema/openapi/schema.json @@ -170,5 +170,6 @@ "dog": "#/components/schemas/Dog", "monster": "https://gigantic-server.com/schemas/Monster/schema.json" } - } + }, + "x-extension-property": "value" } \ No newline at end of file diff --git a/asyncapi-core/src/test/resources/json/v3/schema/openapi/xml.json b/asyncapi-core/src/test/resources/json/v3/schema/openapi/xml.json index d8eaf4dc..dfe05fba 100644 --- a/asyncapi-core/src/test/resources/json/v3/schema/openapi/xml.json +++ b/asyncapi-core/src/test/resources/json/v3/schema/openapi/xml.json @@ -3,5 +3,6 @@ "namespace": "http://example.com/schema/sample", "prefix": "sample", "attribute": true, - "wrapped": true + "wrapped": true, + "x-extension-property": "value" } \ No newline at end of file