From a92b4e5f8b60e4d02d6802d468b79fe1f8380b5c Mon Sep 17 00:00:00 2001 From: tvahrst Date: Sat, 1 Nov 2025 12:38:19 +0100 Subject: [PATCH 01/14] feat: schemaformat option for payload schemas added --- .../json_schema/JsonSchemaGeneratorTest.java | 5 +- .../v3/model/schema/SchemaFormat.java | 1 + .../components/ComponentsService.java | 5 +- .../components/DefaultComponentsService.java | 28 ++- .../common/headers/HeaderClassExtractor.java | 3 +- .../AsyncAnnotationMessageService.java | 2 +- .../SpringAnnotationMessageService.java | 2 +- .../SpringAnnotationMessagesService.java | 2 +- .../payload/internal/PayloadService.java | 10 +- .../schemas/SwaggerSchemaService.java | 61 ++++-- .../asyncapi/schemas/SwaggerSchemaUtil.java | 149 +++++++------ .../properties/PayloadSchemaFormat.java | 20 ++ .../SpringwolfConfigProperties.java | 2 + .../components/SwaggerSchemaUtilTest.java | 196 ++++++++++-------- .../headers/HeaderClassExtractorTest.java | 6 +- .../AsyncAnnotationMessageServiceTest.java | 2 +- .../PayloadAsyncOperationServiceTest.java | 2 +- .../schemas/SwaggerSchemaServiceTest.java | 18 +- .../CloudStreamFunctionChannelsScanner.java | 2 +- .../CloudStreamFunctionOperationsScanner.java | 2 +- ...pringwolfJmsControllerIntegrationTest.java | 2 +- ...ingwolfKafkaControllerIntegrationTest.java | 2 +- 22 files changed, 322 insertions(+), 200 deletions(-) create mode 100644 springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/PayloadSchemaFormat.java diff --git a/springwolf-add-ons/springwolf-json-schema/src/test/java/io/github/springwolf/addons/json_schema/JsonSchemaGeneratorTest.java b/springwolf-add-ons/springwolf-json-schema/src/test/java/io/github/springwolf/addons/json_schema/JsonSchemaGeneratorTest.java index ffec10f17..bdd707b5b 100644 --- a/springwolf-add-ons/springwolf-json-schema/src/test/java/io/github/springwolf/addons/json_schema/JsonSchemaGeneratorTest.java +++ b/springwolf-add-ons/springwolf-json-schema/src/test/java/io/github/springwolf/addons/json_schema/JsonSchemaGeneratorTest.java @@ -7,6 +7,7 @@ import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersionDetector; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; import io.github.springwolf.asyncapi.v3.model.schema.SchemaType; @@ -42,7 +43,7 @@ class JsonSchemaGeneratorTest { @MethodSource void validateJsonSchemaTest(String expectedJsonSchema, Supplier> asyncApiSchema) throws Exception { // given - SchemaObject actualSchema = swaggerSchemaUtil.mapSchema(asyncApiSchema.get()); + ComponentSchema actualSchema = swaggerSchemaUtil.mapSchema(asyncApiSchema.get(), SchemaFormat.ASYNCAPI_V3); // when verifyValidJsonSchema(expectedJsonSchema); @@ -67,7 +68,7 @@ void validateJsonSchemaTest(String expectedJsonSchema, Supplier> async ComponentSchema.of(pongSchema)); // when - Object jsonSchema = jsonSchemaGenerator.fromSchema(ComponentSchema.of(actualSchema), definitions); + Object jsonSchema = jsonSchemaGenerator.fromSchema(actualSchema, definitions); // then String jsonSchemaString = mapper.writeValueAsString(jsonSchema); diff --git a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaFormat.java b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaFormat.java index e15d95255..aef76b112 100644 --- a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaFormat.java +++ b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaFormat.java @@ -12,6 +12,7 @@ public enum SchemaFormat { ASYNCAPI_V3_JSON("application/vnd.aai.asyncapi+json;version=" + AsyncAPI.ASYNCAPI_DEFAULT_VERSION), ASYNCAPI_V3_YAML("application/vnd.aai.asyncapi+yaml;version=" + AsyncAPI.ASYNCAPI_DEFAULT_VERSION), OPENAPI_V3("application/vnd.oai.openapi;version=3.0.0"), + OPENAPI_V3_1("application/vnd.oai.openapi;version=3.1.0"), OPENAPI_V3_JSON("application/vnd.oai.openapi+json;version=3.0.0"), OPENAPI_V3_YAML("application/vnd.oai.openapi+yaml;version=3.0.0"), JSON_SCHEMA_JSON("application/schema+json;version=draft-07"), diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java index 9e50b4a0f..e7b003d49 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java @@ -6,6 +6,7 @@ import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import jakarta.annotation.Nullable; @@ -33,7 +34,7 @@ public interface ComponentsService { * * @param type Type to resolve a schema from * @param contentType Runtime ContentType of Schema - * @return the root schema for the given type. + * @return a {@link SchemaReference} referencing the root schema, or null if no schema could be resolved. */ @Nullable ComponentSchema resolvePayloadSchema(Type type, String contentType); @@ -43,7 +44,7 @@ public interface ComponentsService { * @param headers the schema to register, typically a header schema * @return the title attribute of the given schema */ - String registerSchema(SchemaObject headers); + String registerSimpleSchema(SchemaObject headers); /** * Provides a map of all registered messages. diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java index c5eeba1d4..f327d66c8 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java @@ -5,10 +5,11 @@ import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; +import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; -import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Type; @@ -22,11 +23,10 @@ * in the resulting AsyncApi object. */ @Slf4j -@AllArgsConstructor public class DefaultComponentsService implements ComponentsService { private final SwaggerSchemaService schemaService; - private final SpringwolfConfigProperties springwolfConfigProperties; + private final SchemaFormat payloadSchemaFormat; /** * maps a schema name (key) to a detected corresponding {@link ComponentSchema}. @@ -35,6 +35,15 @@ public class DefaultComponentsService implements ComponentsService { private final Map messages = new HashMap<>(); + public DefaultComponentsService( + SwaggerSchemaService schemaService, SpringwolfConfigProperties springwolfConfigProperties) { + this.schemaService = schemaService; + + PayloadSchemaFormat payloadSchemaFormat = + springwolfConfigProperties.getDocket().getPayloadSchemaFormat(); + this.payloadSchemaFormat = payloadSchemaFormat.getSchemaFormat(); + } + /** * Provides a map of all registered schemas. * @@ -51,12 +60,13 @@ public Map getSchemas() { * * @param type Type to resolve a schema from * @param contentType Runtime ContentType of Schema - * @return the root schema for the given type. + * @return the root schema for the given type */ @Override public ComponentSchema resolvePayloadSchema(Type type, String contentType) { - SwaggerSchemaService.ExtractedSchemas payload = schemaService.resolveSchema(type, contentType); + SwaggerSchemaService.ExtractedSchemas payload = + schemaService.resolveSchema(type, contentType, payloadSchemaFormat); payload.referencedSchemas().forEach(schemas::putIfAbsent); return payload.rootSchema(); } @@ -67,17 +77,17 @@ public ComponentSchema resolvePayloadSchema(Type type, String contentType) { * Use only with schemas with max. one level of properties. Providing {@link SchemaObject}s with deep * property hierarchy will result in an corrupted result. *
- * A typical usecase for this method is registering of header schemas, which have typically a simple structure. + * A typical usecase for this method is registering of header schemas, which have typically a simple structure. * * @param headers the schema to register, typically a header schema * @return the title attribute of the given schema */ @Override - public String registerSchema(SchemaObject headers) { + public String registerSimpleSchema(SchemaObject headers) { log.debug("Registering schema for {}", headers.getTitle()); - SchemaObject headerSchema = schemaService.extractSchema(headers); - this.schemas.putIfAbsent(headers.getTitle(), ComponentSchema.of(headerSchema)); + ComponentSchema processedSchema = schemaService.postProcessSimpleSchema(headers); + this.schemas.putIfAbsent(headers.getTitle(), processedSchema); return headers.getTitle(); } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractor.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractor.java index 0708f7d7d..985527638 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractor.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractor.java @@ -34,7 +34,8 @@ public SchemaObject extractHeader(Method method, PayloadSchemaObject payload) { Header headerAnnotation = argument.getAnnotation(Header.class); String headerName = getHeaderAnnotationName(headerAnnotation); - SwaggerSchemaService.ExtractedSchemas extractedSchema = schemaService.extractSchema(argument.getType()); + SwaggerSchemaService.ExtractedSchemas extractedSchema = + schemaService.postProcessSimpleSchema(argument.getType()); ComponentSchema rootComponentSchema = extractedSchema.rootSchema(); // to stay compatible with former versions. diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java index 845f8471b..f832a4073 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java @@ -37,7 +37,7 @@ public MessageObject buildMessage(AsyncOperation operationData, Method method) { PayloadSchemaObject payloadSchema = payloadAsyncOperationService.extractSchema(operationData, method); SchemaObject headerSchema = AsyncAnnotationUtil.getAsyncHeaders(operationData, stringValueResolver); - String headerSchemaName = this.componentsService.registerSchema(headerSchema); + String headerSchemaName = this.componentsService.registerSimpleSchema(headerSchema); Map messageBinding = AsyncAnnotationUtil.processMessageBindingFromAnnotation(method, messageBindingProcessors); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessageService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessageService.java index f06fc8e3d..1db1f9f78 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessageService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessageService.java @@ -31,7 +31,7 @@ public MessageObject buildMessage( MethodAnnotation annotation, PayloadSchemaObject payloadSchema, SchemaObject headers) { SchemaObject headerSchema = asyncHeadersBuilder.buildHeaders(payloadSchema); SchemaObject mergedHeaderSchema = HeaderSchemaObjectMerger.merge(headerSchema, headers); - String headerModelName = componentsService.registerSchema(mergedHeaderSchema); + String headerModelName = componentsService.registerSimpleSchema(mergedHeaderSchema); Map messageBinding = bindingFactory.buildMessageBinding(annotation, mergedHeaderSchema); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessagesService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessagesService.java index 6bc56308a..f386a36a5 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessagesService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessagesService.java @@ -62,7 +62,7 @@ private MessageObject buildMessage(ClassAnnotation classAnnotation, Method metho SchemaObject headerSchema = asyncHeadersBuilder.buildHeaders(payloadSchema); SchemaObject headers = headerClassExtractor.extractHeader(method, payloadSchema); SchemaObject mergedHeaderSchema = HeaderSchemaObjectMerger.merge(headerSchema, headers); - String headerSchemaName = componentsService.registerSchema(mergedHeaderSchema); + String headerSchemaName = componentsService.registerSimpleSchema(mergedHeaderSchema); Map messageBinding = bindingFactory.buildMessageBinding(classAnnotation, headerSchema); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/internal/PayloadService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/internal/PayloadService.java index 14ce82f4c..29742415d 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/internal/PayloadService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/internal/PayloadService.java @@ -36,6 +36,14 @@ public PayloadSchemaObject buildSchema(Type payloadType) { return buildSchema(contentType, payloadType); } + /** + * creates a {@link PayloadSchemaObject} from the given type and content type. Registers the created schema objects + * with this {@link ComponentsService}. + * + * @param contentType + * @param payloadType + * @return + */ public PayloadSchemaObject buildSchema(String contentType, Type payloadType) { String schemaName = componentsService.getSchemaName(payloadType); String simpleSchemaName = componentsService.getSimpleSchemaName(payloadType); @@ -47,7 +55,7 @@ public PayloadSchemaObject buildSchema(String contentType, Type payloadType) { public PayloadSchemaObject useUnusedPayload() { ComponentSchema schema = PAYLOAD_NOT_USED.schema(); if (schema != null && schema.getSchema() != null) { - this.componentsService.registerSchema(schema.getSchema()); + this.componentsService.registerSimpleSchema(schema.getSchema()); } return PAYLOAD_NOT_USED; } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java index 8ef9a69d6..fe03f952d 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JavaType; import io.github.springwolf.asyncapi.v3.model.ReferenceUtil; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.core.asyncapi.annotations.AsyncApiPayload; import io.github.springwolf.core.asyncapi.components.postprocessors.SchemasPostProcessor; @@ -35,7 +36,9 @@ @Slf4j public class SwaggerSchemaService { - private final ModelConverters converter = ModelConverters.getInstance(); + private final ModelConverters converter_openapi30 = ModelConverters.getInstance(false); + private final ModelConverters converter_openapi31 = ModelConverters.getInstance(true); + private final List schemaPostProcessors; private final SwaggerSchemaUtil swaggerSchemaUtil; private final SpringwolfConfigProperties properties; @@ -46,7 +49,8 @@ public SwaggerSchemaService( SwaggerSchemaUtil swaggerSchemaUtil, SpringwolfConfigProperties properties) { - externalModelConverters.forEach(converter::addConverter); + externalModelConverters.forEach(converter_openapi30::addConverter); + externalModelConverters.forEach(converter_openapi31::addConverter); this.schemaPostProcessors = schemaPostProcessors; this.swaggerSchemaUtil = swaggerSchemaUtil; this.properties = properties; @@ -68,7 +72,7 @@ public record ExtractedSchemas(ComponentSchema rootSchema, Map(newSchemasToProcess), DEFAULT_CONTENT_TYPE); // convert Swagger schema back to an AsnycApi SchemaObject - return swaggerSchemaUtil.mapSchema(headerSchema); + return swaggerSchemaUtil.mapSchema(headerSchema, SchemaFormat.ASYNCAPI_V3); } - public ExtractedSchemas extractSchema(Class type) { - return this.resolveSchema(type, ""); + public ExtractedSchemas postProcessSimpleSchema(Class type) { + return this.resolveSchema(type, "", SchemaFormat.ASYNCAPI_V3); } - public ExtractedSchemas resolveSchema(Type type, String contentType) { + /** + * creates a {@link ExtractedSchemas} from the given type. The resulting ExtractedSchemas will contain the root + * schema (which is always a schema ref) and a List of conrecte schemas referenced from the root schema. + * + * @param type + * @param contentType + * @param schemaFormat SchemaFormat to use + * @return + */ + public ExtractedSchemas resolveSchema(Type type, String contentType, SchemaFormat schemaFormat) { String actualContentType = StringUtils.isBlank(contentType) ? properties.getDocket().getDefaultContentType() : contentType; + + // use swagger to resolve type to a swagger ResolvedSchema Object. + ModelConverters converterToUse = + switch (schemaFormat) { + case ASYNCAPI_V3 -> converter_openapi30; + case OPENAPI_V3 -> converter_openapi30; + case OPENAPI_V3_1 -> converter_openapi31; + default -> throw new IllegalArgumentException("SchemaFormat not supported: " + schemaFormat); + }; + ResolvedSchema resolvedSchema = runWithFqnSetting( - (unused) -> converter.resolveAsResolvedSchema(new AnnotatedType(type).resolveAsRef(true))); + (unused) -> converterToUse.resolveAsResolvedSchema(new AnnotatedType(type).resolveAsRef(true))); if (resolvedSchema == null) { // defaulting to stringSchema when resolvedSchema is null - SchemaObject payloadSchema = swaggerSchemaUtil.mapSchema( - PrimitiveType.fromType(String.class).createProperty()); - return new ExtractedSchemas(ComponentSchema.of(payloadSchema), Map.of()); + Schema stringPropertySchema = + PrimitiveType.fromType(String.class).createProperty(); + ComponentSchema stringComponentSchema = swaggerSchemaUtil.mapSchema(stringPropertySchema, schemaFormat); + return new ExtractedSchemas(stringComponentSchema, Map.of()); } else { Map newSchemasToProcess = new LinkedHashMap<>(resolvedSchema.referencedSchemas); newSchemasToProcess.putIfAbsent(getNameFromType(type), resolvedSchema.schema); @@ -117,7 +141,7 @@ public ExtractedSchemas resolveSchema(Type type, String contentType) { HashMap processedSchemas = new HashMap<>(newSchemasToProcess); postProcessSchemas(newSchemasToProcess, processedSchemas, actualContentType); - return createExtractedSchemas(resolvedSchema.schema, processedSchemas); + return createExtractedSchemas(resolvedSchema.schema, processedSchemas, schemaFormat); } } @@ -126,15 +150,14 @@ public ExtractedSchemas resolveSchema(Type type, String contentType) { * * @param rootSchema Swagger root schema * @param referencedSchemas all referenced swagger schemas + * @param schemaFormat the schema-format of the schemas inside the resulting ExtractedSchemas * @return */ - private ExtractedSchemas createExtractedSchemas(Schema rootSchema, Map referencedSchemas) { - ComponentSchema rootComponentSchema = swaggerSchemaUtil.mapSchemaOrRef(rootSchema); - Map referencedSchemaObjects = swaggerSchemaUtil.mapSchemasMap(referencedSchemas); - Map referencedComponentSchemas = new HashMap<>(); - referencedSchemaObjects.forEach((schemaname, schemaobject) -> { - referencedComponentSchemas.put(schemaname, ComponentSchema.of(schemaobject)); - }); + private ExtractedSchemas createExtractedSchemas( + Schema rootSchema, Map referencedSchemas, SchemaFormat schemaFormat) { + ComponentSchema rootComponentSchema = swaggerSchemaUtil.mapSchemaOrRef(rootSchema, schemaFormat); + Map referencedComponentSchemas = + swaggerSchemaUtil.mapSchemasMap(referencedSchemas, schemaFormat); return new ExtractedSchemas(rootComponentSchema, referencedComponentSchemas); } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java index fb17b35ea..3c1b2b5e9 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java @@ -25,9 +25,9 @@ @RequiredArgsConstructor public class SwaggerSchemaUtil { - public Map mapSchemasMap(Map schemaMap) { + public Map mapSchemasMap(Map schemaMap, SchemaFormat schemaFormat) { return schemaMap.entrySet().stream() - .map(entry -> Map.entry(entry.getKey(), mapSchema(entry.getValue()))) + .map(entry -> Map.entry(entry.getKey(), mapSchema(entry.getValue(), schemaFormat))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } @@ -37,28 +37,49 @@ public Map mapSchemasMap(Map schemaMap) { * referenced location. Otherwise, the given Swagger schema is converted to an AsnycApi {@link SchemaObject} and * put into the resulting {@link ComponentSchema} * - * @param schema the Swagger schema to convert + * @param swaggerSchema the Swagger schema to convert + * @param schemaFormat the schema format of the resulting schema * @return ComponentSchema with either a {@link SchemaReference} or a {@link SchemaObject}. */ - public ComponentSchema mapSchemaOrRef(Schema schema) { - if (schema.get$ref() != null) { - return ComponentSchema.of(new SchemaReference(schema.get$ref())); + public ComponentSchema mapSchemaOrRef(Schema swaggerSchema, SchemaFormat schemaFormat) { + if (swaggerSchema.get$ref() != null) { + return ComponentSchema.of(new SchemaReference(swaggerSchema.get$ref())); } - return ComponentSchema.of(mapSchema(schema)); + return mapSchema(swaggerSchema, schemaFormat); } /** - * Converts the given Swagger schema to a AsnycApi {@link SchemaObject}. Properties are mapped recursively except - * as long as the child schemas are 'real' schems and not schema references. So this method performs a deep conversion + * Converts the given Swagger schema to a {ComponentSchema} with a given schemaFormat. + * If schemaFormat is an openapi format, the given swaggerSchema is simply wrapped to an {@link ComponentSchema}. + *

+ * Properties are mapped recursively except + * as long as the child schemas are 'real' schemas and not schema references. So this method performs a deep conversion * of the entire Swagger schema. * - * @param value the given Swagger schema instance + * @param swaggerSchema the given Swagger schema instance + * @param * @return the resulting AsnycApi SchemaObject */ - public SchemaObject mapSchema(Schema value) { + public ComponentSchema mapSchema(Schema swaggerSchema, SchemaFormat schemaFormat) { + return switch (schemaFormat) { + case OPENAPI_V3, OPENAPI_V3_1 -> + ComponentSchema.of(new MultiFormatSchema(schemaFormat.value, swaggerSchema)); + case ASYNCAPI_V3 -> ComponentSchema.of(mapSwaggerSchemaToAsyncApiSchema(swaggerSchema)); + default -> throw new IllegalArgumentException("SchemaFormat " + schemaFormat + " is not supported"); + }; + } + + /** + * transforms the given Swagger schema to an AsyncApi schema. + * + * @param swaggerSchema + * @return + */ + private SchemaObject mapSwaggerSchemaToAsyncApiSchema(Schema swaggerSchema) { + SchemaObject.SchemaObjectBuilder builder = SchemaObject.builder(); - io.swagger.v3.oas.models.ExternalDocumentation externalDocs = value.getExternalDocs(); + io.swagger.v3.oas.models.ExternalDocumentation externalDocs = swaggerSchema.getExternalDocs(); if (externalDocs != null) { ExternalDocumentation externalDocumentation = ExternalDocumentation.builder() .description(externalDocs.getDescription()) @@ -67,48 +88,48 @@ public SchemaObject mapSchema(Schema value) { builder.externalDocs(externalDocumentation); } - builder.deprecated(value.getDeprecated()); + builder.deprecated(swaggerSchema.getDeprecated()); - builder.title(value.getTitle()); + builder.title(swaggerSchema.getTitle()); - boolean isNullable = Boolean.TRUE.equals(value.getNullable()); - assignType(value, builder, isNullable); + boolean isNullable = Boolean.TRUE.equals(swaggerSchema.getNullable()); + assignType(swaggerSchema, builder, isNullable); - Map properties = value.getProperties(); + Map properties = swaggerSchema.getProperties(); if (properties != null) { Map propertiesMapped = properties.entrySet().stream() - .map(entry -> Map.entry(entry.getKey(), mapSchemaOrRef(entry.getValue()))) + .map(entry -> Map.entry(entry.getKey(), mapSchemaOrRef(entry.getValue(), SchemaFormat.ASYNCAPI_V3))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); builder.properties(propertiesMapped); } - builder.description(value.getDescription()); + builder.description(swaggerSchema.getDescription()); - builder.format(value.getFormat()); - builder.pattern(value.getPattern()); + builder.format(swaggerSchema.getFormat()); + builder.pattern(swaggerSchema.getPattern()); - if (value.getExclusiveMinimum() != null && value.getExclusiveMinimum()) { - builder.exclusiveMinimum(value.getMinimum()); - } else if (value.getExclusiveMinimumValue() != null) { - builder.exclusiveMinimum(value.getExclusiveMinimumValue()); + if (swaggerSchema.getExclusiveMinimum() != null && swaggerSchema.getExclusiveMinimum()) { + builder.exclusiveMinimum(swaggerSchema.getMinimum()); + } else if (swaggerSchema.getExclusiveMinimumValue() != null) { + builder.exclusiveMinimum(swaggerSchema.getExclusiveMinimumValue()); } else { - builder.minimum(value.getMinimum()); + builder.minimum(swaggerSchema.getMinimum()); } - if (value.getExclusiveMaximum() != null && value.getExclusiveMaximum()) { - builder.exclusiveMaximum(value.getMaximum()); - } else if (value.getExclusiveMaximumValue() != null) { - builder.exclusiveMaximum(value.getExclusiveMaximumValue()); + if (swaggerSchema.getExclusiveMaximum() != null && swaggerSchema.getExclusiveMaximum()) { + builder.exclusiveMaximum(swaggerSchema.getMaximum()); + } else if (swaggerSchema.getExclusiveMaximumValue() != null) { + builder.exclusiveMaximum(swaggerSchema.getExclusiveMaximumValue()); } else { - builder.maximum(value.getMaximum()); + builder.maximum(swaggerSchema.getMaximum()); } - builder.multipleOf(value.getMultipleOf()); + builder.multipleOf(swaggerSchema.getMultipleOf()); - builder.minLength(value.getMinLength()); - builder.maxLength(value.getMaxLength()); + builder.minLength(swaggerSchema.getMinLength()); + builder.maxLength(swaggerSchema.getMaxLength()); - List anEnum = value.getEnum(); + List anEnum = swaggerSchema.getEnum(); if (anEnum != null) { List enumStringValues = anEnum.stream().map(Object::toString).collect(Collectors.toCollection(ArrayList::new)); @@ -118,75 +139,73 @@ public SchemaObject mapSchema(Schema value) { builder.enumValues(enumStringValues); } - Object example = value.getExample(); + Object example = swaggerSchema.getExample(); if (example != null) { builder.examples(List.of(example)); } - List examples = value.getExamples(); + List examples = swaggerSchema.getExamples(); if (examples != null && !examples.isEmpty()) { builder.examples(examples); } - Object additionalProperties = value.getAdditionalProperties(); + Object additionalProperties = swaggerSchema.getAdditionalProperties(); if (additionalProperties instanceof Schema) { - builder.additionalProperties(mapSchemaOrRef((Schema) additionalProperties)); + builder.additionalProperties(mapSchemaOrRef((Schema) additionalProperties, SchemaFormat.ASYNCAPI_V3)); } - builder.required(value.getRequired()); + builder.required(swaggerSchema.getRequired()); - if (value.getDiscriminator() != null) { - builder.discriminator(value.getDiscriminator().getPropertyName()); + if (swaggerSchema.getDiscriminator() != null) { + builder.discriminator(swaggerSchema.getDiscriminator().getPropertyName()); } - List allOf = value.getAllOf(); + List allOf = swaggerSchema.getAllOf(); if (allOf != null) { builder.allOf(allOf.stream() - .filter((el) -> el instanceof Schema) - .map((Object schema) -> mapSchemaOrRef((Schema) schema)) + .map(schema -> mapSchemaOrRef(schema, SchemaFormat.ASYNCAPI_V3)) .collect(Collectors.toList())); } - List oneOf = value.getOneOf(); + List oneOf = swaggerSchema.getOneOf(); if (oneOf != null) { builder.oneOf(oneOf.stream() - .filter((el) -> el instanceof Schema) - .map((Object schema) -> mapSchemaOrRef((Schema) schema)) + .map(schema -> mapSchemaOrRef(schema, SchemaFormat.ASYNCAPI_V3)) .collect(Collectors.toList())); } - List anyOf = value.getAnyOf(); + List anyOf = swaggerSchema.getAnyOf(); if (anyOf != null) { builder.anyOf(anyOf.stream() - .filter((el) -> el instanceof Schema) - .map((Object schema) -> mapSchemaOrRef((Schema) schema)) + .map(schema -> mapSchemaOrRef(schema, SchemaFormat.ASYNCAPI_V3)) .collect(Collectors.toList())); } - builder.constValue(value.getConst()); + builder.constValue(swaggerSchema.getConst()); - Schema not = value.getNot(); + Schema not = swaggerSchema.getNot(); if (not != null) { - builder.not(mapSchemaOrRef(not)); + builder.not(mapSchemaOrRef(not, SchemaFormat.ASYNCAPI_V3)); } - Schema items = value.getItems(); - if (items != null && "array".equals(value.getType())) { - builder.items(mapSchemaOrRef(items)); + Schema items = swaggerSchema.getItems(); + if (items != null && "array".equals(swaggerSchema.getType())) { + builder.items(mapSchemaOrRef(items, SchemaFormat.ASYNCAPI_V3)); } - builder.uniqueItems(value.getUniqueItems()); + builder.uniqueItems(swaggerSchema.getUniqueItems()); - builder.minItems(value.getMinItems()); - builder.maxItems(value.getMaxItems()); + builder.minItems(swaggerSchema.getMinItems()); + builder.maxItems(swaggerSchema.getMaxItems()); return builder.build(); } - private static void assignType(Schema value, SchemaObject.SchemaObjectBuilder builder, boolean isNullable) { - Set types = value.getTypes() == null ? new HashSet<>() : new HashSet(value.getTypes()); - if (!types.contains(value.getType())) { + private static void assignType(Schema swaggerSchema, SchemaObject.SchemaObjectBuilder builder, boolean isNullable) { + Set types = + swaggerSchema.getTypes() == null ? new HashSet<>() : new HashSet(swaggerSchema.getTypes()); + if (!types.contains(swaggerSchema.getType())) { // contradicting types; prefer type for backward compatibility // maintainer note: remove condition in next major release - builder.type(value.getType()); + builder.type(swaggerSchema.getType()); return; } @@ -235,6 +254,7 @@ public Object unwrapSchema(Object schema) { * * * if no type is matching, a Runtime Exception is thrown. + * * @param schema Object representing an schema. * @return the resulting Schema */ @@ -262,6 +282,7 @@ public Schema mapToSwagger(Object schema) { * This method does not perform a 'deep' transformation, only the root attributes of asyncApiSchema * are mapped to the Swagger schema. The properties of asyncApiSchema will not be mapped to the * Swagger schema. + * * @param asyncApiSchema * @return */ diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/PayloadSchemaFormat.java b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/PayloadSchemaFormat.java new file mode 100644 index 000000000..748a2d17c --- /dev/null +++ b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/PayloadSchemaFormat.java @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.configuration.properties; + +import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; + +public enum PayloadSchemaFormat { + ASYNCAPI_V3(SchemaFormat.ASYNCAPI_V3), + OPENAPI_V3(SchemaFormat.OPENAPI_V3), + OPENAPI_V3_1(SchemaFormat.OPENAPI_V3_1); + + private final SchemaFormat schemaFormat; + + PayloadSchemaFormat(SchemaFormat schemaFormat) { + this.schemaFormat = schemaFormat; + } + + public SchemaFormat getSchemaFormat() { + return schemaFormat; + } +} diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/SpringwolfConfigProperties.java b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/SpringwolfConfigProperties.java index dec84aa4b..611daa703 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/SpringwolfConfigProperties.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/SpringwolfConfigProperties.java @@ -141,6 +141,8 @@ public static class ConfigDocket { */ private String defaultContentType = DEFAULT_CONTENT_TYPE; + private PayloadSchemaFormat payloadSchemaFormat = PayloadSchemaFormat.ASYNCAPI_V3; + @Nullable private Map servers; diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java index 4216ca489..43af6c793 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java @@ -2,6 +2,7 @@ package io.github.springwolf.core.asyncapi.components; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; import io.github.springwolf.asyncapi.v3.model.schema.SchemaType; @@ -33,7 +34,7 @@ void mapReference() { schema.set$ref("#/components/schemas/MySchema"); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, SchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getReference()).isEqualTo(new SchemaReference("#/components/schemas/MySchema")); @@ -46,7 +47,7 @@ void mapSchema() { schema.setType(SchemaType.STRING); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, SchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getType()).containsExactly(SchemaType.STRING); @@ -65,11 +66,12 @@ void mapExternalDocs() { schema.setExternalDocs(externalDocs); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getExternalDocs().getDescription()).isEqualTo(externalDocs.getDescription()); - assertThat(componentSchema.getExternalDocs().getUrl()).isEqualTo(externalDocs.getUrl()); + assertThat(componentSchema.getSchema().getExternalDocs().getDescription()) + .isEqualTo(externalDocs.getDescription()); + assertThat(componentSchema.getSchema().getExternalDocs().getUrl()).isEqualTo(externalDocs.getUrl()); } @Test @@ -79,10 +81,10 @@ void mapDeprecated() { schema.setDeprecated(true); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getDeprecated()).isEqualTo(true); + assertThat(componentSchema.getSchema().getDeprecated()).isEqualTo(true); } @Test @@ -92,10 +94,10 @@ void mapTitle() { schema.setTitle("title"); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getTitle()).isEqualTo(schema.getTitle()); + assertThat(componentSchema.getSchema().getTitle()).isEqualTo(schema.getTitle()); } @Test @@ -105,10 +107,10 @@ void mapType() { schema.setType(SchemaType.STRING); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getType()).containsExactly(schema.getType()); + assertThat(componentSchema.getSchema().getType()).containsExactly(schema.getType()); } @Test @@ -120,10 +122,11 @@ void mapProperties() { schema.addProperty("property", property); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(((ComponentSchema) componentSchema.getProperties().get("property")) + assertThat(((ComponentSchema) + componentSchema.getSchema().getProperties().get("property")) .getSchema() .getType()) .containsExactly(property.getType()); @@ -136,10 +139,10 @@ void mapDescription() { schema.setDescription("description"); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getDescription()).isEqualTo(schema.getDescription()); + assertThat(componentSchema.getSchema().getDescription()).isEqualTo(schema.getDescription()); } @Test @@ -149,10 +152,10 @@ void mapFormat() { schema.setFormat("format"); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getFormat()).isEqualTo(schema.getFormat()); + assertThat(componentSchema.getSchema().getFormat()).isEqualTo(schema.getFormat()); } @Test @@ -162,10 +165,10 @@ void mapPattern() { schema.setPattern("pattern"); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getPattern()).isEqualTo(schema.getPattern()); + assertThat(componentSchema.getSchema().getPattern()).isEqualTo(schema.getPattern()); } @Test @@ -176,11 +179,11 @@ void mapExclusiveMinimum() { schema.setExclusiveMinimum(true); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getExclusiveMinimum()).isEqualTo(schema.getMinimum()); - assertThat(componentSchema.getMinimum()).isNull(); + assertThat(componentSchema.getSchema().getExclusiveMinimum()).isEqualTo(schema.getMinimum()); + assertThat(componentSchema.getSchema().getMinimum()).isNull(); } @Test @@ -190,11 +193,11 @@ void mapExclusiveMinimumValue() { schema.setExclusiveMinimumValue(BigDecimal.ONE); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getExclusiveMinimum()).isEqualTo(schema.getExclusiveMinimumValue()); - assertThat(componentSchema.getMinimum()).isNull(); + assertThat(componentSchema.getSchema().getExclusiveMinimum()).isEqualTo(schema.getExclusiveMinimumValue()); + assertThat(componentSchema.getSchema().getMinimum()).isNull(); } @Test @@ -205,11 +208,11 @@ void mapExclusiveMaximum() { schema.setExclusiveMaximum(true); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getExclusiveMaximum()).isEqualTo(schema.getMaximum()); - assertThat(componentSchema.getMaximum()).isNull(); + assertThat(componentSchema.getSchema().getExclusiveMaximum()).isEqualTo(schema.getMaximum()); + assertThat(componentSchema.getSchema().getMaximum()).isNull(); } @Test @@ -219,11 +222,11 @@ void mapExclusiveMaximumValue() { schema.setExclusiveMaximumValue(BigDecimal.ONE); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getExclusiveMaximum()).isEqualTo(schema.getExclusiveMaximumValue()); - assertThat(componentSchema.getMaximum()).isNull(); + assertThat(componentSchema.getSchema().getExclusiveMaximum()).isEqualTo(schema.getExclusiveMaximumValue()); + assertThat(componentSchema.getSchema().getMaximum()).isNull(); } @Test @@ -233,11 +236,11 @@ void mapMinimum() { schema.setMinimum(BigDecimal.ONE); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getMinimum()).isEqualTo(schema.getMinimum()); - assertThat(componentSchema.getExclusiveMinimum()).isNull(); + assertThat(componentSchema.getSchema().getMinimum()).isEqualTo(schema.getMinimum()); + assertThat(componentSchema.getSchema().getExclusiveMinimum()).isNull(); } @Test @@ -247,11 +250,11 @@ void mapMaximum() { schema.setMaximum(BigDecimal.ONE); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getMaximum()).isEqualTo(schema.getMaximum()); - assertThat(componentSchema.getExclusiveMaximum()).isNull(); + assertThat(componentSchema.getSchema().getMaximum()).isEqualTo(schema.getMaximum()); + assertThat(componentSchema.getSchema().getExclusiveMaximum()).isNull(); } @Test @@ -261,10 +264,10 @@ void mapMultipleOf() { schema.setMultipleOf(BigDecimal.ONE); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getMultipleOf()).isEqualTo(schema.getMultipleOf()); + assertThat(componentSchema.getSchema().getMultipleOf()).isEqualTo(schema.getMultipleOf()); } @Test @@ -274,10 +277,10 @@ void mapMinLength() { schema.setMinLength(1); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getMinLength()).isEqualTo(schema.getMinLength()); + assertThat(componentSchema.getSchema().getMinLength()).isEqualTo(schema.getMinLength()); } @Test @@ -287,10 +290,10 @@ void mapMaxLength() { schema.setMaxLength(1); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getMaxLength()).isEqualTo(schema.getMaxLength()); + assertThat(componentSchema.getSchema().getMaxLength()).isEqualTo(schema.getMaxLength()); } @Test @@ -300,10 +303,10 @@ void mapEnum() { schema.setEnum(List.of("enum1", "enum2")); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getEnumValues()).isEqualTo(schema.getEnum()); + assertThat(componentSchema.getSchema().getEnumValues()).isEqualTo(schema.getEnum()); } @Test @@ -313,10 +316,10 @@ void mapExample() { schema.setExample("example"); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getExamples()).isEqualTo(List.of(schema.getExample())); + assertThat(componentSchema.getSchema().getExamples()).isEqualTo(List.of(schema.getExample())); } @Test @@ -328,10 +331,14 @@ void mapAdditionalProperties() { schema.setAdditionalProperties(additionalProperties); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getAdditionalProperties().getSchema().getType()) + assertThat(componentSchema + .getSchema() + .getAdditionalProperties() + .getSchema() + .getType()) .containsExactly(additionalProperties.getType()); } @@ -342,10 +349,10 @@ void mapRequired() { schema.setRequired(List.of("required")); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getRequired()).isEqualTo(schema.getRequired()); + assertThat(componentSchema.getSchema().getRequired()).isEqualTo(schema.getRequired()); } @Test @@ -357,10 +364,13 @@ void mapDiscriminator() { schema.setDiscriminator(discriminator); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getDiscriminator()).isEqualTo("name"); + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + + assertThat(componentSchema.getSchema().getDiscriminator()).isEqualTo("name"); } @Test @@ -372,10 +382,13 @@ void mapAllOf() { schema.addAllOfItem(allOf); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat((componentSchema.getAllOf().get(0).getSchema()).getType()) + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + + assertThat((componentSchema.getSchema().getAllOf().get(0).getSchema()).getType()) .containsExactly(allOf.getType()); } @@ -388,10 +401,13 @@ void mapOneOf() { schema.addOneOfItem(oneOf); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat((componentSchema.getOneOf().get(0).getSchema()).getType()) + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + + assertThat((componentSchema.getSchema().getOneOf().get(0).getSchema()).getType()) .containsExactly(oneOf.getType()); } @@ -404,10 +420,12 @@ void mapAnyOf() { schema.addAnyOfItem(anyOf); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat((componentSchema.getAnyOf().get(0).getSchema()).getType()) + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat((componentSchema.getSchema().getAnyOf().get(0).getSchema()).getType()) .containsExactly(anyOf.getType()); } @@ -418,10 +436,12 @@ void mapConst() { schema.setConst("const"); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getConstValue()).isEqualTo(schema.getConst()); + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat(componentSchema.getSchema().getConstValue()).isEqualTo(schema.getConst()); } @Test @@ -433,10 +453,13 @@ void mapNot() { schema.setNot(not); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat((componentSchema.getNot().getSchema()).getType()).containsExactly(not.getType()); + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat((componentSchema.getSchema().getNot().getSchema()).getType()) + .containsExactly(not.getType()); } @Test @@ -449,10 +472,13 @@ void mapItems() { schema.setItems(item); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat((componentSchema.getItems().getSchema()).getType()).containsExactly(item.getType()); + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat((componentSchema.getSchema().getItems().getSchema()).getType()) + .containsExactly(item.getType()); } @Test @@ -462,10 +488,12 @@ void mapUniqueItems() { schema.setUniqueItems(false); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getUniqueItems()).isEqualTo(schema.getUniqueItems()); + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat(componentSchema.getSchema().getUniqueItems()).isEqualTo(schema.getUniqueItems()); } @Test @@ -475,10 +503,12 @@ void mapMinItems() { schema.setMinItems(1); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getMinItems()).isEqualTo(schema.getMinItems()); + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat(componentSchema.getSchema().getMinItems()).isEqualTo(schema.getMinItems()); } @Test @@ -488,10 +518,12 @@ void mapMaxItems() { schema.setMaxItems(10); // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); // then - assertThat(componentSchema.getMaxItems()).isEqualTo(schema.getMaxItems()); + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat(componentSchema.getSchema().getMaxItems()).isEqualTo(schema.getMaxItems()); } } @@ -504,10 +536,10 @@ void mapDescription() { schema.setDescription("description"); // when - Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema); + Schema swaggerSchema = swaggerSchemaUtil.mapToSwagger(schema); // then - assertThat(componentSchema.getDescription()).isEqualTo(schema.getDescription()); + assertThat(swaggerSchema.getDescription()).isEqualTo(schema.getDescription()); } @Test @@ -517,10 +549,10 @@ void mapExamples() { schema.setExamples(List.of("example1", "example2")); // when - Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema); + Schema swaggerSchema = swaggerSchemaUtil.mapToSwagger(schema); // then - assertThat(componentSchema.getExamples()).isEqualTo(schema.getExamples()); + assertThat(swaggerSchema.getExamples()).isEqualTo(schema.getExamples()); } @Test @@ -530,10 +562,10 @@ void mapEnum() { schema.setEnumValues(List.of("enum1", "enum2")); // when - Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema); + Schema swaggerSchema = swaggerSchemaUtil.mapToSwagger(schema); // then - assertThat(componentSchema.getEnum()).isEqualTo(schema.getEnumValues()); + assertThat(swaggerSchema.getEnum()).isEqualTo(schema.getEnumValues()); } @Test @@ -544,11 +576,11 @@ void mapNullableEnum() { schema.setTypes(Set.of(SchemaType.STRING, SchemaType.NULL)); // nullable // when - Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema); + Schema swaggerSchema = swaggerSchemaUtil.mapToSwagger(schema); // then - assertThat(componentSchema.getEnum()).isEqualTo(schema.getEnumValues()); - assertThat(componentSchema.getTypes()).isEqualTo(schema.getType()); + assertThat(swaggerSchema.getEnum()).isEqualTo(schema.getEnumValues()); + assertThat(swaggerSchema.getTypes()).isEqualTo(schema.getType()); } @Test @@ -558,10 +590,10 @@ void mapType() { schema.setType(SchemaType.STRING); // when - Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema); + Schema swaggerSchema = swaggerSchemaUtil.mapToSwagger(schema); // then - assertThat(componentSchema.getType()).isEqualTo(SchemaType.STRING); + assertThat(swaggerSchema.getType()).isEqualTo(SchemaType.STRING); } } } diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorTest.java index 5560d7b09..d1ad408b0 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorTest.java @@ -51,7 +51,7 @@ static void tearDownClass() { @Test void getNoDocumentedHeaders() throws Exception { // given - when(schemaService.extractSchema(String.class)) + when(schemaService.postProcessSimpleSchema(String.class)) .thenReturn(new SwaggerSchemaService.ExtractedSchemas(stringSwaggerSchema, Map.of())); // when @@ -65,7 +65,7 @@ void getNoDocumentedHeaders() throws Exception { @Test void getHeaderWithSingleHeaderAnnotation() throws Exception { // given - when(schemaService.extractSchema(String.class)) + when(schemaService.postProcessSimpleSchema(String.class)) .thenReturn(new SwaggerSchemaService.ExtractedSchemas(stringSwaggerSchema, Map.of())); // when @@ -86,7 +86,7 @@ void getHeaderWithSingleHeaderAnnotation() throws Exception { @Test void getHeaderWithMultipleHeaderAnnotation() throws Exception { // given - when(schemaService.extractSchema(String.class)) + when(schemaService.postProcessSimpleSchema(String.class)) .thenReturn(new SwaggerSchemaService.ExtractedSchemas(stringSwaggerSchema, Map.of())); // when diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageServiceTest.java index 3f4446ca9..96f30cf22 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageServiceTest.java @@ -48,7 +48,7 @@ void setUp() { .when(stringValueResolver) .resolveStringValue(any()); - when(componentsService.registerSchema(any())).thenReturn("headerSchemaName"); + when(componentsService.registerSimpleSchema(any())).thenReturn("headerSchemaName"); when(messageBindingProcessor.process(any())).thenReturn(Optional.empty()); when(payloadAsyncOperationService.extractSchema(any(), any())).thenReturn(payloadSchema); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadAsyncOperationServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadAsyncOperationServiceTest.java index f157527b4..0f1a9984e 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadAsyncOperationServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadAsyncOperationServiceTest.java @@ -111,6 +111,6 @@ void shouldReturnPayloadNotUsed() { SchemaObject schema = result.schema().getSchema(); assertThat(schema.getTitle()).isEqualTo("PayloadNotUsed"); assertThat(schema.getDescription()).isEqualTo("No payload specified"); - verify(componentsService).registerSchema(PAYLOAD_NOT_USED.schema().getSchema()); + verify(componentsService).registerSimpleSchema(PAYLOAD_NOT_USED.schema().getSchema()); } } diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java index 6cc062bdb..a7562a762 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaType; import io.github.springwolf.core.asyncapi.components.postprocessors.SchemasPostProcessor; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; @@ -57,7 +58,7 @@ void setUp() { @Test void classWithSchemaAnnotationWithAsyncapiSchemaformat() { ComponentSchema schema = schemaService - .resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant") + .resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3) .rootSchema(); assertThat(schema.getReference().getRef()).isEqualTo("#/components/schemas/DifferentName"); @@ -66,7 +67,7 @@ void classWithSchemaAnnotationWithAsyncapiSchemaformat() { @Test void classWithSchemaAnnotationWithOpenapiSchemaformat() { ComponentSchema schema = schemaService - .resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant") + .resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3) .rootSchema(); assertThat(schema.getReference().getRef()).isEqualTo("#/components/schemas/DifferentName"); @@ -85,7 +86,7 @@ void getDefinitionWithoutFqnClassName() throws Exception { Class clazz = OneFieldFooWithoutFqn.class; // swagger seems to cache results. Therefore, a new class must be used. Map schemas = schemaServiceWithFqn - .resolveSchema(clazz, "content-type-not-relevant") + .resolveSchema(clazz, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3) .referencedSchemas(); String actualDefinitions = objectMapper.writer(printer).writeValueAsString(schemas); @@ -97,7 +98,7 @@ void getDefinitionWithoutFqnClassName() throws Exception { @Test void postProcessorsAreCalledWithAsyncapiSchemaformat() { - schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "some-content-type"); + schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "some-content-type", SchemaFormat.ASYNCAPI_V3); verify(schemasPostProcessor).process(any(), any(), eq("some-content-type")); verify(schemasPostProcessor2).process(any(), any(), eq("some-content-type")); @@ -105,7 +106,7 @@ void postProcessorsAreCalledWithAsyncapiSchemaformat() { @Test void postProcessorsAreCalledWithOpenapiSchemaformat() { - schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "some-content-type"); + schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "some-content-type", SchemaFormat.ASYNCAPI_V3); verify(schemasPostProcessor).process(any(), any(), eq("some-content-type")); verify(schemasPostProcessor2).process(any(), any(), eq("some-content-type")); @@ -121,15 +122,16 @@ void postProcessorIsSkippedWhenSchemaWasRemoved() { .when(schemasPostProcessor) .process(any(), any(), any()); - schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant"); + schemaService.resolveSchema( + ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3); verifyNoInteractions(schemasPostProcessor2); } @Test void modelConvertersRefsAreResolved() { - SwaggerSchemaService.ExtractedSchemas schema = - schemaService.resolveSchema(ModelConverterNativeClass.class, "content-type-not-relevant"); + SwaggerSchemaService.ExtractedSchemas schema = schemaService.resolveSchema( + ModelConverterNativeClass.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3); assertThat(schema.rootSchema().getReference().getRef()) .contains(ModelConverterNativeClass.class.getName().replace("$", ".")); diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java index 2310995aa..10ff9ea92 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java @@ -88,7 +88,7 @@ private ChannelObject buildChannel(FunctionalChannelBeanData beanData, String ch var messagePayload = MessagePayload.of( MultiFormatSchema.builder().schema(payloadSchema.payload()).build()); - String headerModelName = componentsService.registerSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED); + String headerModelName = componentsService.registerSimpleSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED); MessageHeaders messageHeaders = MessageHeaders.of(SchemaReference.toSchema(headerModelName)); Map messageBinding = buildMessageBinding(beanData.annotatedElement()); diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java index f15ccca4d..01d2f3c10 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java @@ -85,7 +85,7 @@ private Operation buildOperation(FunctionalChannelBeanData beanData) { MessagePayload payload = MessagePayload.of( MultiFormatSchema.builder().schema(payloadSchema.payload()).build()); - String headerModelName = componentsService.registerSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED); + String headerModelName = componentsService.registerSimpleSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED); MessageHeaders messageHeaders = MessageHeaders.of(SchemaReference.toSchema(headerModelName)); MessageObject message = MessageObject.builder() diff --git a/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java b/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java index a5ba0e3a3..89990a487 100644 --- a/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java +++ b/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java @@ -94,7 +94,7 @@ class SpringwolfJmsControllerIntegrationTest { void setup() { when(springwolfJmsProducer.isEnabled()).thenReturn(true); - componentsService.registerSchema(SchemaObject.builder() + componentsService.registerSimpleSchema(SchemaObject.builder() .title(PayloadDto.class.getName()) .properties(new HashMap<>()) .build()); diff --git a/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java b/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java index 991777d39..1b6e2c240 100644 --- a/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java +++ b/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java @@ -94,7 +94,7 @@ class SpringwolfKafkaControllerIntegrationTest { void setup() { when(springwolfKafkaProducer.isEnabled()).thenReturn(true); - componentsService.registerSchema(SchemaObject.builder() + componentsService.registerSimpleSchema(SchemaObject.builder() .title(PayloadDto.class.getName()) .properties(new HashMap<>()) .build()); From 491cbd694587ee7853958c35dd9cbef7fc36b3ef Mon Sep 17 00:00:00 2001 From: tvahrst Date: Sat, 1 Nov 2025 21:41:46 +0100 Subject: [PATCH 02/14] feat: tests for schemaformat option for payload schemas added --- .../examples/walkers/DefaultSchemaWalker.java | 37 +- .../asyncapi/grouping/GroupingService.java | 14 +- .../AsyncAnnotationMessageService.java | 8 +- .../asyncapi/schemas/SwaggerSchemaUtil.java | 4 +- .../components/SwaggerSchemaUtilTest.java | 86 ++++ .../walkers/DefaultSchemaWalkerTest.java | 27 ++ .../schemas/SwaggerSchemaServiceTest.java | 4 +- ...OpenApiV31SchemaFormatIntegrationTest.java | 437 ++++++++++++++++++ ...hOpenApiV3SchemaFormatIntegrationTest.java | 437 ++++++++++++++++++ .../PolymorphicPayloadApplication.java | 12 +- ...OpenApiV31SchemaFormatIntegrationTest.java | 44 ++ ...hOpenApiV3SchemaFormatIntegrationTest.java | 44 ++ .../test/resources/asyncapi.openapiv3.json | 224 +++++++++ .../test/resources/asyncapi.openapiv31.json | 224 +++++++++ 14 files changed, 1587 insertions(+), 15 deletions(-) create mode 100644 springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/examples/walkers/DefaultSchemaWalkerTest.java create mode 100644 springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentWithOpenApiV31SchemaFormatIntegrationTest.java create mode 100644 springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentWithOpenApiV3SchemaFormatIntegrationTest.java create mode 100644 springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java create mode 100644 springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java create mode 100644 springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv3.json create mode 100644 springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/examples/walkers/DefaultSchemaWalker.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/examples/walkers/DefaultSchemaWalker.java index 96f8322da..9c8664ece 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/examples/walkers/DefaultSchemaWalker.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/examples/walkers/DefaultSchemaWalker.java @@ -7,6 +7,7 @@ import io.swagger.v3.oas.models.media.StringSchema; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; import java.text.SimpleDateFormat; @@ -175,7 +176,14 @@ private Optional buildExampleFromUnvisitedSchema( return composedSchemaExample; } - String type = schema.getType(); + // schema may be an openapi v3 or v3.1 schema. While v3 uses an simple 'type' field, v3.1 supports a set of + // types, for example ["string", "null"]. + + String type = getTypeForExampleValue(schema); + if (type == null) { + return Optional.empty(); + } + return switch (type) { case "array" -> buildArrayExample(schema, definitions, visited); case "boolean" -> exampleValueGenerator.createBooleanExample(DEFAULT_BOOLEAN_EXAMPLE, schema); @@ -235,6 +243,33 @@ private String getFirstEnumValue(Schema schema) { return null; } + /** + * looks in schemas openapi-v3 'type' and openapi-v3.1 'types' fields to + * find the best candidate to use as an example value. + * + * @param schema + * @return the type to use for example values, or null if no suitable type was found. + */ + @Nullable + String getTypeForExampleValue(Schema schema) { + // if the single type field is present, it has precedence over the types field + if (schema.getType() != null) { + return schema.getType(); + } + + Set types = schema.getTypes(); + + if (types == null || types.isEmpty()) { + return null; + } + + return types.stream() + .filter(t -> !"null".equals(t)) + .sorted() // sort types to be deterministic + .findFirst() + .orElse(null); + } + private Optional buildFromComposedSchema( Optional name, Schema schema, Map definitions, Set visited) { final List schemasAllOf = schema.getAllOf(); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java index 5af1588aa..f926086e2 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java @@ -203,7 +203,7 @@ private void markSchemas(AsyncAPI fullAsyncApi, MarkingContext markingContext, S * properties, allOf, anyOf, oneOf, not- and items references. Trys to deduce the schema id from the ref path and * returns a Set of detected schema ids. * - * @param markingContext the current {@link MarkingContext} + * @param markingContext the current {@link MarkingContext} * @param componentSchema the {@link ComponentSchema} to analyze * @return Set of schema ids representing nested schema refs */ @@ -217,14 +217,16 @@ private static Set findUnmarkedNestedSchemas( if (componentSchema.getMultiFormatSchema() != null) { MultiFormatSchema multiFormatSchema = componentSchema.getMultiFormatSchema(); - // Currently we support async_api and open_api format. + // Currently we support async_api and open_api v3 and v3.1 format. // The concrete schemaformat mediatype can contain json/yaml postfix, so we check wether the begin of the // media type matches. - if (multiFormatSchema.getSchemaFormat().startsWith(SchemaFormat.ASYNCAPI_V3.toString()) + String schemaFormat = multiFormatSchema.getSchemaFormat(); + if (schemaFormat.startsWith(SchemaFormat.ASYNCAPI_V3.toString()) && multiFormatSchema.getSchema() instanceof SchemaObject schemaObject) { return findUnmarkedNestedSchemasForAsyncAPISchema(markingContext, schemaObject); } - if (multiFormatSchema.getSchemaFormat().startsWith(SchemaFormat.OPENAPI_V3.toString()) + if ((schemaFormat.startsWith(SchemaFormat.OPENAPI_V3.toString()) + || schemaFormat.startsWith(SchemaFormat.OPENAPI_V3_1.toString())) && multiFormatSchema.getSchema() instanceof Schema openapiSchema) { return findUnmarkedNestedSchemasForOpenAPISchema(markingContext, openapiSchema); } @@ -240,7 +242,7 @@ private static Set findUnmarkedNestedSchemas( * returns a Set of detected schema ids. * * @param markingContext the current {@link MarkingContext} - * @param schema the {@link SchemaObject} to analyze + * @param schema the {@link SchemaObject} to analyze * @return Set of schema ids representing nested schema refs */ private static Set findUnmarkedNestedSchemasForAsyncAPISchema( @@ -276,7 +278,7 @@ private static Set findUnmarkedNestedSchemasForAsyncAPISchema( * returns a Set of detected schema ids. * * @param markingContext the current {@link MarkingContext} - * @param openapiSchema the Swagger {@link Schema} to analyze + * @param openapiSchema the Swagger {@link Schema} to analyze * @return Set of schema ids representing nested schema refs */ private static Set findUnmarkedNestedSchemasForOpenAPISchema( diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java index f832a4073..661c202f6 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java @@ -42,8 +42,12 @@ public MessageObject buildMessage(AsyncOperation operationData, Method method) { Map messageBinding = AsyncAnnotationUtil.processMessageBindingFromAnnotation(method, messageBindingProcessors); - var messagePayload = MessagePayload.of( - MultiFormatSchema.builder().schema(payloadSchema.payload()).build()); + // if payloadSchema.payload is already an instance of MultiFormatSchema, do not wrap again. + MultiFormatSchema multiFormatSchema = (payloadSchema.payload() instanceof MultiFormatSchema mfs) + ? mfs + : MultiFormatSchema.builder().schema(payloadSchema.payload()).build(); + + var messagePayload = MessagePayload.of(multiFormatSchema); var builder = MessageObject.builder() .messageId(payloadSchema.name()) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java index 3c1b2b5e9..d2cb48776 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java @@ -62,8 +62,8 @@ public ComponentSchema mapSchemaOrRef(Schema swaggerSchema, SchemaFormat schemaF */ public ComponentSchema mapSchema(Schema swaggerSchema, SchemaFormat schemaFormat) { return switch (schemaFormat) { - case OPENAPI_V3, OPENAPI_V3_1 -> - ComponentSchema.of(new MultiFormatSchema(schemaFormat.value, swaggerSchema)); + case OPENAPI_V3, OPENAPI_V3_1 -> ComponentSchema.of( + new MultiFormatSchema(schemaFormat.value, swaggerSchema)); case ASYNCAPI_V3 -> ComponentSchema.of(mapSwaggerSchemaToAsyncApiSchema(swaggerSchema)); default -> throw new IllegalArgumentException("SchemaFormat " + schemaFormat + " is not supported"); }; diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java index 43af6c793..36a13cf52 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java @@ -2,6 +2,7 @@ package io.github.springwolf.core.asyncapi.components; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; +import io.github.springwolf.asyncapi.v3.model.schema.MultiFormatSchema; import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; @@ -27,6 +28,48 @@ class SwaggerSchemaUtilTest { @Nested class MapSchemaOrRef { + @Test + void mapToOpenApiV3Schema() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setType(SchemaType.STRING); + ExternalDocumentation externalDocs = new ExternalDocumentation(); + externalDocs.setDescription("description"); + externalDocs.setUrl("url"); + schema.setExternalDocs(externalDocs); + + // when + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, SchemaFormat.OPENAPI_V3); + + // then + // componentSchema should contain a MultiFormatSchema with the original openapi schema + MultiFormatSchema multiFormatSchema = componentSchema.getMultiFormatSchema(); + assertThat(multiFormatSchema).isNotNull(); + assertThat(multiFormatSchema.getSchema()).isSameAs(schema); + assertThat(multiFormatSchema.getSchemaFormat()).isEqualTo(SchemaFormat.OPENAPI_V3.value); + } + + @Test + void mapToOpenApiV31Schema() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setType(SchemaType.STRING); + ExternalDocumentation externalDocs = new ExternalDocumentation(); + externalDocs.setDescription("description"); + externalDocs.setUrl("url"); + schema.setExternalDocs(externalDocs); + + // when + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, SchemaFormat.OPENAPI_V3_1); + + // then + // componentSchema should contain a MultiFormatSchema with the original openapi schema + MultiFormatSchema multiFormatSchema = componentSchema.getMultiFormatSchema(); + assertThat(multiFormatSchema).isNotNull(); + assertThat(multiFormatSchema.getSchema()).isSameAs(schema); + assertThat(multiFormatSchema.getSchemaFormat()).isEqualTo(SchemaFormat.OPENAPI_V3_1.value); + } + @Test void mapReference() { // given @@ -56,6 +99,49 @@ void mapSchema() { @Nested class MapSchema { + + @Test + void mapToOpenApiV3Schema() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setType(SchemaType.STRING); + ExternalDocumentation externalDocs = new ExternalDocumentation(); + externalDocs.setDescription("description"); + externalDocs.setUrl("url"); + schema.setExternalDocs(externalDocs); + + // when + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.OPENAPI_V3); + + // then + // componentSchema should contain a MultiFormatSchema with the original openapi schema + MultiFormatSchema multiFormatSchema = componentSchema.getMultiFormatSchema(); + assertThat(multiFormatSchema).isNotNull(); + assertThat(multiFormatSchema.getSchema()).isSameAs(schema); + assertThat(multiFormatSchema.getSchemaFormat()).isEqualTo(SchemaFormat.OPENAPI_V3.value); + } + + @Test + void mapToOpenApiV31Schema() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setType(SchemaType.STRING); + ExternalDocumentation externalDocs = new ExternalDocumentation(); + externalDocs.setDescription("description"); + externalDocs.setUrl("url"); + schema.setExternalDocs(externalDocs); + + // when + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.OPENAPI_V3_1); + + // then + // componentSchema should contain a MultiFormatSchema with the original openapi schema + MultiFormatSchema multiFormatSchema = componentSchema.getMultiFormatSchema(); + assertThat(multiFormatSchema).isNotNull(); + assertThat(multiFormatSchema.getSchema()).isSameAs(schema); + assertThat(multiFormatSchema.getSchemaFormat()).isEqualTo(SchemaFormat.OPENAPI_V3_1.value); + } + @Test void mapExternalDocs() { // given diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/examples/walkers/DefaultSchemaWalkerTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/examples/walkers/DefaultSchemaWalkerTest.java new file mode 100644 index 000000000..fc7b9c215 --- /dev/null +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/examples/walkers/DefaultSchemaWalkerTest.java @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.asyncapi.components.examples.walkers; + +import io.swagger.v3.oas.models.media.Schema; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; + +class DefaultSchemaWalkerTest { + + @Test + void getTypeForExampleValue() { + DefaultSchemaWalker schemaWalker = new DefaultSchemaWalker<>(null); + + Schema schema1 = new Schema(); // no types defined. + Schema schema2 = new Schema().type("string"); + Schema schema3 = new Schema().types(Set.of("string", "null")); + Schema schema4 = new Schema().types(Set.of("string", "integer")); + + assertThat(schemaWalker.getTypeForExampleValue(schema1)).isNull(); + assertThat(schemaWalker.getTypeForExampleValue(schema2)).isEqualTo("string"); + assertThat(schemaWalker.getTypeForExampleValue(schema3)).isEqualTo("string"); + assertThat(schemaWalker.getTypeForExampleValue(schema4)).isEqualTo("integer"); + } +} diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java index a7562a762..afe46eef2 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java @@ -67,7 +67,7 @@ void classWithSchemaAnnotationWithAsyncapiSchemaformat() { @Test void classWithSchemaAnnotationWithOpenapiSchemaformat() { ComponentSchema schema = schemaService - .resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3) + .resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.OPENAPI_V3) .rootSchema(); assertThat(schema.getReference().getRef()).isEqualTo("#/components/schemas/DifferentName"); @@ -106,7 +106,7 @@ void postProcessorsAreCalledWithAsyncapiSchemaformat() { @Test void postProcessorsAreCalledWithOpenapiSchemaformat() { - schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "some-content-type", SchemaFormat.ASYNCAPI_V3); + schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "some-content-type", SchemaFormat.OPENAPI_V3); verify(schemasPostProcessor).process(any(), any(), eq("some-content-type")); verify(schemasPostProcessor2).process(any(), any(), eq("some-content-type")); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentWithOpenApiV31SchemaFormatIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentWithOpenApiV31SchemaFormatIntegrationTest.java new file mode 100644 index 000000000..e43d81004 --- /dev/null +++ b/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentWithOpenApiV31SchemaFormatIntegrationTest.java @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.integrationtests; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.github.springwolf.asyncapi.v3.jackson.AsyncApiSerializerService; +import io.github.springwolf.asyncapi.v3.model.AsyncAPI; +import io.github.springwolf.asyncapi.v3.model.channel.message.Message; +import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; +import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; +import io.github.springwolf.core.asyncapi.AsyncApiService; +import io.github.springwolf.core.asyncapi.scanners.common.headers.AsyncHeadersNotDocumented; +import io.github.springwolf.core.fixtures.MinimalIntegrationTestContextConfiguration; +import io.github.springwolf.core.integrationtests.application.fqn.FqnApplication; +import io.github.springwolf.core.integrationtests.application.listener.ListenerApplication; +import io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication; +import io.github.springwolf.core.integrationtests.application.publisher.PublisherApplication; +import io.github.springwolf.core.integrationtests.application.schema.SchemaEnumAsRefApplication; +import io.github.springwolf.core.standalone.DefaultStandaloneApplication; +import io.swagger.v3.oas.models.media.Schema; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.test.context.TestPropertySource; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AsyncApiDocumentWithOpenApiV31SchemaFormatIntegrationTest { + + @Nested + @SpringBootTest(classes = ListenerApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.listener", + "springwolf.docket.payload-schema-format=openapi_v3_1" + }) + class ListenerAnnotationTest { + @Value("${springwolf.docket.base-package}") + private String basePackage; + + @Autowired + private ConfigurableEnvironment environment; + + @Autowired + private AsyncApiService asyncApiService; + + @Test + void asyncListenerAnnotationIsFound() { + AsyncAPI asyncAPI = asyncApiService.getAsyncAPI(); + assertThat(asyncAPI).isNotNull(); + + assertThat(asyncAPI.getChannels().keySet()) + .containsExactlyInAnyOrder("listener-channel", "listener-class-channel"); + assertThat(asyncAPI.getChannels().get("listener-channel").getMessages()) + .containsOnlyKeys( + "java.lang.String", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getChannels().get("listener-class-channel").getMessages()) + .containsOnlyKeys("java.lang.Integer"); + assertThat(asyncAPI.getOperations()) + .containsOnlyKeys( + "listener-channel_receive_listen", + "listener-channel_receive_listen2", + "listener-channel_receive_listen3", + "listener-channel_receive_listen4", + "listener-class-channel_receive_listen"); + assertThat(asyncAPI.getComponents().getMessages()) + .containsOnlyKeys( + "java.lang.String", + "java.lang.Integer", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getComponents().getSchemas()) + .containsOnlyKeys( + "HeadersNotDocumented", + "java.lang.String", + "java.lang.Integer", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Bar", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + + MessageObject stringMessage = + (MessageObject) asyncAPI.getComponents().getMessages().get("java.lang.String"); + assertThat(stringMessage.getPayload().getMultiFormatSchema().getSchema()) + .isInstanceOf(Schema.class); + Schema inlineStringSchema = + (Schema) stringMessage.getPayload().getMultiFormatSchema().getSchema(); + assertThat(inlineStringSchema.getTypes()).containsExactly("string"); + + MessageObject fooMessage = (MessageObject) asyncAPI.getComponents() + .getMessages() + .get("io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(fooMessage.getPayload().getMultiFormatSchema().getSchema()) + .isInstanceOf(SchemaReference.class); + SchemaReference fooSchemaRef = (SchemaReference) + fooMessage.getPayload().getMultiFormatSchema().getSchema(); + assertThat(fooSchemaRef.getRef()) + .isEqualTo( + "#/components/schemas/io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + } + + @Test + void ensureThatStandaloneResultIsIdentical() { + // given + AsyncApiService asyncApiService = createStandaloneAsyncApiService(environment, basePackage); + + // when + AsyncAPI asyncApi = asyncApiService.getAsyncAPI(); + + // then + assertThat(asyncApi).isEqualTo(asyncApiService.getAsyncAPI()); + } + } + + @Nested + @SpringBootTest(classes = PublisherApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.publisher", + "springwolf.docket.payload-schema-format=openapi_v3_1" + }) + class PublisherAnnotationTest { + @Value("${springwolf.docket.base-package}") + private String basePackage; + + @Autowired + private ConfigurableEnvironment environment; + + @Autowired + private AsyncApiService asyncApiService; + + @Test + void asyncPublisherAnnotationIsFound() { + AsyncAPI asyncAPI = asyncApiService.getAsyncAPI(); + assertThat(asyncAPI).isNotNull(); + + assertThat(asyncAPI.getChannels().keySet()) + .containsExactlyInAnyOrder("publisher-channel", "publisher-class-channel"); + assertThat(asyncAPI.getChannels().get("publisher-channel").getMessages()) + .containsOnlyKeys( + "java.lang.String", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getChannels().get("publisher-class-channel").getMessages()) + .containsOnlyKeys("java.lang.Integer"); + assertThat(asyncAPI.getOperations()) + .containsOnlyKeys( + "publisher-channel_send_publish", + "publisher-channel_send_publish2", + "publisher-channel_send_publish3", + "publisher-channel_send_publish4", + "publisher-class-channel_send_publish"); + assertThat(asyncAPI.getComponents().getMessages()) + .containsOnlyKeys( + "java.lang.String", + "java.lang.Integer", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getComponents().getSchemas()) + .containsOnlyKeys( + "HeadersNotDocumented", + "java.lang.String", + "java.lang.Integer", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Bar", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + + MessageObject stringMessage = + (MessageObject) asyncAPI.getComponents().getMessages().get("java.lang.String"); + assertThat(stringMessage.getPayload().getMultiFormatSchema().getSchema()) + .isInstanceOf(Schema.class); // Swagger Schema + + Schema inlineStringSchema = + (Schema) stringMessage.getPayload().getMultiFormatSchema().getSchema(); + assertThat(inlineStringSchema.getTypes()).containsExactly("string"); + + MessageObject fooMessage = (MessageObject) asyncAPI.getComponents() + .getMessages() + .get("io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(fooMessage.getPayload().getMultiFormatSchema().getSchema()) + .isInstanceOf(SchemaReference.class); + SchemaReference fooSchemaRef = (SchemaReference) + fooMessage.getPayload().getMultiFormatSchema().getSchema(); + assertThat(fooSchemaRef.getRef()) + .isEqualTo( + "#/components/schemas/io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + } + + @Test + void ensureThatStandaloneResultIsIdentical() { + // given + AsyncApiService asyncApiService = createStandaloneAsyncApiService(environment, basePackage); + + // when + AsyncAPI asyncApi = asyncApiService.getAsyncAPI(); + + // then + assertThat(asyncApi).isEqualTo(asyncApiService.getAsyncAPI()); + } + } + + @Nested + @SpringBootTest(classes = FqnApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.fqn", + "springwolf.use-fqn=false", + "springwolf.docket.payload-schema-format=openapi_v3_1" + }) + class FqnTest { + @Autowired + private AsyncApiService asyncApiService; + + @Autowired + private AsyncApiSerializerService asyncApiSerializerService; + + @Test + void allClassesHaveSimpleNameNotFullQualifiedTest() throws Exception { + AsyncAPI asyncAPI = asyncApiService.getAsyncAPI(); + assertThat(asyncAPI).isNotNull(); + String serialized = asyncApiSerializerService.toJsonString(asyncAPI); + + assertThat(serialized) + .contains( + "string", + "integer", + FqnApplication.Foo.class.getSimpleName(), + FqnApplication.Bar.class.getSimpleName(), + AsyncHeadersNotDocumented.NOT_DOCUMENTED.getTitle()) + .doesNotContain( + String.class.getName(), + Integer.class.getName(), + FqnApplication.Foo.class.getName(), + FqnApplication.Bar.class.getName()); + } + } + + @Nested + @SpringBootTest(classes = PolymorphicPayloadApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.polymorphic", + "springwolf.docket.payload-schema-format=openapi_v3_1" + }) + class PolymorphicPayloadTest { + @Autowired + private AsyncApiService asyncApiService; + + @Test + void polymorphicDiscriminatorFieldIsHandled() throws JsonProcessingException { + // when + AsyncAPI asyncAPI = asyncApiService.getAsyncAPI(); + // + // System.out.println(Json.mapper().writerWithDefaultPrettyPrinter().writeValueAsString(asyncAPI)); + + // then + Map messages = asyncAPI.getComponents().getMessages(); + assertThat(messages) + .containsOnlyKeys( + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Payload"); + Map schemas = asyncAPI.getComponents().getSchemas(); + assertThat(schemas) + .containsOnlyKeys( + "HeadersNotDocumented", + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Payload", + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Pet", + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Cat", + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Dog"); + + Schema petSchema = (Schema) schemas.get( + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Pet") + .getMultiFormatSchema() + .getSchema(); + + assertThat(petSchema.getDiscriminator().getPropertyName()).isEqualTo("type"); + assertThat(petSchema.getDiscriminator().getMapping()) + .containsAllEntriesOf( + Map.of( + "dog", + "#/components/schemas/io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Dog", + "cat", + "#/components/schemas/io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Cat")); + + Schema catSchema = (Schema) schemas.get( + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Cat") + .getMultiFormatSchema() + .getSchema(); + + assertThat(catSchema.getAllOf().get(0).get$ref()) + .isEqualTo( + "#/components/schemas/io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Pet"); + + assertThat(catSchema.getAllOf().get(1).getProperties()).containsOnlyKeys("catSpecificField"); + + Schema dogSchema = (Schema) schemas.get( + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Dog") + .getMultiFormatSchema() + .getSchema(); + + assertThat(dogSchema.getAllOf().get(0).get$ref()) + .isEqualTo( + "#/components/schemas/io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Pet"); + assertThat(dogSchema.getAllOf().get(1).getProperties()).containsOnlyKeys("dogSpecificField"); + } + } + + @Nested + @SpringBootTest(classes = SchemaEnumAsRefApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.schema", + "springwolf.docket.payload-schema-format=openapi_v3_1" + }) + class SchemaAsRefTest { + @Autowired + private AsyncApiService asyncApiService; + + @Test + void enumAsRefIsHandled() { + // given + String myEnumRootSchemaName = + "io.github.springwolf.core.integrationtests.application.schema.SchemaEnumAsRefApplication.Schemas.MyEnumRoot"; + String myEnumObjectSchemaName = + "io.github.springwolf.core.integrationtests.application.schema.SchemaEnumAsRefApplication.Schemas.MyEnumObject"; + + // when + AsyncAPI asyncAPI = asyncApiService.getAsyncAPI(); + + // then + Map messages = asyncAPI.getComponents().getMessages(); + assertThat(messages).containsOnlyKeys(myEnumRootSchemaName); + Map schemas = asyncAPI.getComponents().getSchemas(); + assertThat(schemas).containsOnlyKeys("HeadersNotDocumented", myEnumRootSchemaName, myEnumObjectSchemaName); + + assertThat(schemas.get(myEnumRootSchemaName).getMultiFormatSchema().getSchemaFormat()) + .isEqualTo(SchemaFormat.OPENAPI_V3_1.value); + Schema openapiSchema1 = (Schema) + schemas.get(myEnumRootSchemaName).getMultiFormatSchema().getSchema(); + + assertThat(openapiSchema1.getExample().toString()).isEqualTo("{\"myEnumObjectField\":\"DOG\"}"); + + assertThat(schemas.get(myEnumObjectSchemaName) + .getMultiFormatSchema() + .getSchemaFormat()) + .isEqualTo(SchemaFormat.OPENAPI_V3_1.value); + Schema openapiSchema2 = (Schema) + schemas.get(myEnumObjectSchemaName).getMultiFormatSchema().getSchema(); + + assertThat(openapiSchema2.getExample().toString()).isEqualTo("\"DOG\""); + } + } + + @Nested + @SpringBootTest(classes = ListenerApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.listener", + "springwolf.docket.payload-schema-format=openapi_v3_1", + "springwolf.docket.group-configs[0].group=FooMessage", + "springwolf.docket.group-configs[0].action-to-match=", + "springwolf.docket.group-configs[0].channel-name-to-match=", + "springwolf.docket.group-configs[0].message-name-to-match=.*Foo", + "springwolf.docket.group-configs[1].group=all & everything", + "springwolf.docket.group-configs[1].action-to-match=", + "springwolf.docket.group-configs[1].channel-name-to-match=.*", + "springwolf.docket.group-configs[1].message-name-to-match=", + }) + class GroupingTest { + @Autowired + private AsyncApiService asyncApiService; + + @Test + void shouldFindOnlyForGroupFoo() { + AsyncAPI asyncAPI = asyncApiService.getForGroupName("FooMessage").get(); + + assertThat(asyncAPI.getChannels().keySet()).containsExactlyInAnyOrder("listener-channel"); + assertThat(asyncAPI.getChannels().get("listener-channel").getMessages()) + .containsOnlyKeys( + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getOperations()) + .containsOnlyKeys("listener-channel_receive_listen3", "listener-channel_receive_listen4"); + assertThat(asyncAPI.getComponents().getMessages()) + .containsOnlyKeys( + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getComponents().getSchemas()) + .containsOnlyKeys( + "HeadersNotDocumented", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Bar", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + + MessageObject fooMessage = (MessageObject) asyncAPI.getComponents() + .getMessages() + .get("io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(fooMessage.getPayload().getMultiFormatSchema().getSchema()) + .isInstanceOf(SchemaReference.class); + SchemaReference fooSchemaRef = (SchemaReference) + fooMessage.getPayload().getMultiFormatSchema().getSchema(); + assertThat(fooSchemaRef.getRef()) + .isEqualTo( + "#/components/schemas/io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + } + + @Test + void shouldFindAllForGroupAll() { + // given + AsyncAPI fullApi = asyncApiService.getAsyncAPI(); + + // when + AsyncAPI asyncAPIOpt = + asyncApiService.getForGroupName("all & everything").get(); + + // then + + // String and Integer get filtered. + // Question: Why are they in the fullApi in the first place, if not referenced? (inline schema) + fullApi.getComponents().getSchemas().remove(String.class.getName()); + fullApi.getComponents().getSchemas().remove(Integer.class.getName()); + + assertThat(asyncAPIOpt).isEqualTo(fullApi); + } + } + + private AsyncApiService createStandaloneAsyncApiService(ConfigurableEnvironment environment, String basePackage) { + DefaultStandaloneApplication standaloneApplication = DefaultStandaloneApplication.builder() + .addScanPackage(basePackage) + .setEnvironment(environment) + .buildAndStart(); + return standaloneApplication.getAsyncApiService(); + } +} diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentWithOpenApiV3SchemaFormatIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentWithOpenApiV3SchemaFormatIntegrationTest.java new file mode 100644 index 000000000..1fd522761 --- /dev/null +++ b/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentWithOpenApiV3SchemaFormatIntegrationTest.java @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.integrationtests; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.github.springwolf.asyncapi.v3.jackson.AsyncApiSerializerService; +import io.github.springwolf.asyncapi.v3.model.AsyncAPI; +import io.github.springwolf.asyncapi.v3.model.channel.message.Message; +import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; +import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; +import io.github.springwolf.core.asyncapi.AsyncApiService; +import io.github.springwolf.core.asyncapi.scanners.common.headers.AsyncHeadersNotDocumented; +import io.github.springwolf.core.fixtures.MinimalIntegrationTestContextConfiguration; +import io.github.springwolf.core.integrationtests.application.fqn.FqnApplication; +import io.github.springwolf.core.integrationtests.application.listener.ListenerApplication; +import io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication; +import io.github.springwolf.core.integrationtests.application.publisher.PublisherApplication; +import io.github.springwolf.core.integrationtests.application.schema.SchemaEnumAsRefApplication; +import io.github.springwolf.core.standalone.DefaultStandaloneApplication; +import io.swagger.v3.oas.models.media.Schema; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.test.context.TestPropertySource; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class AsyncApiDocumentWithOpenApiV3SchemaFormatIntegrationTest { + + @Nested + @SpringBootTest(classes = ListenerApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.listener", + "springwolf.docket.payload-schema-format=openapi_v3" + }) + class ListenerAnnotationTest { + @Value("${springwolf.docket.base-package}") + private String basePackage; + + @Autowired + private ConfigurableEnvironment environment; + + @Autowired + private AsyncApiService asyncApiService; + + @Test + void asyncListenerAnnotationIsFound() { + AsyncAPI asyncAPI = asyncApiService.getAsyncAPI(); + assertThat(asyncAPI).isNotNull(); + + assertThat(asyncAPI.getChannels().keySet()) + .containsExactlyInAnyOrder("listener-channel", "listener-class-channel"); + assertThat(asyncAPI.getChannels().get("listener-channel").getMessages()) + .containsOnlyKeys( + "java.lang.String", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getChannels().get("listener-class-channel").getMessages()) + .containsOnlyKeys("java.lang.Integer"); + assertThat(asyncAPI.getOperations()) + .containsOnlyKeys( + "listener-channel_receive_listen", + "listener-channel_receive_listen2", + "listener-channel_receive_listen3", + "listener-channel_receive_listen4", + "listener-class-channel_receive_listen"); + assertThat(asyncAPI.getComponents().getMessages()) + .containsOnlyKeys( + "java.lang.String", + "java.lang.Integer", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getComponents().getSchemas()) + .containsOnlyKeys( + "HeadersNotDocumented", + "java.lang.String", + "java.lang.Integer", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Bar", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + + MessageObject stringMessage = + (MessageObject) asyncAPI.getComponents().getMessages().get("java.lang.String"); + assertThat(stringMessage.getPayload().getMultiFormatSchema().getSchema()) + .isInstanceOf(Schema.class); + Schema inlineStringSchema = + (Schema) stringMessage.getPayload().getMultiFormatSchema().getSchema(); + assertThat(inlineStringSchema.getType()).isEqualTo("string"); + assertThat(inlineStringSchema.getTypes()).containsExactly("string"); + + MessageObject fooMessage = (MessageObject) asyncAPI.getComponents() + .getMessages() + .get("io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(fooMessage.getPayload().getMultiFormatSchema().getSchema()) + .isInstanceOf(SchemaReference.class); + SchemaReference fooSchemaRef = (SchemaReference) + fooMessage.getPayload().getMultiFormatSchema().getSchema(); + assertThat(fooSchemaRef.getRef()) + .isEqualTo( + "#/components/schemas/io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + } + + @Test + void ensureThatStandaloneResultIsIdentical() { + // given + AsyncApiService asyncApiService = createStandaloneAsyncApiService(environment, basePackage); + + // when + AsyncAPI asyncApi = asyncApiService.getAsyncAPI(); + + // then + assertThat(asyncApi).isEqualTo(asyncApiService.getAsyncAPI()); + } + } + + @Nested + @SpringBootTest(classes = PublisherApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.publisher", + "springwolf.docket.payload-schema-format=openapi_v3" + }) + class PublisherAnnotationTest { + @Value("${springwolf.docket.base-package}") + private String basePackage; + + @Autowired + private ConfigurableEnvironment environment; + + @Autowired + private AsyncApiService asyncApiService; + + @Test + void asyncPublisherAnnotationIsFound() { + AsyncAPI asyncAPI = asyncApiService.getAsyncAPI(); + assertThat(asyncAPI).isNotNull(); + + assertThat(asyncAPI.getChannels().keySet()) + .containsExactlyInAnyOrder("publisher-channel", "publisher-class-channel"); + assertThat(asyncAPI.getChannels().get("publisher-channel").getMessages()) + .containsOnlyKeys( + "java.lang.String", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getChannels().get("publisher-class-channel").getMessages()) + .containsOnlyKeys("java.lang.Integer"); + assertThat(asyncAPI.getOperations()) + .containsOnlyKeys( + "publisher-channel_send_publish", + "publisher-channel_send_publish2", + "publisher-channel_send_publish3", + "publisher-channel_send_publish4", + "publisher-class-channel_send_publish"); + assertThat(asyncAPI.getComponents().getMessages()) + .containsOnlyKeys( + "java.lang.String", + "java.lang.Integer", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getComponents().getSchemas()) + .containsOnlyKeys( + "HeadersNotDocumented", + "java.lang.String", + "java.lang.Integer", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Bar", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + + MessageObject stringMessage = + (MessageObject) asyncAPI.getComponents().getMessages().get("java.lang.String"); + assertThat(stringMessage.getPayload().getMultiFormatSchema().getSchema()) + .isInstanceOf(Schema.class); // Swagger Schema + + Schema inlineStringSchema = + (Schema) stringMessage.getPayload().getMultiFormatSchema().getSchema(); + assertThat(inlineStringSchema.getType()).isEqualTo("string"); + assertThat(inlineStringSchema.getTypes()).containsExactly("string"); + + MessageObject fooMessage = (MessageObject) asyncAPI.getComponents() + .getMessages() + .get("io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(fooMessage.getPayload().getMultiFormatSchema().getSchema()) + .isInstanceOf(SchemaReference.class); + SchemaReference fooSchemaRef = (SchemaReference) + fooMessage.getPayload().getMultiFormatSchema().getSchema(); + assertThat(fooSchemaRef.getRef()) + .isEqualTo( + "#/components/schemas/io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + } + + @Test + void ensureThatStandaloneResultIsIdentical() { + // given + AsyncApiService asyncApiService = createStandaloneAsyncApiService(environment, basePackage); + + // when + AsyncAPI asyncApi = asyncApiService.getAsyncAPI(); + + // then + assertThat(asyncApi).isEqualTo(asyncApiService.getAsyncAPI()); + } + } + + @Nested + @SpringBootTest(classes = FqnApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.fqn", + "springwolf.use-fqn=false", + "springwolf.docket.payload-schema-format=openapi_v3" + }) + class FqnTest { + @Autowired + private AsyncApiService asyncApiService; + + @Autowired + private AsyncApiSerializerService asyncApiSerializerService; + + @Test + void allClassesHaveSimpleNameNotFullQualifiedTest() throws Exception { + AsyncAPI asyncAPI = asyncApiService.getAsyncAPI(); + assertThat(asyncAPI).isNotNull(); + String serialized = asyncApiSerializerService.toJsonString(asyncAPI); + + assertThat(serialized) + .contains( + "string", + "integer", + FqnApplication.Foo.class.getSimpleName(), + FqnApplication.Bar.class.getSimpleName(), + AsyncHeadersNotDocumented.NOT_DOCUMENTED.getTitle()) + .doesNotContain( + String.class.getName(), + Integer.class.getName(), + FqnApplication.Foo.class.getName(), + FqnApplication.Bar.class.getName()); + } + } + + @Nested + @SpringBootTest(classes = PolymorphicPayloadApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.polymorphic", + "springwolf.docket.payload-schema-format=openapi_v3" + }) + class PolymorphicPayloadTest { + @Autowired + private AsyncApiService asyncApiService; + + @Test + void polymorphicDiscriminatorFieldIsHandled() throws JsonProcessingException { + // when + AsyncAPI asyncAPI = asyncApiService.getAsyncAPI(); + + // then + Map messages = asyncAPI.getComponents().getMessages(); + assertThat(messages) + .containsOnlyKeys( + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Payload"); + Map schemas = asyncAPI.getComponents().getSchemas(); + assertThat(schemas) + .containsOnlyKeys( + "HeadersNotDocumented", + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Payload", + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Pet", + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Cat", + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Dog"); + + Schema petSchema = (Schema) schemas.get( + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Pet") + .getMultiFormatSchema() + .getSchema(); + + assertThat(petSchema.getDiscriminator().getPropertyName()).isEqualTo("type"); + assertThat(petSchema.getDiscriminator().getMapping()) + .containsAllEntriesOf( + Map.of( + "dog", + "#/components/schemas/io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Dog", + "cat", + "#/components/schemas/io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Cat")); + + Schema catSchema = (Schema) schemas.get( + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Cat") + .getMultiFormatSchema() + .getSchema(); + + assertThat(catSchema.getAllOf().get(0).get$ref()) + .isEqualTo( + "#/components/schemas/io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Pet"); + + assertThat(catSchema.getAllOf().get(1).getProperties()).containsOnlyKeys("catSpecificField"); + + Schema dogSchema = (Schema) schemas.get( + "io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Dog") + .getMultiFormatSchema() + .getSchema(); + + assertThat(dogSchema.getAllOf().get(0).get$ref()) + .isEqualTo( + "#/components/schemas/io.github.springwolf.core.integrationtests.application.polymorphic.PolymorphicPayloadApplication.Pet"); + assertThat(dogSchema.getAllOf().get(1).getProperties()).containsOnlyKeys("dogSpecificField"); + } + } + + @Nested + @SpringBootTest(classes = SchemaEnumAsRefApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.schema", + "springwolf.docket.payload-schema-format=openapi_v3" + }) + class SchemaAsRefTest { + @Autowired + private AsyncApiService asyncApiService; + + @Test + void enumAsRefIsHandled() { + // given + String myEnumRootSchemaName = + "io.github.springwolf.core.integrationtests.application.schema.SchemaEnumAsRefApplication.Schemas.MyEnumRoot"; + String myEnumObjectSchemaName = + "io.github.springwolf.core.integrationtests.application.schema.SchemaEnumAsRefApplication.Schemas.MyEnumObject"; + + // when + AsyncAPI asyncAPI = asyncApiService.getAsyncAPI(); + + // then + Map messages = asyncAPI.getComponents().getMessages(); + assertThat(messages).containsOnlyKeys(myEnumRootSchemaName); + Map schemas = asyncAPI.getComponents().getSchemas(); + assertThat(schemas).containsOnlyKeys("HeadersNotDocumented", myEnumRootSchemaName, myEnumObjectSchemaName); + + assertThat(schemas.get(myEnumRootSchemaName).getMultiFormatSchema().getSchemaFormat()) + .isEqualTo(SchemaFormat.OPENAPI_V3.value); + Schema openapiSchema1 = (Schema) + schemas.get(myEnumRootSchemaName).getMultiFormatSchema().getSchema(); + + assertThat(openapiSchema1.getExample().toString()).isEqualTo("{\"myEnumObjectField\":\"\\\"DOG\\\"\"}"); + + assertThat(schemas.get(myEnumObjectSchemaName) + .getMultiFormatSchema() + .getSchemaFormat()) + .isEqualTo(SchemaFormat.OPENAPI_V3.value); + Schema openapiSchema2 = (Schema) + schemas.get(myEnumObjectSchemaName).getMultiFormatSchema().getSchema(); + + assertThat(openapiSchema2.getExample().toString()).isEqualTo("\"DOG\""); + } + } + + @Nested + @SpringBootTest(classes = ListenerApplication.class) + @MinimalIntegrationTestContextConfiguration + @TestPropertySource( + properties = { + "springwolf.docket.base-package=io.github.springwolf.core.integrationtests.application.listener", + "springwolf.docket.payload-schema-format=openapi_v3", + "springwolf.docket.group-configs[0].group=FooMessage", + "springwolf.docket.group-configs[0].action-to-match=", + "springwolf.docket.group-configs[0].channel-name-to-match=", + "springwolf.docket.group-configs[0].message-name-to-match=.*Foo", + "springwolf.docket.group-configs[1].group=all & everything", + "springwolf.docket.group-configs[1].action-to-match=", + "springwolf.docket.group-configs[1].channel-name-to-match=.*", + "springwolf.docket.group-configs[1].message-name-to-match=", + }) + class GroupingTest { + @Autowired + private AsyncApiService asyncApiService; + + @Test + void shouldFindOnlyForGroupFoo() { + AsyncAPI asyncAPI = asyncApiService.getForGroupName("FooMessage").get(); + + assertThat(asyncAPI.getChannels().keySet()).containsExactlyInAnyOrder("listener-channel"); + assertThat(asyncAPI.getChannels().get("listener-channel").getMessages()) + .containsOnlyKeys( + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getOperations()) + .containsOnlyKeys("listener-channel_receive_listen3", "listener-channel_receive_listen4"); + assertThat(asyncAPI.getComponents().getMessages()) + .containsOnlyKeys( + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(asyncAPI.getComponents().getSchemas()) + .containsOnlyKeys( + "HeadersNotDocumented", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Bar", + "io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + + MessageObject fooMessage = (MessageObject) asyncAPI.getComponents() + .getMessages() + .get("io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + assertThat(fooMessage.getPayload().getMultiFormatSchema().getSchema()) + .isInstanceOf(SchemaReference.class); + SchemaReference fooSchemaRef = (SchemaReference) + fooMessage.getPayload().getMultiFormatSchema().getSchema(); + assertThat(fooSchemaRef.getRef()) + .isEqualTo( + "#/components/schemas/io.github.springwolf.core.integrationtests.application.listener.ListenerApplication.Foo"); + } + + @Test + void shouldFindAllForGroupAll() { + // given + AsyncAPI fullApi = asyncApiService.getAsyncAPI(); + + // when + AsyncAPI asyncAPIOpt = + asyncApiService.getForGroupName("all & everything").get(); + + // then + + // String and Integer get filtered. + // Question: Why are they in the fullApi in the first place, if not referenced? (inline schema) + fullApi.getComponents().getSchemas().remove(String.class.getName()); + fullApi.getComponents().getSchemas().remove(Integer.class.getName()); + + assertThat(asyncAPIOpt).isEqualTo(fullApi); + } + } + + private AsyncApiService createStandaloneAsyncApiService(ConfigurableEnvironment environment, String basePackage) { + DefaultStandaloneApplication standaloneApplication = DefaultStandaloneApplication.builder() + .addScanPackage(basePackage) + .setEnvironment(environment) + .buildAndStart(); + return standaloneApplication.getAsyncApiService(); + } +} diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/application/polymorphic/PolymorphicPayloadApplication.java b/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/application/polymorphic/PolymorphicPayloadApplication.java index 294ea91f2..83b246b7a 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/application/polymorphic/PolymorphicPayloadApplication.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/application/polymorphic/PolymorphicPayloadApplication.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo; import io.github.springwolf.core.asyncapi.annotations.AsyncListener; import io.github.springwolf.core.asyncapi.annotations.AsyncOperation; +import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; import io.swagger.v3.oas.annotations.media.Schema; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; @@ -23,13 +24,20 @@ public void listen(Payload payload) {} public record Payload(Pet pet) {} - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type") @JsonSubTypes( value = { @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat"), }) - @Schema(oneOf = {Dog.class, Cat.class}) + @Schema( + discriminatorProperty = "type", + discriminatorMapping = { + @DiscriminatorMapping(value = "dog", schema = Dog.class), + @DiscriminatorMapping(value = "cat", schema = Cat.class) + }, + oneOf = {Dog.class, Cat.class}, + subTypes = {Dog.class, Cat.class}) public interface Pet { String getType(); } diff --git a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java new file mode 100644 index 000000000..18fcca2bd --- /dev/null +++ b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.examples.jms; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.test.context.TestPropertySource; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest( + classes = {SpringwolfJmsExampleApplication.class}, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ExtendWith({JmsTestContainerExtension.class}) +@TestPropertySource(properties = {"springwolf.docket.payload-schema-format=openapi_v3_1"}) +public class ApiWithOpenApiV31SchemaFormatIntegrationTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Value("${server.port}") + public Integer serverPort; + + @Test + void asyncApiResourceArtifactTest() throws Exception { + String url = "/springwolf/docs"; + String actual = restTemplate.getForObject(url, String.class); + String actualPatched = actual.replace("localhost:61616", "activemq:61616"); + Files.writeString(Path.of("src", "test", "resources", "asyncapi.actual.json"), actualPatched); + + InputStream s = this.getClass().getResourceAsStream("/asyncapi.openapiv3.json"); + String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8).trim(); + + assertThat(actualPatched).isEqualTo(expected); + } +} diff --git a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java new file mode 100644 index 000000000..0b2c2df86 --- /dev/null +++ b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.examples.jms; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.test.context.TestPropertySource; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest( + classes = {SpringwolfJmsExampleApplication.class}, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ExtendWith({JmsTestContainerExtension.class}) +@TestPropertySource(properties = {"springwolf.docket.payload-schema-format=openapi_v3"}) +public class ApiWithOpenApiV3SchemaFormatIntegrationTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Value("${server.port}") + public Integer serverPort; + + @Test + void asyncApiResourceArtifactTest() throws Exception { + String url = "/springwolf/docs"; + String actual = restTemplate.getForObject(url, String.class); + String actualPatched = actual.replace("localhost:61616", "activemq:61616"); + Files.writeString(Path.of("src", "test", "resources", "asyncapi.actual.json"), actualPatched); + + InputStream s = this.getClass().getResourceAsStream("/asyncapi.openapiv3.json"); + String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8).trim(); + + assertThat(actualPatched).isEqualTo(expected); + } +} diff --git a/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv3.json b/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv3.json new file mode 100644 index 000000000..f74ed3f93 --- /dev/null +++ b/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv3.json @@ -0,0 +1,224 @@ +{ + "asyncapi": "3.0.0", + "info": { + "title": "Springwolf example project - JMS", + "version": "1.0.0", + "description": "Springwolf [example project](https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-jms-example) to demonstrate springwolfs abilities, including **markdown** support for descriptions.", + "termsOfService": "http://asyncapi.org/terms", + "contact": { + "name": "springwolf", + "url": "https://github.com/springwolf/springwolf-core", + "email": "example@example.com" + }, + "license": { + "name": "Apache License 2.0" + }, + "x-generator": "springwolf" + }, + "defaultContentType": "application/json", + "servers": { + "jms-server": { + "host": "tcp://activemq:61616", + "protocol": "jms" + } + }, + "channels": { + "another-queue": { + "address": "another-queue", + "messages": { + "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" + } + }, + "bindings": { + "jms": { + "bindingVersion": "0.0.1" + } + } + }, + "example-queue": { + "address": "example-queue", + "messages": { + "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" + } + }, + "bindings": { + "jms": { + "bindingVersion": "0.0.1" + } + } + } + }, + "components": { + "schemas": { + "HeadersNotDocumented": { + "title": "HeadersNotDocumented", + "type": "object", + "properties": { }, + "description": "There can be headers, but they are not explicitly documented.", + "examples": [ + { } + ] + }, + "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.0.0", + "schema": { + "description": "Another payload model", + "example": { + "example": { + "someEnum": "FOO2", + "someLong": 5, + "someString": "some string value" + }, + "foo": "bar" + }, + "properties": { + "example": { + "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" + }, + "foo": { + "type": "string", + "description": "Foo field", + "example": "bar", + "maxLength": 100 + } + }, + "required": [ + "example" + ], + "title": "AnotherPayloadDto" + } + }, + "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.0.0", + "schema": { + "description": "Example payload model", + "example": { + "someEnum": "FOO2", + "someLong": 5, + "someString": "some string value" + }, + "properties": { + "someEnum": { + "type": "string", + "description": "Some enum field", + "enum": [ + "FOO1", + "FOO2", + "FOO3" + ], + "example": "FOO2" + }, + "someLong": { + "type": "integer", + "format": "int64", + "description": "Some long field", + "example": 5, + "minimum": 0 + }, + "someString": { + "type": "string", + "description": "Some string field", + "example": "some string value" + } + }, + "required": [ + "someEnum", + "someString" + ], + "title": "ExamplePayloadDto" + } + } + }, + "messages": { + "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { + "headers": { + "$ref": "#/components/schemas/HeadersNotDocumented" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" + } + }, + "name": "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto", + "title": "AnotherPayloadDto", + "bindings": { + "jms": { + "bindingVersion": "0.0.1" + } + } + }, + "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { + "headers": { + "$ref": "#/components/schemas/HeadersNotDocumented" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" + } + }, + "name": "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto", + "title": "ExamplePayloadDto", + "bindings": { + "jms": { + "bindingVersion": "0.0.1" + } + } + } + } + }, + "operations": { + "another-queue_receive_receiveAnotherPayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/another-queue" + }, + "bindings": { + "jms": { } + }, + "messages": [ + { + "$ref": "#/channels/another-queue/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" + } + ] + }, + "another-queue_send_sendMessage": { + "action": "send", + "channel": { + "$ref": "#/channels/another-queue" + }, + "title": "another-queue_send", + "description": "Custom, optional description defined in the AsyncPublisher annotation", + "bindings": { + "jms": { + "internal-field": "customValue", + "nested": { + "key": "nestedValue" + } + } + }, + "messages": [ + { + "$ref": "#/channels/another-queue/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" + } + ] + }, + "example-queue_receive_receiveExamplePayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/example-queue" + }, + "bindings": { + "jms": { } + }, + "messages": [ + { + "$ref": "#/channels/example-queue/messages/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" + } + ] + } + } +} \ No newline at end of file diff --git a/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json b/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json new file mode 100644 index 000000000..131f55476 --- /dev/null +++ b/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json @@ -0,0 +1,224 @@ +{ + "asyncapi": "3.0.0", + "info": { + "title": "Springwolf example project - JMS", + "version": "1.0.0", + "description": "Springwolf [example project](https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-jms-example) to demonstrate springwolfs abilities, including **markdown** support for descriptions.", + "termsOfService": "http://asyncapi.org/terms", + "contact": { + "name": "springwolf", + "url": "https://github.com/springwolf/springwolf-core", + "email": "example@example.com" + }, + "license": { + "name": "Apache License 2.0" + }, + "x-generator": "springwolf" + }, + "defaultContentType": "application/json", + "servers": { + "jms-server": { + "host": "tcp://activemq:61616", + "protocol": "jms" + } + }, + "channels": { + "another-queue": { + "address": "another-queue", + "messages": { + "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" + } + }, + "bindings": { + "jms": { + "bindingVersion": "0.0.1" + } + } + }, + "example-queue": { + "address": "example-queue", + "messages": { + "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" + } + }, + "bindings": { + "jms": { + "bindingVersion": "0.0.1" + } + } + } + }, + "components": { + "schemas": { + "HeadersNotDocumented": { + "title": "HeadersNotDocumented", + "type": "object", + "properties": { }, + "description": "There can be headers, but they are not explicitly documented.", + "examples": [ + { } + ] + }, + "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "description": "Another payload model", + "example": { + "example": { + "someEnum": "FOO2", + "someLong": 5, + "someString": "some string value" + }, + "foo": "bar" + }, + "properties": { + "example": { + "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" + }, + "foo": { + "type": "string", + "description": "Foo field", + "example": "bar", + "maxLength": 100 + } + }, + "required": [ + "example" + ], + "title": "AnotherPayloadDto" + } + }, + "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "description": "Example payload model", + "example": { + "someEnum": "FOO2", + "someLong": 5, + "someString": "some string value" + }, + "properties": { + "someEnum": { + "type": "string", + "description": "Some enum field", + "enum": [ + "FOO1", + "FOO2", + "FOO3" + ], + "example": "FOO2" + }, + "someLong": { + "type": "integer", + "format": "int64", + "description": "Some long field", + "example": 5, + "minimum": 0 + }, + "someString": { + "type": "string", + "description": "Some string field", + "example": "some string value" + } + }, + "required": [ + "someEnum", + "someString" + ], + "title": "ExamplePayloadDto" + } + } + }, + "messages": { + "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { + "headers": { + "$ref": "#/components/schemas/HeadersNotDocumented" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.1.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" + } + }, + "name": "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto", + "title": "AnotherPayloadDto", + "bindings": { + "jms": { + "bindingVersion": "0.0.1" + } + } + }, + "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { + "headers": { + "$ref": "#/components/schemas/HeadersNotDocumented" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.1.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" + } + }, + "name": "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto", + "title": "ExamplePayloadDto", + "bindings": { + "jms": { + "bindingVersion": "0.0.1" + } + } + } + } + }, + "operations": { + "another-queue_receive_receiveAnotherPayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/another-queue" + }, + "bindings": { + "jms": { } + }, + "messages": [ + { + "$ref": "#/channels/another-queue/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" + } + ] + }, + "another-queue_send_sendMessage": { + "action": "send", + "channel": { + "$ref": "#/channels/another-queue" + }, + "title": "another-queue_send", + "description": "Custom, optional description defined in the AsyncPublisher annotation", + "bindings": { + "jms": { + "internal-field": "customValue", + "nested": { + "key": "nestedValue" + } + } + }, + "messages": [ + { + "$ref": "#/channels/another-queue/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" + } + ] + }, + "example-queue_receive_receiveExamplePayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/example-queue" + }, + "bindings": { + "jms": { } + }, + "messages": [ + { + "$ref": "#/channels/example-queue/messages/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" + } + ] + } + } +} \ No newline at end of file From 0e45567e19b29a7bfae48f7dee5aa9e4e1d31e14 Mon Sep 17 00:00:00 2001 From: tvahrst Date: Sun, 2 Nov 2025 15:30:24 +0100 Subject: [PATCH 03/14] chore: spotless --- .../springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java index d2cb48776..3c1b2b5e9 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java @@ -62,8 +62,8 @@ public ComponentSchema mapSchemaOrRef(Schema swaggerSchema, SchemaFormat schemaF */ public ComponentSchema mapSchema(Schema swaggerSchema, SchemaFormat schemaFormat) { return switch (schemaFormat) { - case OPENAPI_V3, OPENAPI_V3_1 -> ComponentSchema.of( - new MultiFormatSchema(schemaFormat.value, swaggerSchema)); + case OPENAPI_V3, OPENAPI_V3_1 -> + ComponentSchema.of(new MultiFormatSchema(schemaFormat.value, swaggerSchema)); case ASYNCAPI_V3 -> ComponentSchema.of(mapSwaggerSchemaToAsyncApiSchema(swaggerSchema)); default -> throw new IllegalArgumentException("SchemaFormat " + schemaFormat + " is not supported"); }; From 3122687b4b2d531c5c209ebc41d2d3d6519d4a2a Mon Sep 17 00:00:00 2001 From: tvahrst Date: Sun, 2 Nov 2025 15:54:44 +0100 Subject: [PATCH 04/14] chore: javadoc --- .../core/configuration/properties/PayloadSchemaFormat.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/PayloadSchemaFormat.java b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/PayloadSchemaFormat.java index 748a2d17c..3022a4bcc 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/PayloadSchemaFormat.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/PayloadSchemaFormat.java @@ -3,6 +3,9 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; +/** + * Enumeration defining the supported payload schema formats, for use in SpringwolfConfigProperties. + */ public enum PayloadSchemaFormat { ASYNCAPI_V3(SchemaFormat.ASYNCAPI_V3), OPENAPI_V3(SchemaFormat.OPENAPI_V3), From 109f50ec18105117d0c1a7c959bf9ee00f5752b3 Mon Sep 17 00:00:00 2001 From: tvahrst Date: Sun, 2 Nov 2025 16:01:26 +0100 Subject: [PATCH 05/14] chore: spotless --- .../springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java index 3c1b2b5e9..d2cb48776 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java @@ -62,8 +62,8 @@ public ComponentSchema mapSchemaOrRef(Schema swaggerSchema, SchemaFormat schemaF */ public ComponentSchema mapSchema(Schema swaggerSchema, SchemaFormat schemaFormat) { return switch (schemaFormat) { - case OPENAPI_V3, OPENAPI_V3_1 -> - ComponentSchema.of(new MultiFormatSchema(schemaFormat.value, swaggerSchema)); + case OPENAPI_V3, OPENAPI_V3_1 -> ComponentSchema.of( + new MultiFormatSchema(schemaFormat.value, swaggerSchema)); case ASYNCAPI_V3 -> ComponentSchema.of(mapSwaggerSchemaToAsyncApiSchema(swaggerSchema)); default -> throw new IllegalArgumentException("SchemaFormat " + schemaFormat + " is not supported"); }; From 3c2e3dc2b6c771f9c8d1c8593ae75556dbaeb586 Mon Sep 17 00:00:00 2001 From: tvahrst Date: Sun, 2 Nov 2025 16:23:29 +0100 Subject: [PATCH 06/14] fix: tests --- .../src/test/resources/asyncapi.openapiv31.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json b/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json index 131f55476..4d3a8ae66 100644 --- a/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json +++ b/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json @@ -64,6 +64,7 @@ "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", "schema": { + "type": "object", "description": "Another payload model", "example": { "example": { @@ -75,7 +76,8 @@ }, "properties": { "example": { - "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" + "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto", + "description": "Example field" }, "foo": { "type": "string", @@ -93,6 +95,7 @@ "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", "schema": { + "type": "object", "description": "Example payload model", "example": { "someEnum": "FOO2", @@ -137,7 +140,7 @@ "$ref": "#/components/schemas/HeadersNotDocumented" }, "payload": { - "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.1.0", + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", "schema": { "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" } @@ -155,7 +158,7 @@ "$ref": "#/components/schemas/HeadersNotDocumented" }, "payload": { - "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.1.0", + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", "schema": { "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" } From ad80cfadc1c9cc9b073f960a6dda0a7488384efe Mon Sep 17 00:00:00 2001 From: tvahrst Date: Sun, 2 Nov 2025 16:42:49 +0100 Subject: [PATCH 07/14] fix: tests --- .../jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java index 18fcca2bd..2096fd887 100644 --- a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java +++ b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java @@ -36,7 +36,7 @@ void asyncApiResourceArtifactTest() throws Exception { String actualPatched = actual.replace("localhost:61616", "activemq:61616"); Files.writeString(Path.of("src", "test", "resources", "asyncapi.actual.json"), actualPatched); - InputStream s = this.getClass().getResourceAsStream("/asyncapi.openapiv3.json"); + InputStream s = this.getClass().getResourceAsStream("/asyncapi.openapiv31.json"); String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8).trim(); assertThat(actualPatched).isEqualTo(expected); From f3b6093c087fbdfb8b9596e77897b59d0c1ea744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller?= Date: Fri, 7 Nov 2025 18:12:06 +0100 Subject: [PATCH 08/14] chore(core): move wrapper logic for MultiFormatSchema to MultiFormatSchema, change back public api method from registerSimpleSchema to registerSchema to prevent breaking change, rename SwaggerSchemaService.postProcessSimpleSchema to postProcessSchemaWithoutRef to clarify the intention of the method Co-authored-by: Timon Back --- .../v3/model/schema/MultiFormatSchema.java | 7 +++++ .../components/ComponentsService.java | 4 +-- .../components/DefaultComponentsService.java | 31 +++++++------------ .../AsyncAnnotationMessageService.java | 10 ++---- .../SpringAnnotationMessageService.java | 2 +- .../SpringAnnotationMessagesService.java | 2 +- .../payload/internal/PayloadService.java | 2 +- .../schemas/SwaggerSchemaService.java | 16 +++++----- .../AsyncAnnotationMessageServiceTest.java | 2 +- .../PayloadAsyncOperationServiceTest.java | 2 +- .../CloudStreamFunctionChannelsScanner.java | 2 +- .../CloudStreamFunctionOperationsScanner.java | 2 +- ...pringwolfJmsControllerIntegrationTest.java | 2 +- ...ingwolfKafkaControllerIntegrationTest.java | 2 +- 14 files changed, 41 insertions(+), 45 deletions(-) diff --git a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/MultiFormatSchema.java b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/MultiFormatSchema.java index 91913f8fd..885f03b85 100644 --- a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/MultiFormatSchema.java +++ b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/MultiFormatSchema.java @@ -49,4 +49,11 @@ public class MultiFormatSchema extends ExtendableObject { */ @JsonProperty(value = "schema") private Object schema; + + public static MultiFormatSchema of(Object schema) { + // if payloadSchema.payload is already an instance of MultiFormatSchema, do not wrap again. + return (schema instanceof MultiFormatSchema mfs) + ? mfs + : MultiFormatSchema.builder().schema(schema).build(); + } } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java index e7b003d49..7010198b1 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java @@ -41,10 +41,10 @@ public interface ComponentsService { /** * registers the given schema with this {@link ComponentsService} - * @param headers the schema to register, typically a header schema + * @param schemaWithoutRef the schema to register, typically a header schema * @return the title attribute of the given schema */ - String registerSimpleSchema(SchemaObject headers); + String registerSchema(SchemaObject schemaWithoutRef); /** * Provides a map of all registered messages. diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java index f327d66c8..5e9f670ba 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java @@ -8,8 +8,8 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; -import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; +import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Type; @@ -23,10 +23,11 @@ * in the resulting AsyncApi object. */ @Slf4j +@AllArgsConstructor public class DefaultComponentsService implements ComponentsService { private final SwaggerSchemaService schemaService; - private final SchemaFormat payloadSchemaFormat; + private final SpringwolfConfigProperties springwolfConfigProperties; /** * maps a schema name (key) to a detected corresponding {@link ComponentSchema}. @@ -35,15 +36,6 @@ public class DefaultComponentsService implements ComponentsService { private final Map messages = new HashMap<>(); - public DefaultComponentsService( - SwaggerSchemaService schemaService, SpringwolfConfigProperties springwolfConfigProperties) { - this.schemaService = schemaService; - - PayloadSchemaFormat payloadSchemaFormat = - springwolfConfigProperties.getDocket().getPayloadSchemaFormat(); - this.payloadSchemaFormat = payloadSchemaFormat.getSchemaFormat(); - } - /** * Provides a map of all registered schemas. * @@ -64,7 +56,8 @@ public Map getSchemas() { */ @Override public ComponentSchema resolvePayloadSchema(Type type, String contentType) { - + SchemaFormat payloadSchemaFormat = + springwolfConfigProperties.getDocket().getPayloadSchemaFormat().getSchemaFormat(); SwaggerSchemaService.ExtractedSchemas payload = schemaService.resolveSchema(type, contentType, payloadSchemaFormat); payload.referencedSchemas().forEach(schemas::putIfAbsent); @@ -75,21 +68,21 @@ public ComponentSchema resolvePayloadSchema(Type type, String contentType) { * registers the given schema with this {@link ComponentsService}. *

NOTE

* Use only with schemas with max. one level of properties. Providing {@link SchemaObject}s with deep - * property hierarchy will result in an corrupted result. + * property hierarchy will result in a corrupted result. *
* A typical usecase for this method is registering of header schemas, which have typically a simple structure. * - * @param headers the schema to register, typically a header schema + * @param schemaWithoutRef the schema to register, typically a header schema * @return the title attribute of the given schema */ @Override - public String registerSimpleSchema(SchemaObject headers) { - log.debug("Registering schema for {}", headers.getTitle()); + public String registerSchema(SchemaObject schemaWithoutRef) { + log.debug("Registering schema for {}", schemaWithoutRef.getTitle()); - ComponentSchema processedSchema = schemaService.postProcessSimpleSchema(headers); - this.schemas.putIfAbsent(headers.getTitle(), processedSchema); + ComponentSchema processedSchema = schemaService.postProcessSchemaWithoutRef(schemaWithoutRef); + this.schemas.putIfAbsent(schemaWithoutRef.getTitle(), processedSchema); - return headers.getTitle(); + return schemaWithoutRef.getTitle(); } /** diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java index 661c202f6..1662c8efd 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java @@ -37,17 +37,13 @@ public MessageObject buildMessage(AsyncOperation operationData, Method method) { PayloadSchemaObject payloadSchema = payloadAsyncOperationService.extractSchema(operationData, method); SchemaObject headerSchema = AsyncAnnotationUtil.getAsyncHeaders(operationData, stringValueResolver); - String headerSchemaName = this.componentsService.registerSimpleSchema(headerSchema); + String headerSchemaName = this.componentsService.registerSchema(headerSchema); Map messageBinding = AsyncAnnotationUtil.processMessageBindingFromAnnotation(method, messageBindingProcessors); - // if payloadSchema.payload is already an instance of MultiFormatSchema, do not wrap again. - MultiFormatSchema multiFormatSchema = (payloadSchema.payload() instanceof MultiFormatSchema mfs) - ? mfs - : MultiFormatSchema.builder().schema(payloadSchema.payload()).build(); - - var messagePayload = MessagePayload.of(multiFormatSchema); + MultiFormatSchema multiFormatSchema = MultiFormatSchema.of(payloadSchema.payload()); + MessagePayload messagePayload = MessagePayload.of(multiFormatSchema); var builder = MessageObject.builder() .messageId(payloadSchema.name()) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessageService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessageService.java index 1db1f9f78..f06fc8e3d 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessageService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessageService.java @@ -31,7 +31,7 @@ public MessageObject buildMessage( MethodAnnotation annotation, PayloadSchemaObject payloadSchema, SchemaObject headers) { SchemaObject headerSchema = asyncHeadersBuilder.buildHeaders(payloadSchema); SchemaObject mergedHeaderSchema = HeaderSchemaObjectMerger.merge(headerSchema, headers); - String headerModelName = componentsService.registerSimpleSchema(mergedHeaderSchema); + String headerModelName = componentsService.registerSchema(mergedHeaderSchema); Map messageBinding = bindingFactory.buildMessageBinding(annotation, mergedHeaderSchema); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessagesService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessagesService.java index f386a36a5..6bc56308a 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessagesService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessagesService.java @@ -62,7 +62,7 @@ private MessageObject buildMessage(ClassAnnotation classAnnotation, Method metho SchemaObject headerSchema = asyncHeadersBuilder.buildHeaders(payloadSchema); SchemaObject headers = headerClassExtractor.extractHeader(method, payloadSchema); SchemaObject mergedHeaderSchema = HeaderSchemaObjectMerger.merge(headerSchema, headers); - String headerSchemaName = componentsService.registerSimpleSchema(mergedHeaderSchema); + String headerSchemaName = componentsService.registerSchema(mergedHeaderSchema); Map messageBinding = bindingFactory.buildMessageBinding(classAnnotation, headerSchema); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/internal/PayloadService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/internal/PayloadService.java index 29742415d..21cb3efd6 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/internal/PayloadService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/internal/PayloadService.java @@ -55,7 +55,7 @@ public PayloadSchemaObject buildSchema(String contentType, Type payloadType) { public PayloadSchemaObject useUnusedPayload() { ComponentSchema schema = PAYLOAD_NOT_USED.schema(); if (schema != null && schema.getSchema() != null) { - this.componentsService.registerSimpleSchema(schema.getSchema()); + this.componentsService.registerSchema(schema.getSchema()); } return PAYLOAD_NOT_USED; } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java index fe03f952d..391cd46d6 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java @@ -64,27 +64,27 @@ public record ExtractedSchemas(ComponentSchema rootSchema, MapNOTE

* The conversion between the AsyncApi {@link SchemaObject} and Swagger schema instance is not a 'full conversion'. Only - * root attributes of the schema an the first level of properties a converted. Providing {@link SchemaObject}s with deep - * property hierarchy will result in an corrupted result. + * root attributes of the schema and the first level of properties are converted. Providing {@link SchemaObject}s with deep + * property hierarchy will result in a corrupted result. *
* A typical usecase for this method is postprocessing of header schemas, which have typically a simple structure. * - * @param headers + * @param schemaWithoutRef * @return */ - public ComponentSchema postProcessSimpleSchema(SchemaObject headers) { - String schemaName = headers.getTitle(); + public ComponentSchema postProcessSchemaWithoutRef(SchemaObject schemaWithoutRef) { + String schemaName = schemaWithoutRef.getTitle(); // create a swagger schema to invoke the postprocessors. Copy attributes vom headers to (Swagger) headerSchema ObjectSchema headerSchema = new ObjectSchema(); headerSchema.setName(schemaName); - headerSchema.setTitle(headers.getTitle()); - headerSchema.setDescription(headers.getDescription()); + headerSchema.setTitle(schemaWithoutRef.getTitle()); + headerSchema.setDescription(schemaWithoutRef.getDescription()); // transform properties of headers to a properties Map of Swagger schemas. // (Only one level, no deep transformation, see SwaggerSchemaUtil#mapToSwagger) // - Map properties = headers.getProperties().entrySet().stream() + Map properties = schemaWithoutRef.getProperties().entrySet().stream() .map((property) -> Map.entry(property.getKey(), (Schema) swaggerSchemaUtil.mapToSwagger(property.getValue()))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageServiceTest.java index 96f30cf22..3f4446ca9 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageServiceTest.java @@ -48,7 +48,7 @@ void setUp() { .when(stringValueResolver) .resolveStringValue(any()); - when(componentsService.registerSimpleSchema(any())).thenReturn("headerSchemaName"); + when(componentsService.registerSchema(any())).thenReturn("headerSchemaName"); when(messageBindingProcessor.process(any())).thenReturn(Optional.empty()); when(payloadAsyncOperationService.extractSchema(any(), any())).thenReturn(payloadSchema); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadAsyncOperationServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadAsyncOperationServiceTest.java index 0f1a9984e..f157527b4 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadAsyncOperationServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadAsyncOperationServiceTest.java @@ -111,6 +111,6 @@ void shouldReturnPayloadNotUsed() { SchemaObject schema = result.schema().getSchema(); assertThat(schema.getTitle()).isEqualTo("PayloadNotUsed"); assertThat(schema.getDescription()).isEqualTo("No payload specified"); - verify(componentsService).registerSimpleSchema(PAYLOAD_NOT_USED.schema().getSchema()); + verify(componentsService).registerSchema(PAYLOAD_NOT_USED.schema().getSchema()); } } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java index 10ff9ea92..2310995aa 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScanner.java @@ -88,7 +88,7 @@ private ChannelObject buildChannel(FunctionalChannelBeanData beanData, String ch var messagePayload = MessagePayload.of( MultiFormatSchema.builder().schema(payloadSchema.payload()).build()); - String headerModelName = componentsService.registerSimpleSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED); + String headerModelName = componentsService.registerSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED); MessageHeaders messageHeaders = MessageHeaders.of(SchemaReference.toSchema(headerModelName)); Map messageBinding = buildMessageBinding(beanData.annotatedElement()); diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java index 01d2f3c10..f15ccca4d 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java @@ -85,7 +85,7 @@ private Operation buildOperation(FunctionalChannelBeanData beanData) { MessagePayload payload = MessagePayload.of( MultiFormatSchema.builder().schema(payloadSchema.payload()).build()); - String headerModelName = componentsService.registerSimpleSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED); + String headerModelName = componentsService.registerSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED); MessageHeaders messageHeaders = MessageHeaders.of(SchemaReference.toSchema(headerModelName)); MessageObject message = MessageObject.builder() diff --git a/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java b/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java index 89990a487..a5ba0e3a3 100644 --- a/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java +++ b/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java @@ -94,7 +94,7 @@ class SpringwolfJmsControllerIntegrationTest { void setup() { when(springwolfJmsProducer.isEnabled()).thenReturn(true); - componentsService.registerSimpleSchema(SchemaObject.builder() + componentsService.registerSchema(SchemaObject.builder() .title(PayloadDto.class.getName()) .properties(new HashMap<>()) .build()); diff --git a/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java b/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java index 1b6e2c240..991777d39 100644 --- a/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java +++ b/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java @@ -94,7 +94,7 @@ class SpringwolfKafkaControllerIntegrationTest { void setup() { when(springwolfKafkaProducer.isEnabled()).thenReturn(true); - componentsService.registerSimpleSchema(SchemaObject.builder() + componentsService.registerSchema(SchemaObject.builder() .title(PayloadDto.class.getName()) .properties(new HashMap<>()) .build()); From ca1b83d3540efaf63a4c2cc9c9656401cfa20e15 Mon Sep 17 00:00:00 2001 From: tvahrst Date: Sun, 9 Nov 2025 15:12:45 +0100 Subject: [PATCH 09/14] chore: ModelConvertersProvider and some minor enhancements. --- .../schemas/ModelConvertersProvider.java | 51 ++++++++++++++++++ .../schemas/SwaggerSchemaService.java | 25 ++------- .../SpringwolfCoreConfiguration.java | 13 +++-- .../schemas/SwaggerSchemaServiceTest.java | 53 ++++++++++++++----- ...OpenApiV31SchemaFormatIntegrationTest.java | 2 +- ...hOpenApiV3SchemaFormatIntegrationTest.java | 2 +- 6 files changed, 108 insertions(+), 38 deletions(-) create mode 100644 springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java new file mode 100644 index 000000000..f6f2cc66b --- /dev/null +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java @@ -0,0 +1,51 @@ +package io.github.springwolf.core.asyncapi.schemas; + +import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; +import io.swagger.v3.core.converter.ModelConverter; +import io.swagger.v3.core.converter.ModelConverters; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.InitializingBean; + +import java.util.List; + +/** + * Provides fully prepared {@link io.swagger.v3.core.converter.ModelConverters} for openapi v3 and v3.1 + * requirements. + */ +@RequiredArgsConstructor +public class ModelConvertersProvider implements InitializingBean { + + private final List externalModelConverters; + + private ModelConverters converter_openapi30; + private ModelConverters converter_openapi31; + + + /** + * provides the appropriate {@link ModelConverters} for the given {@link SchemaFormat}. + * + * @param schemaFormat the schemaFormat that shall be generated. + * @return the appropriate {@link ModelConverters} or {@link IllegalArgumentException} if the given format is not supported. + */ + public ModelConverters getModelConverterForSchemaFormat(SchemaFormat schemaFormat) { + return switch (schemaFormat) { + case ASYNCAPI_V3 -> converter_openapi30; + case OPENAPI_V3 -> converter_openapi30; + case OPENAPI_V3_1 -> converter_openapi31; + default -> throw new IllegalArgumentException("SchemaFormat not supported: " + schemaFormat); + }; + } + + + /** + * initializes instance variables after all constructor properties are set. + */ + @Override + public void afterPropertiesSet() { + converter_openapi30 = new ModelConverters(false); + converter_openapi31 = new ModelConverters(true); + externalModelConverters.forEach(converter_openapi30::addConverter); + externalModelConverters.forEach(converter_openapi31::addConverter); + } + +} diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java index 391cd46d6..ee0e4d769 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java @@ -19,6 +19,7 @@ import io.swagger.v3.core.util.RefUtils; import io.swagger.v3.oas.models.media.ObjectSchema; import io.swagger.v3.oas.models.media.Schema; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -35,26 +36,14 @@ import static io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties.ConfigDocket.DEFAULT_CONTENT_TYPE; @Slf4j +@RequiredArgsConstructor public class SwaggerSchemaService { - private final ModelConverters converter_openapi30 = ModelConverters.getInstance(false); - private final ModelConverters converter_openapi31 = ModelConverters.getInstance(true); private final List schemaPostProcessors; private final SwaggerSchemaUtil swaggerSchemaUtil; private final SpringwolfConfigProperties properties; + private final ModelConvertersProvider modelConvertersProvider; - public SwaggerSchemaService( - List externalModelConverters, - List schemaPostProcessors, - SwaggerSchemaUtil swaggerSchemaUtil, - SpringwolfConfigProperties properties) { - - externalModelConverters.forEach(converter_openapi30::addConverter); - externalModelConverters.forEach(converter_openapi31::addConverter); - this.schemaPostProcessors = schemaPostProcessors; - this.swaggerSchemaUtil = swaggerSchemaUtil; - this.properties = properties; - } public record ExtractedSchemas(ComponentSchema rootSchema, Map referencedSchemas) {} @@ -116,13 +105,7 @@ public ExtractedSchemas resolveSchema(Type type, String contentType, SchemaForma StringUtils.isBlank(contentType) ? properties.getDocket().getDefaultContentType() : contentType; // use swagger to resolve type to a swagger ResolvedSchema Object. - ModelConverters converterToUse = - switch (schemaFormat) { - case ASYNCAPI_V3 -> converter_openapi30; - case OPENAPI_V3 -> converter_openapi30; - case OPENAPI_V3_1 -> converter_openapi31; - default -> throw new IllegalArgumentException("SchemaFormat not supported: " + schemaFormat); - }; + ModelConverters converterToUse = modelConvertersProvider.getModelConverterForSchemaFormat(schemaFormat); ResolvedSchema resolvedSchema = runWithFqnSetting( (unused) -> converterToUse.resolveAsResolvedSchema(new AnnotatedType(type).resolveAsRef(true))); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfCoreConfiguration.java b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfCoreConfiguration.java index ad68cfbf0..f221dd0b6 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfCoreConfiguration.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfCoreConfiguration.java @@ -38,6 +38,7 @@ import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadService; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.TypeExtractor; import io.github.springwolf.core.asyncapi.scanners.common.utils.StringValueResolverProxy; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.asyncapi.schemas.converters.SchemaTitleModelConverter; @@ -123,12 +124,18 @@ public ComponentsService componentsService( @Bean @ConditionalOnMissingBean public SwaggerSchemaService schemasService( - List modelConverters, List schemaPostProcessors, SwaggerSchemaUtil swaggerSchemaUtil, - SpringwolfConfigProperties springwolfConfigProperties) { + SpringwolfConfigProperties springwolfConfigProperties, + ModelConvertersProvider modelConvertersProvider) { return new SwaggerSchemaService( - modelConverters, schemaPostProcessors, swaggerSchemaUtil, springwolfConfigProperties); + schemaPostProcessors, swaggerSchemaUtil, springwolfConfigProperties, modelConvertersProvider); + } + + @Bean + @ConditionalOnMissingBean + public ModelConvertersProvider modelConvertersProvider(List modelConverters) { + return new ModelConvertersProvider(modelConverters); } @Bean diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java index afe46eef2..91f0cdf12 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.core.asyncapi.schemas; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.PrettyPrinter; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; @@ -51,26 +52,54 @@ void setUp() { schemaService = new SwaggerSchemaService( List.of(new ModelConverterNativeClass.Converter()), List.of(schemasPostProcessor, schemasPostProcessor2), - new SwaggerSchemaUtil(), + new SwaggerSchemaMapper(), new SpringwolfConfigProperties()); } @Test - void classWithSchemaAnnotationWithAsyncapiSchemaformat() { - ComponentSchema schema = schemaService - .resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3) - .rootSchema(); + void classWithSchemaAnnotationWithAsyncapiSchemaformat() throws JsonProcessingException { + SwaggerSchemaService.ExtractedSchemas extractedSchemas = schemaService.resolveSchema( + ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3); + + assertThat(extractedSchemas.referencedSchemas()).hasSize(1); + ComponentSchema modelSchema = extractedSchemas.referencedSchemas().get("DifferentName"); + assertThat(modelSchema.getSchema()).isNotNull(); // we expect an asyncapi schema + assertThat(modelSchema.getMultiFormatSchema()).isNull(); - assertThat(schema.getReference().getRef()).isEqualTo("#/components/schemas/DifferentName"); + ComponentSchema rootSchema = extractedSchemas.rootSchema(); + assertThat(rootSchema.getReference().getRef()).isEqualTo("#/components/schemas/DifferentName"); } @Test - void classWithSchemaAnnotationWithOpenapiSchemaformat() { - ComponentSchema schema = schemaService - .resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.OPENAPI_V3) - .rootSchema(); + void classWithSchemaAnnotationWithOpenapiSchema30format() { + SwaggerSchemaService.ExtractedSchemas extractedSchemas = schemaService.resolveSchema( + ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.OPENAPI_V3); + + assertThat(extractedSchemas.referencedSchemas()).hasSize(1); + ComponentSchema modelSchema = extractedSchemas.referencedSchemas().get("DifferentName"); + // we expect modelSchema to contain a multiformat schema with schemaformat openapi-v3 + assertThat(modelSchema.getMultiFormatSchema()).isNotNull(); + assertThat(modelSchema.getMultiFormatSchema().getSchemaFormat()).isEqualTo(SchemaFormat.OPENAPI_V3.value); + assertThat(modelSchema.getSchema()).isNull(); + + ComponentSchema rootSchema = extractedSchemas.rootSchema(); + assertThat(rootSchema.getReference().getRef()).isEqualTo("#/components/schemas/DifferentName"); + } - assertThat(schema.getReference().getRef()).isEqualTo("#/components/schemas/DifferentName"); + @Test + void classWithSchemaAnnotationWithOpenapiSchema31format() { + SwaggerSchemaService.ExtractedSchemas extractedSchemas = schemaService.resolveSchema( + ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.OPENAPI_V3_1); + + assertThat(extractedSchemas.referencedSchemas()).hasSize(1); + ComponentSchema modelSchema = extractedSchemas.referencedSchemas().get("DifferentName"); + // we expect modelSchema to contain a multiformat schema with schemaformat openapi-v3.1 + assertThat(modelSchema.getMultiFormatSchema()).isNotNull(); + assertThat(modelSchema.getMultiFormatSchema().getSchemaFormat()).isEqualTo(SchemaFormat.OPENAPI_V3_1.value); + assertThat(modelSchema.getSchema()).isNull(); + + ComponentSchema rootSchema = extractedSchemas.rootSchema(); + assertThat(rootSchema.getReference().getRef()).isEqualTo("#/components/schemas/DifferentName"); } @Test @@ -80,7 +109,7 @@ void getDefinitionWithoutFqnClassName() throws Exception { properties.setUseFqn(false); SwaggerSchemaService schemaServiceWithFqn = - new SwaggerSchemaService(List.of(), List.of(), new SwaggerSchemaUtil(), properties); + new SwaggerSchemaService(List.of(), List.of(), new SwaggerSchemaMapper(), properties); // when Class clazz = diff --git a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java index 2096fd887..bfd7c3134 100644 --- a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java +++ b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java @@ -34,7 +34,7 @@ void asyncApiResourceArtifactTest() throws Exception { String url = "/springwolf/docs"; String actual = restTemplate.getForObject(url, String.class); String actualPatched = actual.replace("localhost:61616", "activemq:61616"); - Files.writeString(Path.of("src", "test", "resources", "asyncapi.actual.json"), actualPatched); + Files.writeString(Path.of("src", "test", "resources", "asyncapi.openapiv31.actual.json"), actualPatched); InputStream s = this.getClass().getResourceAsStream("/asyncapi.openapiv31.json"); String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8).trim(); diff --git a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java index 0b2c2df86..546501174 100644 --- a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java +++ b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java @@ -34,7 +34,7 @@ void asyncApiResourceArtifactTest() throws Exception { String url = "/springwolf/docs"; String actual = restTemplate.getForObject(url, String.class); String actualPatched = actual.replace("localhost:61616", "activemq:61616"); - Files.writeString(Path.of("src", "test", "resources", "asyncapi.actual.json"), actualPatched); + Files.writeString(Path.of("src", "test", "resources", "asyncapi.openapiv3.actual.json"), actualPatched); InputStream s = this.getClass().getResourceAsStream("/asyncapi.openapiv3.json"); String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8).trim(); From f9e1ac55bbce1ba65f68503bcbe1a06bf87181b5 Mon Sep 17 00:00:00 2001 From: tvahrst Date: Sun, 9 Nov 2025 15:16:54 +0100 Subject: [PATCH 10/14] chore: spotless --- .../core/asyncapi/schemas/ModelConvertersProvider.java | 4 +--- .../core/asyncapi/schemas/SwaggerSchemaService.java | 2 -- .../springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java index f6f2cc66b..7f20596f2 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.core.asyncapi.schemas; import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; @@ -20,7 +21,6 @@ public class ModelConvertersProvider implements InitializingBean { private ModelConverters converter_openapi30; private ModelConverters converter_openapi31; - /** * provides the appropriate {@link ModelConverters} for the given {@link SchemaFormat}. * @@ -36,7 +36,6 @@ public ModelConverters getModelConverterForSchemaFormat(SchemaFormat schemaForma }; } - /** * initializes instance variables after all constructor properties are set. */ @@ -47,5 +46,4 @@ public void afterPropertiesSet() { externalModelConverters.forEach(converter_openapi30::addConverter); externalModelConverters.forEach(converter_openapi31::addConverter); } - } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java index ee0e4d769..f74d5b431 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java @@ -10,7 +10,6 @@ import io.github.springwolf.core.asyncapi.components.postprocessors.SchemasPostProcessor; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.swagger.v3.core.converter.AnnotatedType; -import io.swagger.v3.core.converter.ModelConverter; import io.swagger.v3.core.converter.ModelConverters; import io.swagger.v3.core.converter.ResolvedSchema; import io.swagger.v3.core.jackson.TypeNameResolver; @@ -44,7 +43,6 @@ public class SwaggerSchemaService { private final SpringwolfConfigProperties properties; private final ModelConvertersProvider modelConvertersProvider; - public record ExtractedSchemas(ComponentSchema rootSchema, Map referencedSchemas) {} /** diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java index d2cb48776..3c1b2b5e9 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java @@ -62,8 +62,8 @@ public ComponentSchema mapSchemaOrRef(Schema swaggerSchema, SchemaFormat schemaF */ public ComponentSchema mapSchema(Schema swaggerSchema, SchemaFormat schemaFormat) { return switch (schemaFormat) { - case OPENAPI_V3, OPENAPI_V3_1 -> ComponentSchema.of( - new MultiFormatSchema(schemaFormat.value, swaggerSchema)); + case OPENAPI_V3, OPENAPI_V3_1 -> + ComponentSchema.of(new MultiFormatSchema(schemaFormat.value, swaggerSchema)); case ASYNCAPI_V3 -> ComponentSchema.of(mapSwaggerSchemaToAsyncApiSchema(swaggerSchema)); default -> throw new IllegalArgumentException("SchemaFormat " + schemaFormat + " is not supported"); }; From 4560d6920b6a95fb37316871cac3abfc8a2d68be Mon Sep 17 00:00:00 2001 From: tvahrst Date: Tue, 11 Nov 2025 21:40:12 +0100 Subject: [PATCH 11/14] update tests for new ModelConvertersProvider bean. Moved initialization of ModelConvertersProvider from 'afterPropertiesSet' to the constructor. --- .../schemas/ModelConvertersProvider.java | 27 +++++++------------ .../asyncapi/schemas/SwaggerSchemaUtil.java | 4 +-- ...faultComponentsServiceIntegrationTest.java | 11 ++++++-- ...tJsonComponentsServiceIntegrationTest.java | 5 ++-- ...ltXmlComponentsServiceIntegrationTest.java | 5 ++-- ...tYamlComponentsServiceIntegrationTest.java | 5 ++-- ...ssLevelChannelsScannerIntegrationTest.java | 2 ++ ...odLevelChannelsScannerIntegrationTest.java | 2 ++ .../HeaderClassExtractorIntegrationTest.java | 25 +++++++++-------- .../schemas/SwaggerSchemaServiceTest.java | 10 +++---- ...unctionChannelsScannerIntegrationTest.java | 4 ++- ...pringwolfJmsControllerIntegrationTest.java | 2 ++ ...ingwolfKafkaControllerIntegrationTest.java | 2 ++ 13 files changed, 59 insertions(+), 45 deletions(-) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java index 7f20596f2..cf40d586c 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java @@ -4,8 +4,6 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.swagger.v3.core.converter.ModelConverter; import io.swagger.v3.core.converter.ModelConverters; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.InitializingBean; import java.util.List; @@ -13,13 +11,17 @@ * Provides fully prepared {@link io.swagger.v3.core.converter.ModelConverters} for openapi v3 and v3.1 * requirements. */ -@RequiredArgsConstructor -public class ModelConvertersProvider implements InitializingBean { +public class ModelConvertersProvider { - private final List externalModelConverters; + private final ModelConverters converter_openapi30; + private final ModelConverters converter_openapi31; - private ModelConverters converter_openapi30; - private ModelConverters converter_openapi31; + public ModelConvertersProvider(List externalModelConverters) { + converter_openapi30 = new ModelConverters(false); + converter_openapi31 = new ModelConverters(true); + externalModelConverters.forEach(converter_openapi30::addConverter); + externalModelConverters.forEach(converter_openapi31::addConverter); + } /** * provides the appropriate {@link ModelConverters} for the given {@link SchemaFormat}. @@ -35,15 +37,4 @@ public ModelConverters getModelConverterForSchemaFormat(SchemaFormat schemaForma default -> throw new IllegalArgumentException("SchemaFormat not supported: " + schemaFormat); }; } - - /** - * initializes instance variables after all constructor properties are set. - */ - @Override - public void afterPropertiesSet() { - converter_openapi30 = new ModelConverters(false); - converter_openapi31 = new ModelConverters(true); - externalModelConverters.forEach(converter_openapi30::addConverter); - externalModelConverters.forEach(converter_openapi31::addConverter); - } } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java index 3c1b2b5e9..d2cb48776 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java @@ -62,8 +62,8 @@ public ComponentSchema mapSchemaOrRef(Schema swaggerSchema, SchemaFormat schemaF */ public ComponentSchema mapSchema(Schema swaggerSchema, SchemaFormat schemaFormat) { return switch (schemaFormat) { - case OPENAPI_V3, OPENAPI_V3_1 -> - ComponentSchema.of(new MultiFormatSchema(schemaFormat.value, swaggerSchema)); + case OPENAPI_V3, OPENAPI_V3_1 -> ComponentSchema.of( + new MultiFormatSchema(schemaFormat.value, swaggerSchema)); case ASYNCAPI_V3 -> ComponentSchema.of(mapSwaggerSchemaToAsyncApiSchema(swaggerSchema)); default -> throw new IllegalArgumentException("SchemaFormat " + schemaFormat + " is not supported"); }; diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsServiceIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsServiceIntegrationTest.java index 90a4b4ad8..ad6865c26 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsServiceIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsServiceIntegrationTest.java @@ -5,8 +5,10 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.asyncapi.v3.model.schema.SchemaType; import io.github.springwolf.core.asyncapi.annotations.AsyncApiPayload; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; +import io.github.springwolf.core.asyncapi.schemas.converters.SchemaTitleModelConverter; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.DiscriminatorMapping; @@ -27,8 +29,13 @@ class DefaultComponentsServiceIntegrationTest { private final SpringwolfConfigProperties configProperties = new SpringwolfConfigProperties(); - private final SwaggerSchemaService schemaService = - new SwaggerSchemaService(List.of(), List.of(), new SwaggerSchemaUtil(), configProperties); + private static final SchemaTitleModelConverter titleModelConverter = new SchemaTitleModelConverter(); + + private final SwaggerSchemaService schemaService = new SwaggerSchemaService( + List.of(), + new SwaggerSchemaUtil(), + configProperties, + new ModelConvertersProvider(List.of(titleModelConverter))); private final ComponentsService componentsService = new DefaultComponentsService(schemaService, configProperties); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultJsonComponentsServiceIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultJsonComponentsServiceIntegrationTest.java index b5555d95d..0c8827f6b 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultJsonComponentsServiceIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultJsonComponentsServiceIntegrationTest.java @@ -14,6 +14,7 @@ import io.github.springwolf.core.asyncapi.components.examples.walkers.DefaultSchemaWalker; import io.github.springwolf.core.asyncapi.components.examples.walkers.json.ExampleJsonValueGenerator; import io.github.springwolf.core.asyncapi.components.postprocessors.ExampleGeneratorPostProcessor; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.asyncapi.schemas.converters.SchemaTitleModelConverter; @@ -50,11 +51,11 @@ class DefaultJsonComponentsServiceIntegrationTest { private final SpringwolfConfigProperties springwolfConfigProperties = new SpringwolfConfigProperties(); private final SwaggerSchemaService schemaService = new SwaggerSchemaService( - List.of(new SchemaTitleModelConverter()), List.of(new ExampleGeneratorPostProcessor( new SchemaWalkerProvider(List.of(new DefaultSchemaWalker<>(new ExampleJsonValueGenerator()))))), new SwaggerSchemaUtil(), - springwolfConfigProperties); + springwolfConfigProperties, + new ModelConvertersProvider(List.of(new SchemaTitleModelConverter()))); private final ComponentsService componentsService = new DefaultComponentsService(schemaService, springwolfConfigProperties); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultXmlComponentsServiceIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultXmlComponentsServiceIntegrationTest.java index a1e5f03fe..fc17f796a 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultXmlComponentsServiceIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultXmlComponentsServiceIntegrationTest.java @@ -11,6 +11,7 @@ import io.github.springwolf.core.asyncapi.components.examples.walkers.xml.DefaultExampleXmlValueSerializer; import io.github.springwolf.core.asyncapi.components.examples.walkers.xml.ExampleXmlValueGenerator; import io.github.springwolf.core.asyncapi.components.postprocessors.ExampleGeneratorPostProcessor; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.asyncapi.schemas.converters.SchemaTitleModelConverter; @@ -51,11 +52,11 @@ class DefaultXmlComponentsServiceIntegrationTest { private final SpringwolfConfigProperties configProperties = new SpringwolfConfigProperties(); private final SwaggerSchemaService schemaService = new SwaggerSchemaService( - List.of(new SchemaTitleModelConverter()), List.of(new ExampleGeneratorPostProcessor(new SchemaWalkerProvider(List.of( new DefaultSchemaWalker<>(new ExampleXmlValueGenerator(new DefaultExampleXmlValueSerializer())))))), new SwaggerSchemaUtil(), - configProperties); + configProperties, + new ModelConvertersProvider(List.of(new SchemaTitleModelConverter()))); private final ComponentsService componentsService = new DefaultComponentsService(schemaService, configProperties); @Test diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultYamlComponentsServiceIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultYamlComponentsServiceIntegrationTest.java index 22646e273..a4fbfdbf0 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultYamlComponentsServiceIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultYamlComponentsServiceIntegrationTest.java @@ -16,6 +16,7 @@ import io.github.springwolf.core.asyncapi.components.examples.walkers.yaml.DefaultExampleYamlValueSerializer; import io.github.springwolf.core.asyncapi.components.examples.walkers.yaml.ExampleYamlValueGenerator; import io.github.springwolf.core.asyncapi.components.postprocessors.ExampleGeneratorPostProcessor; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.asyncapi.schemas.converters.SchemaTitleModelConverter; @@ -54,11 +55,11 @@ class DefaultYamlComponentsServiceIntegrationTest { new ExampleJsonValueGenerator(), new DefaultExampleYamlValueSerializer(), springwolfConfigProperties); private final SwaggerSchemaService schemaService = new SwaggerSchemaService( - List.of(new SchemaTitleModelConverter()), List.of(new ExampleGeneratorPostProcessor( new SchemaWalkerProvider(List.of(new DefaultSchemaWalker<>(exampleYamlValueGenerator))))), new SwaggerSchemaUtil(), - new SpringwolfConfigProperties()); + new SpringwolfConfigProperties(), + new ModelConvertersProvider(List.of(new SchemaTitleModelConverter()))); private final ComponentsService componentsService = new DefaultComponentsService(schemaService, springwolfConfigProperties); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java index e80ad1ace..5c7b369fe 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java @@ -29,6 +29,7 @@ import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadExtractor; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadService; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.TypeExtractor; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; @@ -71,6 +72,7 @@ SchemaWalkerProvider.class, ExampleJsonValueGenerator.class, SpringwolfConfigProperties.class, + ModelConvertersProvider.class }) class SpringAnnotationClassLevelChannelsScannerIntegrationTest { diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScannerIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScannerIntegrationTest.java index f95a6a4e1..92b6da7a4 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScannerIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScannerIntegrationTest.java @@ -29,6 +29,7 @@ import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadExtractor; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadService; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.TypeExtractor; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; @@ -73,6 +74,7 @@ SchemaWalkerProvider.class, ExampleJsonValueGenerator.class, SpringwolfConfigProperties.class, + ModelConvertersProvider.class }) class SpringAnnotationMethodLevelChannelsScannerIntegrationTest { @Autowired diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorIntegrationTest.java index 3be2f7650..af7fd39f4 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorIntegrationTest.java @@ -5,6 +5,7 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.asyncapi.v3.model.schema.SchemaType; import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadSchemaObject; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.asyncapi.schemas.converters.SchemaTitleModelConverter; @@ -12,7 +13,6 @@ import io.swagger.v3.core.converter.ModelConverters; import lombok.val; import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.messaging.handler.annotation.Header; @@ -25,8 +25,13 @@ class HeaderClassExtractorIntegrationTest { - private final SwaggerSchemaService schemaService = - new SwaggerSchemaService(List.of(), List.of(), new SwaggerSchemaUtil(), new SpringwolfConfigProperties()); + private static final SchemaTitleModelConverter titleModelConverter = new SchemaTitleModelConverter(); + + private final SwaggerSchemaService schemaService = new SwaggerSchemaService( + List.of(), + new SwaggerSchemaUtil(), + new SpringwolfConfigProperties(), + new ModelConvertersProvider(List.of(titleModelConverter))); private final HeaderClassExtractor headerClassExtractor = new HeaderClassExtractor(schemaService); private final PayloadSchemaObject payloadSchemaName = new PayloadSchemaObject( @@ -34,14 +39,12 @@ class HeaderClassExtractorIntegrationTest { private final SchemaObject stringSchema = SchemaObject.builder().type(SchemaType.STRING).build(); - private static final SchemaTitleModelConverter titleModelConverter = new SchemaTitleModelConverter(); - - @BeforeAll - static void setupClass() { - // make sure hat SpringWolf SchemaTitleModelConverter is registered with ModelConverters static registry. - // this happens in Spring tests automatically but to run only this testclass, this is necessary: - ModelConverters.getInstance().addConverter(titleModelConverter); - } + // @BeforeAll + // static void setupClass() { + // // make sure hat SpringWolf SchemaTitleModelConverter is registered with ModelConverters static registry. + // // this happens in Spring tests automatically but to run only this testclass, this is necessary: + // ModelConverters.getInstance().addConverter(titleModelConverter); + // } @AfterAll static void tearDownClass() { diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java index 91f0cdf12..f9dd6d100 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java @@ -50,10 +50,10 @@ class SwaggerSchemaServiceTest { @BeforeEach void setUp() { schemaService = new SwaggerSchemaService( - List.of(new ModelConverterNativeClass.Converter()), List.of(schemasPostProcessor, schemasPostProcessor2), - new SwaggerSchemaMapper(), - new SpringwolfConfigProperties()); + new SwaggerSchemaUtil(), + new SpringwolfConfigProperties(), + new ModelConvertersProvider(List.of(new ModelConverterNativeClass.Converter()))); } @Test @@ -108,8 +108,8 @@ void getDefinitionWithoutFqnClassName() throws Exception { SpringwolfConfigProperties properties = new SpringwolfConfigProperties(); properties.setUseFqn(false); - SwaggerSchemaService schemaServiceWithFqn = - new SwaggerSchemaService(List.of(), List.of(), new SwaggerSchemaMapper(), properties); + SwaggerSchemaService schemaServiceWithFqn = new SwaggerSchemaService( + List.of(), new SwaggerSchemaUtil(), properties, new ModelConvertersProvider(List.of())); // when Class clazz = diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java index 0d4698d09..59d7a0842 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java @@ -30,6 +30,7 @@ import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadExtractor; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadService; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.TypeExtractor; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.configuration.docket.DefaultAsyncApiDocketService; @@ -82,7 +83,8 @@ CloudStreamFunctionChannelsScanner.class, CloudStreamFunctionOperationsScanner.class, FunctionalChannelBeanBuilder.class, - SpringwolfConfigProperties.class + SpringwolfConfigProperties.class, + ModelConvertersProvider.class }) @TestPropertySource( properties = { diff --git a/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java b/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java index a5ba0e3a3..b6f8b42f4 100644 --- a/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java +++ b/springwolf-plugins/springwolf-jms-plugin/src/test/java/io/github/springwolf/plugins/jms/controller/SpringwolfJmsControllerIntegrationTest.java @@ -11,6 +11,7 @@ import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadExtractor; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadService; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.TypeExtractor; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; @@ -63,6 +64,7 @@ SchemaWalkerProvider.class, ExampleJsonValueGenerator.class, SpringwolfConfigProperties.class, + ModelConvertersProvider.class }) @TestPropertySource( properties = { diff --git a/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java b/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java index 991777d39..37f036425 100644 --- a/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java +++ b/springwolf-plugins/springwolf-kafka-plugin/src/test/java/io/github/springwolf/plugins/kafka/controller/SpringwolfKafkaControllerIntegrationTest.java @@ -11,6 +11,7 @@ import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadExtractor; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadService; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.TypeExtractor; +import io.github.springwolf.core.asyncapi.schemas.ModelConvertersProvider; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; @@ -63,6 +64,7 @@ SchemaWalkerProvider.class, ExampleJsonValueGenerator.class, SpringwolfConfigProperties.class, + ModelConvertersProvider.class }) @TestPropertySource( properties = { From a6f63e05ab3336976d0e6875f9c4e65fd6e81d75 Mon Sep 17 00:00:00 2001 From: Timon Back Date: Fri, 14 Nov 2025 16:06:53 +0100 Subject: [PATCH 12/14] feat(core): use PayloadSchemaFormat internally, switch to SchemaFormat only for the asyncapi artifact --- .../json_schema/JsonSchemaGeneratorTest.java | 5 +- .../components/DefaultComponentsService.java | 6 +- .../schemas/ModelConvertersProvider.java | 7 +- .../schemas/SwaggerSchemaService.java | 27 +++---- .../asyncapi/schemas/SwaggerSchemaUtil.java | 42 +++++----- .../components/SwaggerSchemaUtilTest.java | 76 ++++++++++--------- .../schemas/SwaggerSchemaServiceTest.java | 22 +++--- 7 files changed, 96 insertions(+), 89 deletions(-) diff --git a/springwolf-add-ons/springwolf-json-schema/src/test/java/io/github/springwolf/addons/json_schema/JsonSchemaGeneratorTest.java b/springwolf-add-ons/springwolf-json-schema/src/test/java/io/github/springwolf/addons/json_schema/JsonSchemaGeneratorTest.java index bdd707b5b..71f2c8216 100644 --- a/springwolf-add-ons/springwolf-json-schema/src/test/java/io/github/springwolf/addons/json_schema/JsonSchemaGeneratorTest.java +++ b/springwolf-add-ons/springwolf-json-schema/src/test/java/io/github/springwolf/addons/json_schema/JsonSchemaGeneratorTest.java @@ -7,11 +7,11 @@ import com.networknt.schema.JsonSchemaFactory; import com.networknt.schema.SpecVersionDetector; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; -import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; import io.github.springwolf.asyncapi.v3.model.schema.SchemaType; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; +import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat; import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.BooleanSchema; @@ -43,7 +43,8 @@ class JsonSchemaGeneratorTest { @MethodSource void validateJsonSchemaTest(String expectedJsonSchema, Supplier> asyncApiSchema) throws Exception { // given - ComponentSchema actualSchema = swaggerSchemaUtil.mapSchema(asyncApiSchema.get(), SchemaFormat.ASYNCAPI_V3); + ComponentSchema actualSchema = + swaggerSchemaUtil.mapSchema(asyncApiSchema.get(), PayloadSchemaFormat.ASYNCAPI_V3); // when verifyValidJsonSchema(expectedJsonSchema); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java index 5e9f670ba..e27cad7be 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java @@ -5,9 +5,9 @@ import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; -import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; +import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -56,8 +56,8 @@ public Map getSchemas() { */ @Override public ComponentSchema resolvePayloadSchema(Type type, String contentType) { - SchemaFormat payloadSchemaFormat = - springwolfConfigProperties.getDocket().getPayloadSchemaFormat().getSchemaFormat(); + PayloadSchemaFormat payloadSchemaFormat = + springwolfConfigProperties.getDocket().getPayloadSchemaFormat(); SwaggerSchemaService.ExtractedSchemas payload = schemaService.resolveSchema(type, contentType, payloadSchemaFormat); payload.referencedSchemas().forEach(schemas::putIfAbsent); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java index cf40d586c..aa87ce4a1 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java @@ -2,6 +2,7 @@ package io.github.springwolf.core.asyncapi.schemas; import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; +import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat; import io.swagger.v3.core.converter.ModelConverter; import io.swagger.v3.core.converter.ModelConverters; @@ -29,12 +30,10 @@ public ModelConvertersProvider(List externalModelConverters) { * @param schemaFormat the schemaFormat that shall be generated. * @return the appropriate {@link ModelConverters} or {@link IllegalArgumentException} if the given format is not supported. */ - public ModelConverters getModelConverterForSchemaFormat(SchemaFormat schemaFormat) { + public ModelConverters getModelConverterForSchemaFormat(PayloadSchemaFormat schemaFormat) { return switch (schemaFormat) { - case ASYNCAPI_V3 -> converter_openapi30; - case OPENAPI_V3 -> converter_openapi30; + case ASYNCAPI_V3, OPENAPI_V3 -> converter_openapi30; case OPENAPI_V3_1 -> converter_openapi31; - default -> throw new IllegalArgumentException("SchemaFormat not supported: " + schemaFormat); }; } } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java index f74d5b431..239b408af 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java @@ -4,10 +4,10 @@ import com.fasterxml.jackson.databind.JavaType; import io.github.springwolf.asyncapi.v3.model.ReferenceUtil; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; -import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.core.asyncapi.annotations.AsyncApiPayload; import io.github.springwolf.core.asyncapi.components.postprocessors.SchemasPostProcessor; +import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.swagger.v3.core.converter.AnnotatedType; import io.swagger.v3.core.converter.ModelConverters; @@ -81,12 +81,12 @@ public ComponentSchema postProcessSchemaWithoutRef(SchemaObject schemaWithoutRef Map newSchemasToProcess = Map.of(schemaName, headerSchema); postProcessSchemas(newSchemasToProcess, new HashMap<>(newSchemasToProcess), DEFAULT_CONTENT_TYPE); - // convert Swagger schema back to an AsnycApi SchemaObject - return swaggerSchemaUtil.mapSchema(headerSchema, SchemaFormat.ASYNCAPI_V3); + // convert Swagger schema back to an AsyncApi SchemaObject + return swaggerSchemaUtil.mapSchema(headerSchema, PayloadSchemaFormat.ASYNCAPI_V3); } public ExtractedSchemas postProcessSimpleSchema(Class type) { - return this.resolveSchema(type, "", SchemaFormat.ASYNCAPI_V3); + return this.resolveSchema(type, "", PayloadSchemaFormat.ASYNCAPI_V3); } /** @@ -95,15 +95,15 @@ public ExtractedSchemas postProcessSimpleSchema(Class type) { * * @param type * @param contentType - * @param schemaFormat SchemaFormat to use + * @param payloadSchemaFormat SchemaFormat to use * @return */ - public ExtractedSchemas resolveSchema(Type type, String contentType, SchemaFormat schemaFormat) { + public ExtractedSchemas resolveSchema(Type type, String contentType, PayloadSchemaFormat payloadSchemaFormat) { String actualContentType = StringUtils.isBlank(contentType) ? properties.getDocket().getDefaultContentType() : contentType; // use swagger to resolve type to a swagger ResolvedSchema Object. - ModelConverters converterToUse = modelConvertersProvider.getModelConverterForSchemaFormat(schemaFormat); + ModelConverters converterToUse = modelConvertersProvider.getModelConverterForSchemaFormat(payloadSchemaFormat); ResolvedSchema resolvedSchema = runWithFqnSetting( (unused) -> converterToUse.resolveAsResolvedSchema(new AnnotatedType(type).resolveAsRef(true))); @@ -112,7 +112,8 @@ public ExtractedSchemas resolveSchema(Type type, String contentType, SchemaForma // defaulting to stringSchema when resolvedSchema is null Schema stringPropertySchema = PrimitiveType.fromType(String.class).createProperty(); - ComponentSchema stringComponentSchema = swaggerSchemaUtil.mapSchema(stringPropertySchema, schemaFormat); + ComponentSchema stringComponentSchema = + swaggerSchemaUtil.mapSchema(stringPropertySchema, payloadSchemaFormat); return new ExtractedSchemas(stringComponentSchema, Map.of()); } else { Map newSchemasToProcess = new LinkedHashMap<>(resolvedSchema.referencedSchemas); @@ -122,7 +123,7 @@ public ExtractedSchemas resolveSchema(Type type, String contentType, SchemaForma HashMap processedSchemas = new HashMap<>(newSchemasToProcess); postProcessSchemas(newSchemasToProcess, processedSchemas, actualContentType); - return createExtractedSchemas(resolvedSchema.schema, processedSchemas, schemaFormat); + return createExtractedSchemas(resolvedSchema.schema, processedSchemas, payloadSchemaFormat); } } @@ -131,14 +132,14 @@ public ExtractedSchemas resolveSchema(Type type, String contentType, SchemaForma * * @param rootSchema Swagger root schema * @param referencedSchemas all referenced swagger schemas - * @param schemaFormat the schema-format of the schemas inside the resulting ExtractedSchemas + * @param payloadSchemaFormat the schema-format of the schemas inside the resulting ExtractedSchemas * @return */ private ExtractedSchemas createExtractedSchemas( - Schema rootSchema, Map referencedSchemas, SchemaFormat schemaFormat) { - ComponentSchema rootComponentSchema = swaggerSchemaUtil.mapSchemaOrRef(rootSchema, schemaFormat); + Schema rootSchema, Map referencedSchemas, PayloadSchemaFormat payloadSchemaFormat) { + ComponentSchema rootComponentSchema = swaggerSchemaUtil.mapSchemaOrRef(rootSchema, payloadSchemaFormat); Map referencedComponentSchemas = - swaggerSchemaUtil.mapSchemasMap(referencedSchemas, schemaFormat); + swaggerSchemaUtil.mapSchemasMap(referencedSchemas, payloadSchemaFormat); return new ExtractedSchemas(rootComponentSchema, referencedComponentSchemas); } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java index d2cb48776..edd62e092 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java @@ -4,10 +4,10 @@ import io.github.springwolf.asyncapi.v3.model.ExternalDocumentation; import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema; import io.github.springwolf.asyncapi.v3.model.schema.MultiFormatSchema; -import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; import io.github.springwolf.asyncapi.v3.model.schema.SchemaType; +import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat; import io.swagger.v3.oas.models.media.Schema; import lombok.RequiredArgsConstructor; import org.springframework.lang.Nullable; @@ -25,7 +25,7 @@ @RequiredArgsConstructor public class SwaggerSchemaUtil { - public Map mapSchemasMap(Map schemaMap, SchemaFormat schemaFormat) { + public Map mapSchemasMap(Map schemaMap, PayloadSchemaFormat schemaFormat) { return schemaMap.entrySet().stream() .map(entry -> Map.entry(entry.getKey(), mapSchema(entry.getValue(), schemaFormat))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); @@ -37,15 +37,15 @@ public Map mapSchemasMap(Map schemaMap, * referenced location. Otherwise, the given Swagger schema is converted to an AsnycApi {@link SchemaObject} and * put into the resulting {@link ComponentSchema} * - * @param swaggerSchema the Swagger schema to convert - * @param schemaFormat the schema format of the resulting schema + * @param swaggerSchema the Swagger schema to convert + * @param payloadSchemaFormat the schema format of the resulting schema * @return ComponentSchema with either a {@link SchemaReference} or a {@link SchemaObject}. */ - public ComponentSchema mapSchemaOrRef(Schema swaggerSchema, SchemaFormat schemaFormat) { + public ComponentSchema mapSchemaOrRef(Schema swaggerSchema, PayloadSchemaFormat payloadSchemaFormat) { if (swaggerSchema.get$ref() != null) { return ComponentSchema.of(new SchemaReference(swaggerSchema.get$ref())); } - return mapSchema(swaggerSchema, schemaFormat); + return mapSchema(swaggerSchema, payloadSchemaFormat); } /** @@ -56,16 +56,16 @@ public ComponentSchema mapSchemaOrRef(Schema swaggerSchema, SchemaFormat schemaF * as long as the child schemas are 'real' schemas and not schema references. So this method performs a deep conversion * of the entire Swagger schema. * - * @param swaggerSchema the given Swagger schema instance * @param + * @param swaggerSchema the given Swagger schema instance + * @param payloadSchemaFormat * @return the resulting AsnycApi SchemaObject */ - public ComponentSchema mapSchema(Schema swaggerSchema, SchemaFormat schemaFormat) { - return switch (schemaFormat) { + public ComponentSchema mapSchema(Schema swaggerSchema, PayloadSchemaFormat payloadSchemaFormat) { + return switch (payloadSchemaFormat) { case OPENAPI_V3, OPENAPI_V3_1 -> ComponentSchema.of( - new MultiFormatSchema(schemaFormat.value, swaggerSchema)); + new MultiFormatSchema(payloadSchemaFormat.getSchemaFormat().value, swaggerSchema)); case ASYNCAPI_V3 -> ComponentSchema.of(mapSwaggerSchemaToAsyncApiSchema(swaggerSchema)); - default -> throw new IllegalArgumentException("SchemaFormat " + schemaFormat + " is not supported"); }; } @@ -98,7 +98,8 @@ private SchemaObject mapSwaggerSchemaToAsyncApiSchema(Schema swaggerSchema) { Map properties = swaggerSchema.getProperties(); if (properties != null) { Map propertiesMapped = properties.entrySet().stream() - .map(entry -> Map.entry(entry.getKey(), mapSchemaOrRef(entry.getValue(), SchemaFormat.ASYNCAPI_V3))) + .map(entry -> Map.entry( + entry.getKey(), mapSchemaOrRef(entry.getValue(), PayloadSchemaFormat.ASYNCAPI_V3))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); builder.properties(propertiesMapped); @@ -150,7 +151,8 @@ private SchemaObject mapSwaggerSchemaToAsyncApiSchema(Schema swaggerSchema) { Object additionalProperties = swaggerSchema.getAdditionalProperties(); if (additionalProperties instanceof Schema) { - builder.additionalProperties(mapSchemaOrRef((Schema) additionalProperties, SchemaFormat.ASYNCAPI_V3)); + builder.additionalProperties( + mapSchemaOrRef((Schema) additionalProperties, PayloadSchemaFormat.ASYNCAPI_V3)); } builder.required(swaggerSchema.getRequired()); @@ -162,21 +164,21 @@ private SchemaObject mapSwaggerSchemaToAsyncApiSchema(Schema swaggerSchema) { List allOf = swaggerSchema.getAllOf(); if (allOf != null) { builder.allOf(allOf.stream() - .map(schema -> mapSchemaOrRef(schema, SchemaFormat.ASYNCAPI_V3)) + .map(schema -> mapSchemaOrRef(schema, PayloadSchemaFormat.ASYNCAPI_V3)) .collect(Collectors.toList())); } List oneOf = swaggerSchema.getOneOf(); if (oneOf != null) { builder.oneOf(oneOf.stream() - .map(schema -> mapSchemaOrRef(schema, SchemaFormat.ASYNCAPI_V3)) + .map(schema -> mapSchemaOrRef(schema, PayloadSchemaFormat.ASYNCAPI_V3)) .collect(Collectors.toList())); } List anyOf = swaggerSchema.getAnyOf(); if (anyOf != null) { builder.anyOf(anyOf.stream() - .map(schema -> mapSchemaOrRef(schema, SchemaFormat.ASYNCAPI_V3)) + .map(schema -> mapSchemaOrRef(schema, PayloadSchemaFormat.ASYNCAPI_V3)) .collect(Collectors.toList())); } @@ -184,12 +186,12 @@ private SchemaObject mapSwaggerSchemaToAsyncApiSchema(Schema swaggerSchema) { Schema not = swaggerSchema.getNot(); if (not != null) { - builder.not(mapSchemaOrRef(not, SchemaFormat.ASYNCAPI_V3)); + builder.not(mapSchemaOrRef(not, PayloadSchemaFormat.ASYNCAPI_V3)); } Schema items = swaggerSchema.getItems(); if (items != null && "array".equals(swaggerSchema.getType())) { - builder.items(mapSchemaOrRef(items, SchemaFormat.ASYNCAPI_V3)); + builder.items(mapSchemaOrRef(items, PayloadSchemaFormat.ASYNCAPI_V3)); } builder.uniqueItems(swaggerSchema.getUniqueItems()); @@ -248,8 +250,8 @@ public Object unwrapSchema(Object schema) { *
  • a {@link SchemaReference} which is converted to a Swagger Schema with a $ref reference
  • *
  • a {@link MultiFormatSchema}. In this case, these Mediatypes are supported: *
      - *
    • {@link SchemaFormat#ASYNCAPI_V3}
    • - *
    • {@link SchemaFormat#OPENAPI_V3}
    • + *
    • {@link PayloadSchemaFormat#ASYNCAPI_V3}
    • + *
    • {@link PayloadSchemaFormat#OPENAPI_V3}
    • *
    *
  • * diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java index 36a13cf52..baadc4f59 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java @@ -8,6 +8,7 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference; import io.github.springwolf.asyncapi.v3.model.schema.SchemaType; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; +import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat; import io.swagger.v3.oas.models.ExternalDocumentation; import io.swagger.v3.oas.models.media.Discriminator; import io.swagger.v3.oas.models.media.ObjectSchema; @@ -39,7 +40,7 @@ void mapToOpenApiV3Schema() { schema.setExternalDocs(externalDocs); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, SchemaFormat.OPENAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, PayloadSchemaFormat.OPENAPI_V3); // then // componentSchema should contain a MultiFormatSchema with the original openapi schema @@ -60,7 +61,8 @@ void mapToOpenApiV31Schema() { schema.setExternalDocs(externalDocs); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, SchemaFormat.OPENAPI_V3_1); + ComponentSchema componentSchema = + swaggerSchemaUtil.mapSchemaOrRef(schema, PayloadSchemaFormat.OPENAPI_V3_1); // then // componentSchema should contain a MultiFormatSchema with the original openapi schema @@ -77,7 +79,7 @@ void mapReference() { schema.set$ref("#/components/schemas/MySchema"); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getReference()).isEqualTo(new SchemaReference("#/components/schemas/MySchema")); @@ -90,7 +92,7 @@ void mapSchema() { schema.setType(SchemaType.STRING); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getType()).containsExactly(SchemaType.STRING); @@ -111,7 +113,7 @@ void mapToOpenApiV3Schema() { schema.setExternalDocs(externalDocs); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.OPENAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.OPENAPI_V3); // then // componentSchema should contain a MultiFormatSchema with the original openapi schema @@ -132,7 +134,7 @@ void mapToOpenApiV31Schema() { schema.setExternalDocs(externalDocs); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.OPENAPI_V3_1); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.OPENAPI_V3_1); // then // componentSchema should contain a MultiFormatSchema with the original openapi schema @@ -152,7 +154,7 @@ void mapExternalDocs() { schema.setExternalDocs(externalDocs); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getExternalDocs().getDescription()) @@ -167,7 +169,7 @@ void mapDeprecated() { schema.setDeprecated(true); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getDeprecated()).isEqualTo(true); @@ -180,7 +182,7 @@ void mapTitle() { schema.setTitle("title"); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getTitle()).isEqualTo(schema.getTitle()); @@ -193,7 +195,7 @@ void mapType() { schema.setType(SchemaType.STRING); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getType()).containsExactly(schema.getType()); @@ -208,7 +210,7 @@ void mapProperties() { schema.addProperty("property", property); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(((ComponentSchema) @@ -225,7 +227,7 @@ void mapDescription() { schema.setDescription("description"); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getDescription()).isEqualTo(schema.getDescription()); @@ -238,7 +240,7 @@ void mapFormat() { schema.setFormat("format"); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getFormat()).isEqualTo(schema.getFormat()); @@ -251,7 +253,7 @@ void mapPattern() { schema.setPattern("pattern"); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getPattern()).isEqualTo(schema.getPattern()); @@ -265,7 +267,7 @@ void mapExclusiveMinimum() { schema.setExclusiveMinimum(true); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getExclusiveMinimum()).isEqualTo(schema.getMinimum()); @@ -279,7 +281,7 @@ void mapExclusiveMinimumValue() { schema.setExclusiveMinimumValue(BigDecimal.ONE); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getExclusiveMinimum()).isEqualTo(schema.getExclusiveMinimumValue()); @@ -294,7 +296,7 @@ void mapExclusiveMaximum() { schema.setExclusiveMaximum(true); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getExclusiveMaximum()).isEqualTo(schema.getMaximum()); @@ -308,7 +310,7 @@ void mapExclusiveMaximumValue() { schema.setExclusiveMaximumValue(BigDecimal.ONE); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getExclusiveMaximum()).isEqualTo(schema.getExclusiveMaximumValue()); @@ -322,7 +324,7 @@ void mapMinimum() { schema.setMinimum(BigDecimal.ONE); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getMinimum()).isEqualTo(schema.getMinimum()); @@ -336,7 +338,7 @@ void mapMaximum() { schema.setMaximum(BigDecimal.ONE); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getMaximum()).isEqualTo(schema.getMaximum()); @@ -350,7 +352,7 @@ void mapMultipleOf() { schema.setMultipleOf(BigDecimal.ONE); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getMultipleOf()).isEqualTo(schema.getMultipleOf()); @@ -363,7 +365,7 @@ void mapMinLength() { schema.setMinLength(1); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getMinLength()).isEqualTo(schema.getMinLength()); @@ -376,7 +378,7 @@ void mapMaxLength() { schema.setMaxLength(1); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getMaxLength()).isEqualTo(schema.getMaxLength()); @@ -389,7 +391,7 @@ void mapEnum() { schema.setEnum(List.of("enum1", "enum2")); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getEnumValues()).isEqualTo(schema.getEnum()); @@ -402,7 +404,7 @@ void mapExample() { schema.setExample("example"); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getExamples()).isEqualTo(List.of(schema.getExample())); @@ -417,7 +419,7 @@ void mapAdditionalProperties() { schema.setAdditionalProperties(additionalProperties); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema @@ -435,7 +437,7 @@ void mapRequired() { schema.setRequired(List.of("required")); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then assertThat(componentSchema.getSchema().getRequired()).isEqualTo(schema.getRequired()); @@ -450,7 +452,7 @@ void mapDiscriminator() { schema.setDiscriminator(discriminator); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then // ensure that componentSchema contains an AsnycApi SchemaObjekt. @@ -468,7 +470,7 @@ void mapAllOf() { schema.addAllOfItem(allOf); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then // ensure that componentSchema contains an AsnycApi SchemaObjekt. @@ -487,7 +489,7 @@ void mapOneOf() { schema.addOneOfItem(oneOf); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then // ensure that componentSchema contains an AsnycApi SchemaObjekt. @@ -506,7 +508,7 @@ void mapAnyOf() { schema.addAnyOfItem(anyOf); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then // ensure that componentSchema contains an AsnycApi SchemaObjekt. @@ -522,7 +524,7 @@ void mapConst() { schema.setConst("const"); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then // ensure that componentSchema contains an AsnycApi SchemaObjekt. @@ -539,7 +541,7 @@ void mapNot() { schema.setNot(not); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then // ensure that componentSchema contains an AsnycApi SchemaObjekt. @@ -558,7 +560,7 @@ void mapItems() { schema.setItems(item); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then // ensure that componentSchema contains an AsnycApi SchemaObjekt. @@ -574,7 +576,7 @@ void mapUniqueItems() { schema.setUniqueItems(false); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then // ensure that componentSchema contains an AsnycApi SchemaObjekt. @@ -589,7 +591,7 @@ void mapMinItems() { schema.setMinItems(1); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then // ensure that componentSchema contains an AsnycApi SchemaObjekt. @@ -604,7 +606,7 @@ void mapMaxItems() { schema.setMaxItems(10); // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.ASYNCAPI_V3); + ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, PayloadSchemaFormat.ASYNCAPI_V3); // then // ensure that componentSchema contains an AsnycApi SchemaObjekt. diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java index f9dd6d100..7c5a73b7f 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.core.asyncapi.schemas; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.PrettyPrinter; import com.fasterxml.jackson.core.util.DefaultIndenter; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; @@ -12,6 +11,7 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat; import io.github.springwolf.asyncapi.v3.model.schema.SchemaType; import io.github.springwolf.core.asyncapi.components.postprocessors.SchemasPostProcessor; +import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.swagger.v3.core.converter.AnnotatedType; import io.swagger.v3.core.converter.ModelConverter; @@ -57,9 +57,9 @@ void setUp() { } @Test - void classWithSchemaAnnotationWithAsyncapiSchemaformat() throws JsonProcessingException { + void classWithSchemaAnnotationWithAsyncapiSchemaformat() { SwaggerSchemaService.ExtractedSchemas extractedSchemas = schemaService.resolveSchema( - ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3); + ClassWithSchemaAnnotation.class, "content-type-not-relevant", PayloadSchemaFormat.ASYNCAPI_V3); assertThat(extractedSchemas.referencedSchemas()).hasSize(1); ComponentSchema modelSchema = extractedSchemas.referencedSchemas().get("DifferentName"); @@ -73,7 +73,7 @@ void classWithSchemaAnnotationWithAsyncapiSchemaformat() throws JsonProcessingEx @Test void classWithSchemaAnnotationWithOpenapiSchema30format() { SwaggerSchemaService.ExtractedSchemas extractedSchemas = schemaService.resolveSchema( - ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.OPENAPI_V3); + ClassWithSchemaAnnotation.class, "content-type-not-relevant", PayloadSchemaFormat.OPENAPI_V3); assertThat(extractedSchemas.referencedSchemas()).hasSize(1); ComponentSchema modelSchema = extractedSchemas.referencedSchemas().get("DifferentName"); @@ -89,7 +89,7 @@ void classWithSchemaAnnotationWithOpenapiSchema30format() { @Test void classWithSchemaAnnotationWithOpenapiSchema31format() { SwaggerSchemaService.ExtractedSchemas extractedSchemas = schemaService.resolveSchema( - ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.OPENAPI_V3_1); + ClassWithSchemaAnnotation.class, "content-type-not-relevant", PayloadSchemaFormat.OPENAPI_V3_1); assertThat(extractedSchemas.referencedSchemas()).hasSize(1); ComponentSchema modelSchema = extractedSchemas.referencedSchemas().get("DifferentName"); @@ -115,7 +115,7 @@ void getDefinitionWithoutFqnClassName() throws Exception { Class clazz = OneFieldFooWithoutFqn.class; // swagger seems to cache results. Therefore, a new class must be used. Map schemas = schemaServiceWithFqn - .resolveSchema(clazz, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3) + .resolveSchema(clazz, "content-type-not-relevant", PayloadSchemaFormat.ASYNCAPI_V3) .referencedSchemas(); String actualDefinitions = objectMapper.writer(printer).writeValueAsString(schemas); @@ -127,7 +127,8 @@ void getDefinitionWithoutFqnClassName() throws Exception { @Test void postProcessorsAreCalledWithAsyncapiSchemaformat() { - schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "some-content-type", SchemaFormat.ASYNCAPI_V3); + schemaService.resolveSchema( + ClassWithSchemaAnnotation.class, "some-content-type", PayloadSchemaFormat.ASYNCAPI_V3); verify(schemasPostProcessor).process(any(), any(), eq("some-content-type")); verify(schemasPostProcessor2).process(any(), any(), eq("some-content-type")); @@ -135,7 +136,8 @@ void postProcessorsAreCalledWithAsyncapiSchemaformat() { @Test void postProcessorsAreCalledWithOpenapiSchemaformat() { - schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "some-content-type", SchemaFormat.OPENAPI_V3); + schemaService.resolveSchema( + ClassWithSchemaAnnotation.class, "some-content-type", PayloadSchemaFormat.OPENAPI_V3); verify(schemasPostProcessor).process(any(), any(), eq("some-content-type")); verify(schemasPostProcessor2).process(any(), any(), eq("some-content-type")); @@ -152,7 +154,7 @@ void postProcessorIsSkippedWhenSchemaWasRemoved() { .process(any(), any(), any()); schemaService.resolveSchema( - ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3); + ClassWithSchemaAnnotation.class, "content-type-not-relevant", PayloadSchemaFormat.ASYNCAPI_V3); verifyNoInteractions(schemasPostProcessor2); } @@ -160,7 +162,7 @@ void postProcessorIsSkippedWhenSchemaWasRemoved() { @Test void modelConvertersRefsAreResolved() { SwaggerSchemaService.ExtractedSchemas schema = schemaService.resolveSchema( - ModelConverterNativeClass.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3); + ModelConverterNativeClass.class, "content-type-not-relevant", PayloadSchemaFormat.ASYNCAPI_V3); assertThat(schema.rootSchema().getReference().getRef()) .contains(ModelConverterNativeClass.class.getName().replace("$", ".")); From aba7bf713fd150321c40f5168edb812b2fdd4863 Mon Sep 17 00:00:00 2001 From: Timon Back Date: Fri, 14 Nov 2025 16:51:07 +0100 Subject: [PATCH 13/14] chore: move OpenAPIv3 integration test to kafka example Kafka example includes many more listener, schemas and possible edge cases --- .../asyncapi/grouping/GroupingService.java | 7 +- .../schemas/ModelConvertersProvider.java | 2 +- .../schemas/SwaggerSchemaService.java | 2 +- .../HeaderClassExtractorIntegrationTest.java | 7 - ...OpenApiV31SchemaFormatIntegrationTest.java | 44 - ...hOpenApiV3SchemaFormatIntegrationTest.java | 44 - .../test/resources/asyncapi.openapiv3.json | 224 -- .../test/resources/asyncapi.openapiv31.json | 227 -- .../PayloadSchemaFormatIntegrationTest.java | 51 + .../test/resources/asyncapi.openapiv31.json | 1950 +++++++++++++++++ 10 files changed, 2009 insertions(+), 549 deletions(-) delete mode 100644 springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java delete mode 100644 springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java delete mode 100644 springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv3.json delete mode 100644 springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json create mode 100644 springwolf-examples/springwolf-kafka-example/src/test/java/io/github/springwolf/examples/kafka/PayloadSchemaFormatIntegrationTest.java create mode 100644 springwolf-examples/springwolf-kafka-example/src/test/resources/asyncapi.openapiv31.json diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java index f926086e2..1ca4ebd54 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java @@ -283,7 +283,12 @@ private static Set findUnmarkedNestedSchemasForAsyncAPISchema( */ private static Set findUnmarkedNestedSchemasForOpenAPISchema( MarkingContext markingContext, Schema openapiSchema) { - Stream propertySchemas = openapiSchema.getProperties().values().stream(); + final Stream propertySchemas; + if(openapiSchema.getProperties() != null) { + propertySchemas = openapiSchema.getProperties().values().stream(); + }else { + propertySchemas = Stream.empty(); + } Stream referencedSchemas = Stream.of( openapiSchema.getAllOf(), openapiSchema.getAnyOf(), openapiSchema.getOneOf()) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java index aa87ce4a1..5bfb1adcd 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java @@ -30,7 +30,7 @@ public ModelConvertersProvider(List externalModelConverters) { * @param schemaFormat the schemaFormat that shall be generated. * @return the appropriate {@link ModelConverters} or {@link IllegalArgumentException} if the given format is not supported. */ - public ModelConverters getModelConverterForSchemaFormat(PayloadSchemaFormat schemaFormat) { + public ModelConverters getModelConverterFor(PayloadSchemaFormat schemaFormat) { return switch (schemaFormat) { case ASYNCAPI_V3, OPENAPI_V3 -> converter_openapi30; case OPENAPI_V3_1 -> converter_openapi31; diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java index 239b408af..e3db527cc 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java @@ -103,7 +103,7 @@ public ExtractedSchemas resolveSchema(Type type, String contentType, PayloadSche StringUtils.isBlank(contentType) ? properties.getDocket().getDefaultContentType() : contentType; // use swagger to resolve type to a swagger ResolvedSchema Object. - ModelConverters converterToUse = modelConvertersProvider.getModelConverterForSchemaFormat(payloadSchemaFormat); + ModelConverters converterToUse = modelConvertersProvider.getModelConverterFor(payloadSchemaFormat); ResolvedSchema resolvedSchema = runWithFqnSetting( (unused) -> converterToUse.resolveAsResolvedSchema(new AnnotatedType(type).resolveAsRef(true))); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorIntegrationTest.java index af7fd39f4..329deff50 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractorIntegrationTest.java @@ -39,13 +39,6 @@ class HeaderClassExtractorIntegrationTest { private final SchemaObject stringSchema = SchemaObject.builder().type(SchemaType.STRING).build(); - // @BeforeAll - // static void setupClass() { - // // make sure hat SpringWolf SchemaTitleModelConverter is registered with ModelConverters static registry. - // // this happens in Spring tests automatically but to run only this testclass, this is necessary: - // ModelConverters.getInstance().addConverter(titleModelConverter); - // } - @AfterAll static void tearDownClass() { ModelConverters.getInstance().removeConverter(titleModelConverter); diff --git a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java deleted file mode 100644 index bfd7c3134..000000000 --- a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV31SchemaFormatIntegrationTest.java +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.springwolf.examples.jms; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.test.context.TestPropertySource; - -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.assertj.core.api.Assertions.assertThat; - -@SpringBootTest( - classes = {SpringwolfJmsExampleApplication.class}, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@ExtendWith({JmsTestContainerExtension.class}) -@TestPropertySource(properties = {"springwolf.docket.payload-schema-format=openapi_v3_1"}) -public class ApiWithOpenApiV31SchemaFormatIntegrationTest { - - @Autowired - private TestRestTemplate restTemplate; - - @Value("${server.port}") - public Integer serverPort; - - @Test - void asyncApiResourceArtifactTest() throws Exception { - String url = "/springwolf/docs"; - String actual = restTemplate.getForObject(url, String.class); - String actualPatched = actual.replace("localhost:61616", "activemq:61616"); - Files.writeString(Path.of("src", "test", "resources", "asyncapi.openapiv31.actual.json"), actualPatched); - - InputStream s = this.getClass().getResourceAsStream("/asyncapi.openapiv31.json"); - String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8).trim(); - - assertThat(actualPatched).isEqualTo(expected); - } -} diff --git a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java b/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java deleted file mode 100644 index 546501174..000000000 --- a/springwolf-examples/springwolf-jms-example/src/test/java/io/github/springwolf/examples/jms/ApiWithOpenApiV3SchemaFormatIntegrationTest.java +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.springwolf.examples.jms; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.test.context.TestPropertySource; - -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.assertj.core.api.Assertions.assertThat; - -@SpringBootTest( - classes = {SpringwolfJmsExampleApplication.class}, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -@ExtendWith({JmsTestContainerExtension.class}) -@TestPropertySource(properties = {"springwolf.docket.payload-schema-format=openapi_v3"}) -public class ApiWithOpenApiV3SchemaFormatIntegrationTest { - - @Autowired - private TestRestTemplate restTemplate; - - @Value("${server.port}") - public Integer serverPort; - - @Test - void asyncApiResourceArtifactTest() throws Exception { - String url = "/springwolf/docs"; - String actual = restTemplate.getForObject(url, String.class); - String actualPatched = actual.replace("localhost:61616", "activemq:61616"); - Files.writeString(Path.of("src", "test", "resources", "asyncapi.openapiv3.actual.json"), actualPatched); - - InputStream s = this.getClass().getResourceAsStream("/asyncapi.openapiv3.json"); - String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8).trim(); - - assertThat(actualPatched).isEqualTo(expected); - } -} diff --git a/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv3.json b/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv3.json deleted file mode 100644 index f74ed3f93..000000000 --- a/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv3.json +++ /dev/null @@ -1,224 +0,0 @@ -{ - "asyncapi": "3.0.0", - "info": { - "title": "Springwolf example project - JMS", - "version": "1.0.0", - "description": "Springwolf [example project](https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-jms-example) to demonstrate springwolfs abilities, including **markdown** support for descriptions.", - "termsOfService": "http://asyncapi.org/terms", - "contact": { - "name": "springwolf", - "url": "https://github.com/springwolf/springwolf-core", - "email": "example@example.com" - }, - "license": { - "name": "Apache License 2.0" - }, - "x-generator": "springwolf" - }, - "defaultContentType": "application/json", - "servers": { - "jms-server": { - "host": "tcp://activemq:61616", - "protocol": "jms" - } - }, - "channels": { - "another-queue": { - "address": "another-queue", - "messages": { - "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { - "$ref": "#/components/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" - } - }, - "bindings": { - "jms": { - "bindingVersion": "0.0.1" - } - } - }, - "example-queue": { - "address": "example-queue", - "messages": { - "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { - "$ref": "#/components/messages/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" - } - }, - "bindings": { - "jms": { - "bindingVersion": "0.0.1" - } - } - } - }, - "components": { - "schemas": { - "HeadersNotDocumented": { - "title": "HeadersNotDocumented", - "type": "object", - "properties": { }, - "description": "There can be headers, but they are not explicitly documented.", - "examples": [ - { } - ] - }, - "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { - "schemaFormat": "application/vnd.oai.openapi;version=3.0.0", - "schema": { - "description": "Another payload model", - "example": { - "example": { - "someEnum": "FOO2", - "someLong": 5, - "someString": "some string value" - }, - "foo": "bar" - }, - "properties": { - "example": { - "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" - }, - "foo": { - "type": "string", - "description": "Foo field", - "example": "bar", - "maxLength": 100 - } - }, - "required": [ - "example" - ], - "title": "AnotherPayloadDto" - } - }, - "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { - "schemaFormat": "application/vnd.oai.openapi;version=3.0.0", - "schema": { - "description": "Example payload model", - "example": { - "someEnum": "FOO2", - "someLong": 5, - "someString": "some string value" - }, - "properties": { - "someEnum": { - "type": "string", - "description": "Some enum field", - "enum": [ - "FOO1", - "FOO2", - "FOO3" - ], - "example": "FOO2" - }, - "someLong": { - "type": "integer", - "format": "int64", - "description": "Some long field", - "example": 5, - "minimum": 0 - }, - "someString": { - "type": "string", - "description": "Some string field", - "example": "some string value" - } - }, - "required": [ - "someEnum", - "someString" - ], - "title": "ExamplePayloadDto" - } - } - }, - "messages": { - "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { - "headers": { - "$ref": "#/components/schemas/HeadersNotDocumented" - }, - "payload": { - "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", - "schema": { - "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" - } - }, - "name": "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto", - "title": "AnotherPayloadDto", - "bindings": { - "jms": { - "bindingVersion": "0.0.1" - } - } - }, - "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { - "headers": { - "$ref": "#/components/schemas/HeadersNotDocumented" - }, - "payload": { - "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", - "schema": { - "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" - } - }, - "name": "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto", - "title": "ExamplePayloadDto", - "bindings": { - "jms": { - "bindingVersion": "0.0.1" - } - } - } - } - }, - "operations": { - "another-queue_receive_receiveAnotherPayload": { - "action": "receive", - "channel": { - "$ref": "#/channels/another-queue" - }, - "bindings": { - "jms": { } - }, - "messages": [ - { - "$ref": "#/channels/another-queue/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" - } - ] - }, - "another-queue_send_sendMessage": { - "action": "send", - "channel": { - "$ref": "#/channels/another-queue" - }, - "title": "another-queue_send", - "description": "Custom, optional description defined in the AsyncPublisher annotation", - "bindings": { - "jms": { - "internal-field": "customValue", - "nested": { - "key": "nestedValue" - } - } - }, - "messages": [ - { - "$ref": "#/channels/another-queue/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" - } - ] - }, - "example-queue_receive_receiveExamplePayload": { - "action": "receive", - "channel": { - "$ref": "#/channels/example-queue" - }, - "bindings": { - "jms": { } - }, - "messages": [ - { - "$ref": "#/channels/example-queue/messages/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" - } - ] - } - } -} \ No newline at end of file diff --git a/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json b/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json deleted file mode 100644 index 4d3a8ae66..000000000 --- a/springwolf-examples/springwolf-jms-example/src/test/resources/asyncapi.openapiv31.json +++ /dev/null @@ -1,227 +0,0 @@ -{ - "asyncapi": "3.0.0", - "info": { - "title": "Springwolf example project - JMS", - "version": "1.0.0", - "description": "Springwolf [example project](https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-jms-example) to demonstrate springwolfs abilities, including **markdown** support for descriptions.", - "termsOfService": "http://asyncapi.org/terms", - "contact": { - "name": "springwolf", - "url": "https://github.com/springwolf/springwolf-core", - "email": "example@example.com" - }, - "license": { - "name": "Apache License 2.0" - }, - "x-generator": "springwolf" - }, - "defaultContentType": "application/json", - "servers": { - "jms-server": { - "host": "tcp://activemq:61616", - "protocol": "jms" - } - }, - "channels": { - "another-queue": { - "address": "another-queue", - "messages": { - "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { - "$ref": "#/components/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" - } - }, - "bindings": { - "jms": { - "bindingVersion": "0.0.1" - } - } - }, - "example-queue": { - "address": "example-queue", - "messages": { - "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { - "$ref": "#/components/messages/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" - } - }, - "bindings": { - "jms": { - "bindingVersion": "0.0.1" - } - } - } - }, - "components": { - "schemas": { - "HeadersNotDocumented": { - "title": "HeadersNotDocumented", - "type": "object", - "properties": { }, - "description": "There can be headers, but they are not explicitly documented.", - "examples": [ - { } - ] - }, - "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { - "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", - "schema": { - "type": "object", - "description": "Another payload model", - "example": { - "example": { - "someEnum": "FOO2", - "someLong": 5, - "someString": "some string value" - }, - "foo": "bar" - }, - "properties": { - "example": { - "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto", - "description": "Example field" - }, - "foo": { - "type": "string", - "description": "Foo field", - "example": "bar", - "maxLength": 100 - } - }, - "required": [ - "example" - ], - "title": "AnotherPayloadDto" - } - }, - "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { - "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", - "schema": { - "type": "object", - "description": "Example payload model", - "example": { - "someEnum": "FOO2", - "someLong": 5, - "someString": "some string value" - }, - "properties": { - "someEnum": { - "type": "string", - "description": "Some enum field", - "enum": [ - "FOO1", - "FOO2", - "FOO3" - ], - "example": "FOO2" - }, - "someLong": { - "type": "integer", - "format": "int64", - "description": "Some long field", - "example": 5, - "minimum": 0 - }, - "someString": { - "type": "string", - "description": "Some string field", - "example": "some string value" - } - }, - "required": [ - "someEnum", - "someString" - ], - "title": "ExamplePayloadDto" - } - } - }, - "messages": { - "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto": { - "headers": { - "$ref": "#/components/schemas/HeadersNotDocumented" - }, - "payload": { - "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", - "schema": { - "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" - } - }, - "name": "io.github.springwolf.examples.jms.dtos.AnotherPayloadDto", - "title": "AnotherPayloadDto", - "bindings": { - "jms": { - "bindingVersion": "0.0.1" - } - } - }, - "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto": { - "headers": { - "$ref": "#/components/schemas/HeadersNotDocumented" - }, - "payload": { - "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", - "schema": { - "$ref": "#/components/schemas/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" - } - }, - "name": "io.github.springwolf.examples.jms.dtos.ExamplePayloadDto", - "title": "ExamplePayloadDto", - "bindings": { - "jms": { - "bindingVersion": "0.0.1" - } - } - } - } - }, - "operations": { - "another-queue_receive_receiveAnotherPayload": { - "action": "receive", - "channel": { - "$ref": "#/channels/another-queue" - }, - "bindings": { - "jms": { } - }, - "messages": [ - { - "$ref": "#/channels/another-queue/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" - } - ] - }, - "another-queue_send_sendMessage": { - "action": "send", - "channel": { - "$ref": "#/channels/another-queue" - }, - "title": "another-queue_send", - "description": "Custom, optional description defined in the AsyncPublisher annotation", - "bindings": { - "jms": { - "internal-field": "customValue", - "nested": { - "key": "nestedValue" - } - } - }, - "messages": [ - { - "$ref": "#/channels/another-queue/messages/io.github.springwolf.examples.jms.dtos.AnotherPayloadDto" - } - ] - }, - "example-queue_receive_receiveExamplePayload": { - "action": "receive", - "channel": { - "$ref": "#/channels/example-queue" - }, - "bindings": { - "jms": { } - }, - "messages": [ - { - "$ref": "#/channels/example-queue/messages/io.github.springwolf.examples.jms.dtos.ExamplePayloadDto" - } - ] - } - } -} \ No newline at end of file diff --git a/springwolf-examples/springwolf-kafka-example/src/test/java/io/github/springwolf/examples/kafka/PayloadSchemaFormatIntegrationTest.java b/springwolf-examples/springwolf-kafka-example/src/test/java/io/github/springwolf/examples/kafka/PayloadSchemaFormatIntegrationTest.java new file mode 100644 index 000000000..09b35dbfb --- /dev/null +++ b/springwolf-examples/springwolf-kafka-example/src/test/java/io/github/springwolf/examples/kafka/PayloadSchemaFormatIntegrationTest.java @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.examples.kafka; + +import io.github.springwolf.asyncapi.v3.jackson.AsyncApiSerializerService; +import io.github.springwolf.asyncapi.v3.jackson.DefaultAsyncApiSerializerService; +import io.github.springwolf.asyncapi.v3.model.AsyncAPI; +import io.github.springwolf.core.standalone.DefaultStandaloneApplication; +import io.github.springwolf.core.standalone.StandaloneApplication; +import io.github.springwolf.core.standalone.StandaloneEnvironmentLoader; +import org.junit.jupiter.api.Test; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.MapPropertySource; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class PayloadSchemaFormatIntegrationTest { + @Test + void asyncApiStandaloneArtifactTest() throws Exception { + // given + ConfigurableEnvironment environment = StandaloneEnvironmentLoader.load(); + environment + .getPropertySources() + .addFirst(new MapPropertySource( + "env", Map.of("springwolf.docket.payload-schema-format", "openapi_v3_1"))); + StandaloneApplication standaloneApplication = DefaultStandaloneApplication.builder() + .setEnvironment(environment) + .buildAndStart(); + + // when + AsyncAPI asyncApi = standaloneApplication.getAsyncApiService().getAsyncAPI(); + + // then + assertThat(asyncApi).isNotNull(); + + AsyncApiSerializerService serializerService = new DefaultAsyncApiSerializerService(); + String actual = serializerService.toJsonString(asyncApi); + String actualPatched = actual.replace("localhost:9092", "kafka:29092"); + Files.writeString(Path.of("src", "test", "resources", "asyncapi.openapiv31.actual.json"), actualPatched); + + // then + InputStream s = this.getClass().getResourceAsStream("/asyncapi.openapiv31.json"); + String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8).trim(); + assertThat(actualPatched).isEqualTo(expected); + } +} diff --git a/springwolf-examples/springwolf-kafka-example/src/test/resources/asyncapi.openapiv31.json b/springwolf-examples/springwolf-kafka-example/src/test/resources/asyncapi.openapiv31.json new file mode 100644 index 000000000..593445035 --- /dev/null +++ b/springwolf-examples/springwolf-kafka-example/src/test/resources/asyncapi.openapiv31.json @@ -0,0 +1,1950 @@ +{ + "asyncapi": "3.0.0", + "info": { + "title": "Springwolf example project - Kafka", + "version": "1.0.0", + "description": "Springwolf [example project](https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-kafka-example) to demonstrate springwolfs abilities, including **markdown** support for descriptions.", + "termsOfService": "http://asyncapi.org/terms", + "contact": { + "name": "springwolf", + "url": "https://github.com/springwolf/springwolf-core", + "email": "example@example.com" + }, + "license": { + "name": "Apache License 2.0" + }, + "x-generator": "springwolf" + }, + "defaultContentType": "application/json", + "servers": { + "kafka-server": { + "host": "kafka:29092", + "protocol": "kafka" + } + }, + "channels": { + "another-topic": { + "address": "another-topic", + "messages": { + "io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "avro-topic": { + "address": "avro-topic", + "messages": { + "io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "example-topic": { + "address": "example-topic", + "messages": { + "io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "integer-topic": { + "address": "integer-topic", + "messages": { + "java.lang.Integer": { + "$ref": "#/components/messages/java.lang.Integer" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "multi-payload-topic": { + "address": "multi-payload-topic", + "messages": { + "io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + }, + "io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto" + }, + "javax.money.MonetaryAmount": { + "$ref": "#/components/messages/javax.money.MonetaryAmount" + } + }, + "bindings": { + "kafka": { + "topic": "multi-payload-topic", + "partitions": 3, + "replicas": 1, + "topicConfiguration": { + "cleanup.policy": [ + "compact", + "delete" + ], + "retention.ms": 86400000, + "retention.bytes": -1, + "delete.retention.ms": 86400000, + "max.message.bytes": 1048588 + }, + "bindingVersion": "0.5.0" + } + } + }, + "no-payload-used-topic": { + "address": "no-payload-used-topic", + "messages": { + "PayloadNotUsed": { + "$ref": "#/components/messages/PayloadNotUsed" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "nullable-topic": { + "address": "nullable-topic", + "messages": { + "io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "protobuf-topic": { + "address": "protobuf-topic", + "messages": { + "io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "string-topic": { + "address": "string-topic", + "messages": { + "io.github.springwolf.examples.kafka.consumers.StringConsumer.StringEnvelope": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.consumers.StringConsumer.StringEnvelope" + }, + "java.lang.String": { + "$ref": "#/components/messages/java.lang.String" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "topic-defined-via-asyncPublisher-annotation": { + "address": "topic-defined-via-asyncPublisher-annotation", + "messages": { + "io.github.springwolf.examples.kafka.dtos.NestedPayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dtos.NestedPayloadDto" + } + }, + "servers": [ + { + "$ref": "#/servers/kafka-server" + } + ], + "bindings": { } + }, + "vehicle-topic": { + "address": "vehicle-topic", + "messages": { + "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "xml-topic": { + "address": "xml-topic", + "messages": { + "io.github.springwolf.examples.kafka.dtos.XmlPayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dtos.XmlPayloadDto" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "yaml-topic": { + "address": "yaml-topic", + "messages": { + "io.github.springwolf.examples.kafka.dtos.YamlPayloadDto": { + "$ref": "#/components/messages/io.github.springwolf.examples.kafka.dtos.YamlPayloadDto" + } + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + } + }, + "components": { + "schemas": { + "HeadersNotDocumented": { + "title": "HeadersNotDocumented", + "type": "object", + "properties": { }, + "description": "There can be headers, but they are not explicitly documented.", + "examples": [ + { } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "description": "There can be headers, but they are not explicitly documented.", + "title": "HeadersNotDocumented", + "type": "object" + } + }, + "HeadersNotUsed": { + "title": "HeadersNotUsed", + "type": "object", + "properties": { }, + "description": "No headers are present.", + "examples": [ + { } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "description": "No headers are present.", + "title": "HeadersNotUsed", + "type": "object" + } + }, + "PayloadNotUsed": { + "title": "PayloadNotUsed", + "type": "object", + "properties": { }, + "description": "No payload specified", + "examples": [ + { } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "description": "No payload specified", + "title": "PayloadNotUsed", + "type": "object" + } + }, + "SpringDefaultHeaderAndCloudEvent": { + "title": "SpringDefaultHeaderAndCloudEvent", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.NestedPayloadDto" + ], + "examples": [ + "io.github.springwolf.examples.kafka.dtos.NestedPayloadDto" + ] + }, + "ce_id": { + "type": "string", + "description": "CloudEvent Id Header", + "enum": [ + "2c60089e-6f39-459d-8ced-2d6df7e4c03a" + ], + "examples": [ + "2c60089e-6f39-459d-8ced-2d6df7e4c03a" + ] + }, + "ce_source": { + "type": "string", + "description": "CloudEvent Source Header", + "enum": [ + "http://localhost" + ], + "examples": [ + "http://localhost" + ] + }, + "ce_specversion": { + "type": "string", + "description": "CloudEvent Spec Version Header", + "enum": [ + "1.0" + ], + "examples": [ + "1.0" + ] + }, + "ce_subject": { + "type": "string", + "description": "CloudEvent Subject Header", + "enum": [ + "Springwolf example project - Kafka" + ], + "examples": [ + "Springwolf example project - Kafka" + ] + }, + "ce_time": { + "type": "string", + "description": "CloudEvent Time Header", + "enum": [ + "2023-10-28 20:01:23+00:00" + ], + "examples": [ + "2023-10-28 20:01:23+00:00" + ] + }, + "ce_type": { + "type": "string", + "description": "CloudEvent Payload Type Header", + "enum": [ + "NestedPayloadDto.v1" + ], + "examples": [ + "NestedPayloadDto.v1" + ] + }, + "content-type": { + "type": "string", + "description": "CloudEvent Content-Type Header", + "enum": [ + "application/json" + ], + "examples": [ + "application/json" + ] + } + }, + "description": "Spring __TypeId__ and CloudEvent Headers", + "examples": [ + { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.NestedPayloadDto", + "ce_id": "2c60089e-6f39-459d-8ced-2d6df7e4c03a", + "ce_source": "http://localhost", + "ce_specversion": "1.0", + "ce_subject": "Springwolf example project - Kafka", + "ce_time": "2023-10-28 20:01:23+00:00", + "ce_type": "NestedPayloadDto.v1", + "content-type": "application/json" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "description": "Spring __TypeId__ and CloudEvent Headers", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.NestedPayloadDto" + ], + "type": "string" + }, + "ce_id": { + "description": "CloudEvent Id Header", + "enum": [ + "2c60089e-6f39-459d-8ced-2d6df7e4c03a" + ], + "type": "string" + }, + "ce_source": { + "description": "CloudEvent Source Header", + "enum": [ + "http://localhost" + ], + "type": "string" + }, + "ce_specversion": { + "description": "CloudEvent Spec Version Header", + "enum": [ + "1.0" + ], + "type": "string" + }, + "ce_subject": { + "description": "CloudEvent Subject Header", + "enum": [ + "Springwolf example project - Kafka" + ], + "type": "string" + }, + "ce_time": { + "description": "CloudEvent Time Header", + "enum": [ + "2023-10-28 20:01:23+00:00" + ], + "type": "string" + }, + "ce_type": { + "description": "CloudEvent Payload Type Header", + "enum": [ + "NestedPayloadDto.v1" + ], + "type": "string" + }, + "content-type": { + "description": "CloudEvent Content-Type Header", + "enum": [ + "application/json" + ], + "type": "string" + } + }, + "title": "SpringDefaultHeaderAndCloudEvent", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-AnotherPayloadAvroDto": { + "title": "SpringKafkaDefaultHeaders-AnotherPayloadAvroDto", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto" + ], + "examples": [ + "io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto" + ] + } + }, + "examples": [ + { + "__TypeId__": "io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-AnotherPayloadAvroDto", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-AnotherPayloadDto": { + "title": "SpringKafkaDefaultHeaders-AnotherPayloadDto", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + ], + "examples": [ + "io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + ] + } + }, + "examples": [ + { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-AnotherPayloadDto", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-AnotherTopic": { + "title": "SpringKafkaDefaultHeaders-AnotherTopic", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Type ID" + }, + "my_uuid_field": { + "type": "string", + "description": "Event identifier", + "format": "uuid" + } + }, + "examples": [ + { + "__TypeId__": "string", + "my_uuid_field": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Type ID", + "type": "string" + }, + "my_uuid_field": { + "description": "Event identifier", + "format": "uuid", + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-AnotherTopic", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-ExamplePayloadDto-546532105": { + "title": "SpringKafkaDefaultHeaders-ExamplePayloadDto-546532105", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto" + ], + "examples": [ + "io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto" + ] + }, + "kafka_offset": { + "type": "integer", + "format": "int32", + "examples": [ + 0 + ] + }, + "kafka_receivedMessageKey": { + "type": "string", + "examples": [ + "\"string\"" + ] + }, + "kafka_recordMetadata": { + "type": "object", + "examples": [ + { } + ] + } + }, + "examples": [ + { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto", + "kafka_offset": 0, + "kafka_receivedMessageKey": "string", + "kafka_recordMetadata": { } + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto" + ], + "type": "string" + }, + "kafka_offset": { + "format": "int32", + "type": "integer" + }, + "kafka_receivedMessageKey": { + "type": "string" + }, + "kafka_recordMetadata": { + "type": "object" + } + }, + "title": "SpringKafkaDefaultHeaders-ExamplePayloadDto-546532105", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-Message": { + "title": "SpringKafkaDefaultHeaders-Message", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message" + ], + "examples": [ + "io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message" + ] + } + }, + "examples": [ + { + "__TypeId__": "io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-Message", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-MonetaryAmount": { + "title": "SpringKafkaDefaultHeaders-MonetaryAmount", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "javax.money.MonetaryAmount" + ], + "examples": [ + "javax.money.MonetaryAmount" + ] + } + }, + "examples": [ + { + "__TypeId__": "javax.money.MonetaryAmount" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "javax.money.MonetaryAmount" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-MonetaryAmount", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-PayloadNotUsed": { + "title": "SpringKafkaDefaultHeaders-PayloadNotUsed", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "PayloadNotUsed" + ], + "examples": [ + "PayloadNotUsed" + ] + } + }, + "examples": [ + { + "__TypeId__": "PayloadNotUsed" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "PayloadNotUsed" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-PayloadNotUsed", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-RequiredAndNullablePayloadDto": { + "title": "SpringKafkaDefaultHeaders-RequiredAndNullablePayloadDto", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto" + ], + "examples": [ + "io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto" + ] + } + }, + "examples": [ + { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-RequiredAndNullablePayloadDto", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-VehicleBase": { + "title": "SpringKafkaDefaultHeaders-VehicleBase", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase" + ], + "examples": [ + "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase" + ] + } + }, + "examples": [ + { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-VehicleBase", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-XmlPayloadDto": { + "title": "SpringKafkaDefaultHeaders-XmlPayloadDto", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.XmlPayloadDto" + ], + "examples": [ + "io.github.springwolf.examples.kafka.dtos.XmlPayloadDto" + ] + } + }, + "examples": [ + { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.XmlPayloadDto" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.XmlPayloadDto" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-XmlPayloadDto", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-YamlPayloadDto": { + "title": "SpringKafkaDefaultHeaders-YamlPayloadDto", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.YamlPayloadDto" + ], + "examples": [ + "io.github.springwolf.examples.kafka.dtos.YamlPayloadDto" + ] + } + }, + "examples": [ + { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.YamlPayloadDto" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "io.github.springwolf.examples.kafka.dtos.YamlPayloadDto" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-YamlPayloadDto", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-integer": { + "title": "SpringKafkaDefaultHeaders-integer", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "java.lang.Integer" + ], + "examples": [ + "java.lang.Integer" + ] + } + }, + "examples": [ + { + "__TypeId__": "java.lang.Integer" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "java.lang.Integer" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-integer", + "type": "object" + } + }, + "SpringKafkaDefaultHeaders-string": { + "title": "SpringKafkaDefaultHeaders-string", + "type": "object", + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "java.lang.String" + ], + "examples": [ + "java.lang.String" + ] + } + }, + "examples": [ + { + "__TypeId__": "java.lang.String" + } + ], + "x-json-schema": { + "$schema": "https://json-schema.org/draft-04/schema#", + "properties": { + "__TypeId__": { + "description": "Spring Type Id Header", + "enum": [ + "java.lang.String" + ], + "type": "string" + } + }, + "title": "SpringKafkaDefaultHeaders-string", + "type": "object" + } + }, + "io.github.springwolf.examples.kafka.consumers.StringConsumer.StringEnvelope": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "string", + "description": "Payload description using @Schema annotation and @AsyncApiPayload within envelope class", + "example": "\"string\"", + "maxLength": 100 + } + }, + "io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "example": { + "examplePayloadAvroDto": { + "someLong": 0, + "someString": "string" + }, + "someEnum": "FOO1" + }, + "properties": { + "examplePayloadAvroDto": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dto.avro.ExamplePayloadAvroDto" + }, + "someEnum": { + "type": "string", + "enum": [ + "FOO1", + "FOO2", + "FOO3" + ] + } + }, + "title": "AnotherPayloadAvroDto" + } + }, + "io.github.springwolf.examples.kafka.dto.avro.ExamplePayloadAvroDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "example": { + "someLong": 0, + "someString": "string" + }, + "properties": { + "someLong": { + "type": "integer", + "format": "int64" + }, + "someString": { + "type": "string" + } + }, + "title": "ExamplePayloadAvroDto" + } + }, + "io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "example": { + "someEnum": "FOO1", + "someLong": 0, + "someString": "string" + }, + "properties": { + "someEnum": { + "type": "string", + "enum": [ + "FOO1", + "FOO2", + "FOO3", + "UNRECOGNIZED" + ] + }, + "someLong": { + "type": "integer", + "format": "int64" + }, + "someString": { + "type": "string" + } + }, + "title": "Message" + } + }, + "io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "description": "Another payload model", + "example": { + "example": { + "someEnum": "FOO2", + "someLong": 5, + "someString": "some string value" + }, + "foo": "bar" + }, + "properties": { + "example": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto" + }, + "foo": { + "type": "string", + "description": "Foo field", + "example": "bar", + "maxLength": 100 + } + }, + "required": [ + "example" + ], + "title": "AnotherPayloadDto" + } + }, + "io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "description": "Example payload model demonstrating markdown text styling:\n**bold**, *cursive* and underlined\n", + "example": { + "someEnum": "FOO2", + "someLong": 5, + "someString": "some string value" + }, + "properties": { + "someEnum": { + "type": "string", + "description": "Some enum field", + "enum": [ + "FOO1", + "FOO2", + "FOO3" + ], + "example": "FOO2" + }, + "someLong": { + "type": "integer", + "format": "int64", + "description": "Some long field", + "example": 5, + "minimum": 0 + }, + "someString": { + "type": "string", + "description": "### Some string field with Markdown\n\n- **bold**\n- *cursive*\n- images: \"Springwolf\"\n- and code blocks (json, http, java)\n ```json\n {\n \"key1\":\"value1\",\n \"key2\":\"value2\"\n }\n ```\n", + "example": "some string value" + } + }, + "required": [ + "someEnum", + "someString" + ], + "title": "ExamplePayloadDto" + } + }, + "io.github.springwolf.examples.kafka.dtos.NestedPayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "description": "Payload model with nested complex types", + "example": { + "examplePayloads": [ + { + "someEnum": "FOO2", + "someLong": 5, + "someString": "some string value" + } + ], + "someStrings": [ + "some string value" + ] + }, + "properties": { + "examplePayloads": { + "type": "array", + "items": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto" + } + }, + "someStrings": { + "type": "array", + "items": { + "type": "string", + "description": "Some string field", + "example": "some string value" + }, + "uniqueItems": true + } + }, + "title": "NestedPayloadDto" + } + }, + "io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "description": "Demonstrate required and nullable. Note, @Schema is only descriptive without nullability check", + "example": { + "enumField": "COMPLEX1", + "notRequiredField": "string", + "requiredAndNullableField": "string", + "requiredButNullableField": "string", + "requiredField": "string" + }, + "properties": { + "enumField": { + "type": "string", + "description": "Follows OpenAPI 3.1 spec", + "enum": [ + "COMPLEX1", + "COMPLEX2" + ] + }, + "notRequiredField": { + "type": "string", + "description": "This field can be skipped, but value cannot be null" + }, + "requiredAndNullableField": { + "type": "string", + "description": "This field can be skipped, or value can be null or present" + }, + "requiredButNullableField": { + "type": "string", + "description": "This field must be present, but value can be null" + }, + "requiredField": { + "type": "string", + "description": "This field must be present, and value cannot be null" + } + }, + "required": [ + "enumField", + "requiredButNullableField", + "requiredField" + ], + "title": "RequiredAndNullablePayloadDto" + } + }, + "io.github.springwolf.examples.kafka.dtos.XmlPayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "string", + "example": "FOO10string", + "properties": { + "someAttribute": { + "type": "string", + "xml": { + "attribute": true + } + }, + "someEnum": { + "type": "string", + "enum": [ + "FOO1", + "FOO2", + "FOO3" + ] + }, + "someLong": { + "type": "integer", + "format": "int64" + }, + "someString": { + "type": "string" + } + }, + "title": "XmlPayloadDto" + } + }, + "io.github.springwolf.examples.kafka.dtos.YamlPayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "string", + "example": "someEnum: FOO1\nsomeLong: 0\nsomeString: string\n", + "properties": { + "someEnum": { + "type": "string", + "enum": [ + "FOO1", + "FOO2", + "FOO3" + ] + }, + "someLong": { + "type": "integer", + "format": "int64" + }, + "someString": { + "type": "string" + } + }, + "title": "YamlPayloadDto" + } + }, + "io.github.springwolf.examples.kafka.dtos.discriminator.EnginePower": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "example": { + "hp": 0, + "torque": 0 + }, + "properties": { + "hp": { + "type": "integer", + "format": "int32" + }, + "torque": { + "type": "integer", + "format": "int32" + } + }, + "title": "EnginePower" + } + }, + "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "description": "Demonstrates the use of discriminator for polymorphic deserialization (not publishable)", + "discriminator": { + "propertyName": "vehicleType", + "mapping": { + "VehicleElectricPayloadDto": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.discriminator.VehicleElectricPayloadDto", + "VehicleGasolinePayloadDto": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.discriminator.VehicleGasolinePayloadDto" + } + }, + "example": { + "batteryCapacity": 0, + "chargeTime": 0, + "enginePower": { + "hp": 0, + "torque": 0 + }, + "powerSource": "string", + "topSpeed": 0, + "vehicleType": "string" + }, + "oneOf": [ + { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.discriminator.VehicleElectricPayloadDto" + }, + { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.discriminator.VehicleGasolinePayloadDto" + } + ], + "properties": { + "enginePower": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.discriminator.EnginePower" + }, + "powerSource": { + "type": "string" + }, + "topSpeed": { + "type": "integer", + "format": "int32" + }, + "vehicleType": { + "type": "string" + } + }, + "title": "VehicleBase" + } + }, + "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleElectricPayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase" + }, + { + "type": "object", + "properties": { + "batteryCapacity": { + "type": "integer", + "format": "int32" + }, + "chargeTime": { + "type": "integer", + "format": "int32" + } + } + } + ], + "description": "Electric vehicle implementation of VehicleBase", + "example": { + "batteryCapacity": 0, + "chargeTime": 0, + "enginePower": { + "hp": 0, + "torque": 0 + }, + "powerSource": "string", + "topSpeed": 0, + "vehicleType": "string" + } + } + }, + "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleGasolinePayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase" + }, + { + "type": "object", + "properties": { + "fuelCapacity": { + "type": "integer", + "format": "int32" + } + } + } + ], + "description": "Gasoline vehicle implementation of VehicleBase", + "example": { + "enginePower": { + "hp": 0, + "torque": 0 + }, + "fuelCapacity": 0, + "powerSource": "string", + "topSpeed": 0, + "vehicleType": "string" + } + } + }, + "java.lang.Integer": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "integer", + "format": "int32", + "example": 0 + } + }, + "java.lang.String": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "string", + "example": "\"string\"" + } + }, + "javax.money.MonetaryAmount": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "example": { + "amount": 99.99, + "currency": "USD" + }, + "properties": { + "amount": { + "type": "number", + "example": 99.99, + "minimum": 0.01 + }, + "currency": { + "type": "string", + "example": "USD" + } + } + } + } + }, + "messages": { + "PayloadNotUsed": { + "headers": { + "$ref": "#/components/schemas/SpringKafkaDefaultHeaders-PayloadNotUsed" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "title": "PayloadNotUsed", + "type": "object", + "properties": { }, + "description": "No payload specified" + } + }, + "name": "PayloadNotUsed", + "title": "PayloadNotUsed", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "io.github.springwolf.examples.kafka.consumers.StringConsumer.StringEnvelope": { + "headers": { + "$ref": "#/components/schemas/HeadersNotUsed" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.consumers.StringConsumer.StringEnvelope" + } + }, + "name": "StringPayload", + "title": "StringEnvelope", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto": { + "headers": { + "$ref": "#/components/schemas/HeadersNotDocumented" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto" + } + }, + "name": "io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto", + "title": "AnotherPayloadAvroDto", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message": { + "headers": { + "$ref": "#/components/schemas/SpringKafkaDefaultHeaders-Message" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message" + } + }, + "name": "io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message", + "title": "Message", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto": { + "headers": { + "$ref": "#/components/schemas/SpringKafkaDefaultHeaders-AnotherTopic" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + } + }, + "name": "io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto", + "title": "AnotherPayloadDto", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto": { + "headers": { + "$ref": "#/components/schemas/SpringKafkaDefaultHeaders-ExamplePayloadDto-546532105" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto" + } + }, + "name": "io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto", + "title": "ExamplePayloadDto", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "io.github.springwolf.examples.kafka.dtos.NestedPayloadDto": { + "headers": { + "$ref": "#/components/schemas/SpringDefaultHeaderAndCloudEvent" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.NestedPayloadDto" + } + }, + "name": "io.github.springwolf.examples.kafka.dtos.NestedPayloadDto", + "title": "NestedPayloadDto", + "bindings": { + "kafka": { + "key": { + "type": "string", + "description": "Kafka Producer Message Key", + "examples": [ + "example-key" + ] + }, + "bindingVersion": "0.5.0" + } + } + }, + "io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto": { + "headers": { + "$ref": "#/components/schemas/SpringKafkaDefaultHeaders-RequiredAndNullablePayloadDto" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto" + } + }, + "name": "io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto", + "title": "RequiredAndNullablePayloadDto", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "io.github.springwolf.examples.kafka.dtos.XmlPayloadDto": { + "headers": { + "$ref": "#/components/schemas/HeadersNotDocumented" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.XmlPayloadDto" + } + }, + "contentType": "text/xml", + "name": "io.github.springwolf.examples.kafka.dtos.XmlPayloadDto", + "title": "XmlPayloadDto", + "description": "Showcases a xml based message", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "io.github.springwolf.examples.kafka.dtos.YamlPayloadDto": { + "headers": { + "$ref": "#/components/schemas/HeadersNotDocumented" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.YamlPayloadDto" + } + }, + "contentType": "application/yaml", + "name": "io.github.springwolf.examples.kafka.dtos.YamlPayloadDto", + "title": "YamlPayloadDto", + "description": "Showcases a yaml based message", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase": { + "headers": { + "$ref": "#/components/schemas/SpringKafkaDefaultHeaders-VehicleBase" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase" + } + }, + "name": "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase", + "title": "VehicleBase", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "java.lang.Integer": { + "headers": { + "$ref": "#/components/schemas/SpringKafkaDefaultHeaders-integer" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "integer", + "format": "int32", + "example": 0 + } + } + }, + "name": "java.lang.Integer", + "title": "integer", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "java.lang.String": { + "headers": { + "$ref": "#/components/schemas/SpringKafkaDefaultHeaders-string" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "string", + "example": "\"string\"" + } + } + }, + "name": "java.lang.String", + "title": "string", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + } + }, + "javax.money.MonetaryAmount": { + "headers": { + "$ref": "#/components/schemas/SpringKafkaDefaultHeaders-MonetaryAmount" + }, + "payload": { + "schemaFormat": "application/vnd.aai.asyncapi+json;version=3.0.0", + "schema": { + "$ref": "#/components/schemas/javax.money.MonetaryAmount" + } + }, + "name": "javax.money.MonetaryAmount", + "title": "MonetaryAmount", + "bindings": { + "kafka": { + "key": { + "type": "string", + "description": "Kafka Consumer Message Key", + "examples": [ + "example-key" + ] + }, + "bindingVersion": "0.5.0" + } + } + } + } + }, + "operations": { + "another-topic_receive_receiveAnotherPayloadBatched": { + "action": "receive", + "channel": { + "$ref": "#/channels/another-topic" + }, + "bindings": { + "kafka": { + "groupId": { + "type": "string", + "enum": [ + "example-group-id" + ] + }, + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/another-topic/messages/io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + } + ] + }, + "another-topic_send_sendMessage": { + "action": "send", + "channel": { + "$ref": "#/channels/another-topic" + }, + "title": "another-topic_send", + "description": "Auto-generated description", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/another-topic/messages/io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + } + ] + }, + "avro-topic_receive_receiveExampleAvroPayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/avro-topic" + }, + "title": "avro-topic_receive", + "description": "Requires a running kafka-schema-registry. See docker-compose.yml to start it", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/avro-topic/messages/io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto" + } + ] + }, + "example-topic_receive_receiveExamplePayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/example-topic" + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/example-topic/messages/io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto" + } + ] + }, + "integer-topic_receive_receiveIntegerPayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/integer-topic" + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/integer-topic/messages/java.lang.Integer" + } + ] + }, + "multi-payload-topic_receive_ExampleClassLevelKafkaListener": { + "action": "receive", + "channel": { + "$ref": "#/channels/multi-payload-topic" + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/multi-payload-topic/messages/io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto" + }, + { + "$ref": "#/channels/multi-payload-topic/messages/io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + }, + { + "$ref": "#/channels/multi-payload-topic/messages/javax.money.MonetaryAmount" + } + ] + }, + "multi-payload-topic_receive_receiveMonetaryAmount": { + "action": "receive", + "channel": { + "$ref": "#/channels/multi-payload-topic" + }, + "title": "multi-payload-topic_receive", + "description": "Override description in the AsyncListener annotation with servers at kafka:29092", + "bindings": { + "kafka": { + "groupId": { + "type": "string", + "enum": [ + "foo-groupId" + ] + }, + "clientId": { + "type": "string", + "enum": [ + "foo-clientId" + ] + }, + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/multi-payload-topic/messages/javax.money.MonetaryAmount" + } + ] + }, + "no-payload-used-topic_receive_receiveExamplePayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/no-payload-used-topic" + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/no-payload-used-topic/messages/PayloadNotUsed" + } + ] + }, + "nullable-topic_receive_receiveNullablePayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/nullable-topic" + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/nullable-topic/messages/io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto" + } + ] + }, + "protobuf-topic_receive_receiveExampleProtobufPayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/protobuf-topic" + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/protobuf-topic/messages/io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message" + } + ] + }, + "string-topic_receive_receiveStringPayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/string-topic" + }, + "title": "string-topic_receive", + "description": "Final classes (like String) can be documented using an envelope class and the @AsyncApiPayload annotation.", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/string-topic/messages/io.github.springwolf.examples.kafka.consumers.StringConsumer.StringEnvelope" + }, + { + "$ref": "#/channels/string-topic/messages/java.lang.String" + } + ] + }, + "topic-defined-via-asyncPublisher-annotation_send_sendMessage": { + "action": "send", + "channel": { + "$ref": "#/channels/topic-defined-via-asyncPublisher-annotation" + }, + "title": "topic-defined-via-asyncPublisher-annotation_send", + "description": "Custom, optional description defined in the AsyncPublisher annotation", + "bindings": { + "kafka": { + "clientId": { + "type": "string", + "enum": [ + "foo-clientId" + ] + }, + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/topic-defined-via-asyncPublisher-annotation/messages/io.github.springwolf.examples.kafka.dtos.NestedPayloadDto" + } + ] + }, + "vehicle-topic_receive_receiveExamplePayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/vehicle-topic" + }, + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/vehicle-topic/messages/io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase" + } + ] + }, + "xml-topic_receive_receiveExamplePayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/xml-topic" + }, + "title": "xml-topic_receive", + "description": "Auto-generated description", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/xml-topic/messages/io.github.springwolf.examples.kafka.dtos.XmlPayloadDto" + } + ] + }, + "yaml-topic_receive_receiveExamplePayload": { + "action": "receive", + "channel": { + "$ref": "#/channels/yaml-topic" + }, + "title": "yaml-topic_receive", + "description": "Auto-generated description", + "bindings": { + "kafka": { + "bindingVersion": "0.5.0" + } + }, + "messages": [ + { + "$ref": "#/channels/yaml-topic/messages/io.github.springwolf.examples.kafka.dtos.YamlPayloadDto" + } + ] + } + } +} \ No newline at end of file From 014bcb98ac0542be07505d3c2aeb6b229c1ced54 Mon Sep 17 00:00:00 2001 From: tvahrst Date: Sat, 15 Nov 2025 09:27:38 +0100 Subject: [PATCH 14/14] spotless --- .../springwolf/core/asyncapi/grouping/GroupingService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java index 1ca4ebd54..0e5487efe 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java @@ -284,9 +284,9 @@ private static Set findUnmarkedNestedSchemasForAsyncAPISchema( private static Set findUnmarkedNestedSchemasForOpenAPISchema( MarkingContext markingContext, Schema openapiSchema) { final Stream propertySchemas; - if(openapiSchema.getProperties() != null) { + if (openapiSchema.getProperties() != null) { propertySchemas = openapiSchema.getProperties().values().stream(); - }else { + } else { propertySchemas = Stream.empty(); }