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..fb6e4363c 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 @@ -10,7 +10,8 @@ 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.asyncapi.schemas.SwaggerSchemaMapper; +import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.swagger.v3.core.util.Json; import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.BooleanSchema; @@ -35,14 +36,14 @@ class JsonSchemaGeneratorTest { private final ObjectMapper mapper = Json.mapper(); - private final SwaggerSchemaUtil swaggerSchemaUtil = new SwaggerSchemaUtil(); + private final SwaggerSchemaMapper swaggerSchemaMapper = new SwaggerSchemaMapper(new SpringwolfConfigProperties()); private final JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(); @ParameterizedTest @MethodSource void validateJsonSchemaTest(String expectedJsonSchema, Supplier> asyncApiSchema) throws Exception { // given - SchemaObject actualSchema = swaggerSchemaUtil.mapSchema(asyncApiSchema.get()); + ComponentSchema actualSchema = swaggerSchemaMapper.mapSchema(asyncApiSchema.get()); // 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/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-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..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 @@ -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,17 +34,17 @@ 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); /** * 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 registerSchema(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 c5eeba1d4..da4a65f51 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 @@ -26,7 +26,6 @@ public class DefaultComponentsService implements ComponentsService { private final SwaggerSchemaService schemaService; - private final SpringwolfConfigProperties springwolfConfigProperties; /** * maps a schema name (key) to a detected corresponding {@link ComponentSchema}. @@ -51,11 +50,10 @@ 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); payload.referencedSchemas().forEach(schemas::putIfAbsent); return payload.rootSchema(); @@ -65,21 +63,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. + * 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 registerSchema(SchemaObject headers) { - log.debug("Registering schema for {}", headers.getTitle()); + public String registerSchema(SchemaObject schemaWithoutRef) { + log.debug("Registering schema for {}", schemaWithoutRef.getTitle()); - SchemaObject headerSchema = schemaService.extractSchema(headers); - this.schemas.putIfAbsent(headers.getTitle(), ComponentSchema.of(headerSchema)); + 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/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..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 @@ -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,12 +278,17 @@ 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( 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/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/headers/HeaderSchemaObjectMerger.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderSchemaObjectMerger.java index 2ff2f8ffd..4ebf188aa 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderSchemaObjectMerger.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderSchemaObjectMerger.java @@ -24,7 +24,6 @@ public static SchemaObject merge(SchemaObject initial, SchemaObject... schemas) SchemaObject.SchemaObjectBuilder headerSchemaBuilder = SchemaObject.builder().type(SchemaType.OBJECT); - String title = initial.getTitle(); String description = initial.getDescription(); Map headerProperties = new HashMap<>(initial.getProperties()); 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..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 @@ -42,8 +42,8 @@ public MessageObject buildMessage(AsyncOperation operationData, Method method) { Map messageBinding = AsyncAnnotationUtil.processMessageBindingFromAnnotation(method, messageBindingProcessors); - var messagePayload = MessagePayload.of( - MultiFormatSchema.builder().schema(payloadSchema.payload()).build()); + 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/payload/internal/PayloadService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/internal/PayloadService.java index 14ce82f4c..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 @@ -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); 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..dc41e91e2 --- /dev/null +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/ModelConvertersProvider.java @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.asyncapi.schemas; + +import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; +import io.swagger.v3.core.converter.ModelConverter; +import io.swagger.v3.core.converter.ModelConverters; + +import java.util.List; + +/** + * Provides fully prepared {@link io.swagger.v3.core.converter.ModelConverters} for openapi v3 and v3.1 + * requirements. + */ +public class ModelConvertersProvider { + + private final SpringwolfConfigProperties springwolfConfigProperties; + private final ModelConverters converter_openapi30; + private final ModelConverters converter_openapi31; + + public ModelConvertersProvider( + SpringwolfConfigProperties springwolfConfigProperties, List externalModelConverters) { + this.springwolfConfigProperties = springwolfConfigProperties; + + converter_openapi30 = new ModelConverters(false); + converter_openapi31 = new ModelConverters(true); + externalModelConverters.forEach(converter_openapi30::addConverter); + externalModelConverters.forEach(converter_openapi31::addConverter); + } + + public ModelConverters getModelConverter() { + return switch (springwolfConfigProperties.getDocket().getPayloadSchemaFormat()) { + 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/SwaggerSchemaUtil.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaMapper.java similarity index 59% rename from springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaUtil.java rename to springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaMapper.java index b8311c328..ea920ff4f 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/SwaggerSchemaMapper.java @@ -4,10 +4,11 @@ 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.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.swagger.v3.oas.models.media.Schema; import lombok.RequiredArgsConstructor; import org.springframework.lang.Nullable; @@ -23,9 +24,11 @@ * Utils class providing services to map between Swagger schemas and AsyncApi schemas. */ @RequiredArgsConstructor -public class SwaggerSchemaUtil { +public class SwaggerSchemaMapper { - public Map mapSchemasMap(Map schemaMap) { + private final SpringwolfConfigProperties springwolfConfigProperties; + + public Map mapSchemasMap(Map schemaMap) { return schemaMap.entrySet().stream() .map(entry -> Map.entry(entry.getKey(), mapSchema(entry.getValue()))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); @@ -37,28 +40,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 * @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) { + if (swaggerSchema.get$ref() != null) { + return ComponentSchema.of(new SchemaReference(swaggerSchema.get$ref())); } - return ComponentSchema.of(mapSchema(schema)); + return mapSchema(swaggerSchema); } /** - * 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 + * @param swaggerSchema the given Swagger schema instance * @return the resulting AsnycApi SchemaObject */ - public SchemaObject mapSchema(Schema value) { + public ComponentSchema mapSchema(Schema swaggerSchema) { + PayloadSchemaFormat payloadSchemaFormat = + springwolfConfigProperties.getDocket().getPayloadSchemaFormat(); + return switch (payloadSchemaFormat) { + case OPENAPI_V3, OPENAPI_V3_1 -> ComponentSchema.of( + new MultiFormatSchema(payloadSchemaFormat.getSchemaFormat().value, swaggerSchema)); + case ASYNCAPI_V3 -> ComponentSchema.of(mapSwaggerSchemaToAsyncApiSchema(swaggerSchema)); + }; + } + + /** + * 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,14 +91,14 @@ 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()))) @@ -83,32 +107,32 @@ public SchemaObject mapSchema(Schema value) { 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 +142,67 @@ 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.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)) - .collect(Collectors.toList())); + builder.allOf(allOf.stream().map(schema -> mapSchemaOrRef(schema)).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)) - .collect(Collectors.toList())); + builder.oneOf(oneOf.stream().map(schema -> mapSchemaOrRef(schema)).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)) - .collect(Collectors.toList())); + builder.anyOf(anyOf.stream().map(schema -> mapSchemaOrRef(schema)).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)); } - Schema items = value.getItems(); - if (items != null && "array".equals(value.getType())) { + Schema items = swaggerSchema.getItems(); + if (items != null && "array".equals(swaggerSchema.getType())) { builder.items(mapSchemaOrRef(items)); } - 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; } @@ -222,19 +238,15 @@ public Object unwrapSchema(Object schema) { } /** - * tries to transforms the given schema object to an Swagger Schema. 'schema' may be an asnycapi {@link SchemaObject} + * tries to transforms the given schema object to a Swagger Schema. 'schema' may be an asnycapi {@link SchemaObject} * or an {@link ComponentSchema} object. If schema is a {@link ComponentSchema} it may contain: *
    *
  • a {@link SchemaObject} which is handled the same way a directly provided {@link SchemaObject} is handled
  • *
  • 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}
    • - *
    - *
  • + *
  • a {@link MultiFormatSchema}.
  • *
* if no type is matching, a Runtime Exception is thrown. + * * @param schema Object representing an schema. * @return the resulting Schema */ @@ -262,6 +274,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/asyncapi/schemas/SwaggerSchemaService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java index 8ef9a69d6..c518df12c 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 @@ -9,7 +9,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; @@ -18,6 +17,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; @@ -34,23 +34,13 @@ import static io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties.ConfigDocket.DEFAULT_CONTENT_TYPE; @Slf4j +@RequiredArgsConstructor public class SwaggerSchemaService { - private final ModelConverters converter = ModelConverters.getInstance(); - private final List schemaPostProcessors; - private final SwaggerSchemaUtil swaggerSchemaUtil; - private final SpringwolfConfigProperties properties; - - public SwaggerSchemaService( - List externalModelConverters, - List schemaPostProcessors, - SwaggerSchemaUtil swaggerSchemaUtil, - SpringwolfConfigProperties properties) { - externalModelConverters.forEach(converter::addConverter); - this.schemaPostProcessors = schemaPostProcessors; - this.swaggerSchemaUtil = swaggerSchemaUtil; - this.properties = properties; - } + private final SpringwolfConfigProperties properties; + private final List schemaPostProcessors; + private final SwaggerSchemaMapper swaggerSchemaMapper; + private final ModelConvertersProvider modelConvertersProvider; public record ExtractedSchemas(ComponentSchema rootSchema, Map referencedSchemas) {} @@ -60,29 +50,29 @@ 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 SchemaObject extractSchema(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()))) + Map.entry(property.getKey(), (Schema) swaggerSchemaMapper.mapToSwagger(property.getValue()))) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); headerSchema.setProperties(properties); @@ -90,25 +80,38 @@ public SchemaObject extractSchema(SchemaObject headers) { 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); + // convert Swagger schema back to an AsyncApi SchemaObject + return swaggerSchemaMapper.mapSchema(headerSchema); } - public ExtractedSchemas extractSchema(Class type) { + public ExtractedSchemas postProcessSimpleSchema(Class type) { return this.resolveSchema(type, ""); } + /** + * 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 + * @return + */ public ExtractedSchemas resolveSchema(Type type, String contentType) { String actualContentType = StringUtils.isBlank(contentType) ? properties.getDocket().getDefaultContentType() : contentType; + + // use swagger to resolve type to a swagger ResolvedSchema Object. + ModelConverters converterToUse = modelConvertersProvider.getModelConverter(); + 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 = swaggerSchemaMapper.mapSchema(stringPropertySchema); + return new ExtractedSchemas(stringComponentSchema, Map.of()); } else { Map newSchemasToProcess = new LinkedHashMap<>(resolvedSchema.referencedSchemas); newSchemasToProcess.putIfAbsent(getNameFromType(type), resolvedSchema.schema); @@ -129,12 +132,8 @@ public ExtractedSchemas resolveSchema(Type type, String contentType) { * @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)); - }); + ComponentSchema rootComponentSchema = swaggerSchemaMapper.mapSchemaOrRef(rootSchema); + Map referencedComponentSchemas = swaggerSchemaMapper.mapSchemasMap(referencedSchemas); return new ExtractedSchemas(rootComponentSchema, referencedComponentSchemas); } 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..6785a5bc2 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,8 +38,9 @@ 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.SwaggerSchemaMapper; 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.docket.AsyncApiDocketService; import io.github.springwolf.core.configuration.docket.DefaultAsyncApiDocketService; @@ -109,26 +110,32 @@ public OperationsService operationsService(List ope @Bean @ConditionalOnMissingBean - public SwaggerSchemaUtil swaggerSchemaUtil() { - return new SwaggerSchemaUtil(); + public SwaggerSchemaMapper swaggerSchemaUtil(SpringwolfConfigProperties springwolfConfigProperties) { + return new SwaggerSchemaMapper(springwolfConfigProperties); } @Bean @ConditionalOnMissingBean - public ComponentsService componentsService( - SwaggerSchemaService schemaService, SpringwolfConfigProperties springwolfConfigProperties) { - return new DefaultComponentsService(schemaService, springwolfConfigProperties); + public ComponentsService componentsService(SwaggerSchemaService schemaService) { + return new DefaultComponentsService(schemaService); } @Bean @ConditionalOnMissingBean public SwaggerSchemaService schemasService( - List modelConverters, + SpringwolfConfigProperties springwolfConfigProperties, List schemaPostProcessors, - SwaggerSchemaUtil swaggerSchemaUtil, - SpringwolfConfigProperties springwolfConfigProperties) { + SwaggerSchemaMapper swaggerSchemaMapper, + ModelConvertersProvider modelConvertersProvider) { return new SwaggerSchemaService( - modelConverters, schemaPostProcessors, swaggerSchemaUtil, springwolfConfigProperties); + springwolfConfigProperties, schemaPostProcessors, swaggerSchemaMapper, modelConvertersProvider); + } + + @Bean + @ConditionalOnMissingBean + public ModelConvertersProvider modelConvertersProvider( + SpringwolfConfigProperties springwolfConfigProperties, List modelConverters) { + return new ModelConvertersProvider(springwolfConfigProperties, modelConverters); } @Bean 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..3022a4bcc --- /dev/null +++ b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/properties/PayloadSchemaFormat.java @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.configuration.properties; + +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), + 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/DefaultComponentsServiceIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsServiceIntegrationTest.java index 90a4b4ad8..b6934d163 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.SwaggerSchemaMapper; 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,10 +29,15 @@ 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 ComponentsService componentsService = new DefaultComponentsService(schemaService, configProperties); + private final SwaggerSchemaService schemaService = new SwaggerSchemaService( + configProperties, + List.of(), + new SwaggerSchemaMapper(configProperties), + new ModelConvertersProvider(configProperties, List.of(titleModelConverter))); + + private final ComponentsService componentsService = new DefaultComponentsService(schemaService); @Nested class AllSchemaFields { diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsServiceTest.java index 0ca8bde10..cd19803f7 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsServiceTest.java @@ -8,7 +8,6 @@ import com.fasterxml.jackson.databind.SerializationFeature; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; -import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.swagger.v3.core.util.Json; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -33,7 +32,7 @@ class DefaultComponentsServiceTest { @BeforeEach void setUp() { - componentsService = new DefaultComponentsService(schemaService, new SpringwolfConfigProperties()); + componentsService = new DefaultComponentsService(schemaService); } @Test 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..e958286ea 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,8 +14,9 @@ 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.SwaggerSchemaMapper; 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.github.springwolf.core.fixtures.ClasspathUtil; @@ -50,13 +51,12 @@ class DefaultJsonComponentsServiceIntegrationTest { private final SpringwolfConfigProperties springwolfConfigProperties = new SpringwolfConfigProperties(); private final SwaggerSchemaService schemaService = new SwaggerSchemaService( - List.of(new SchemaTitleModelConverter()), + springwolfConfigProperties, List.of(new ExampleGeneratorPostProcessor( new SchemaWalkerProvider(List.of(new DefaultSchemaWalker<>(new ExampleJsonValueGenerator()))))), - new SwaggerSchemaUtil(), - springwolfConfigProperties); - private final ComponentsService componentsService = - new DefaultComponentsService(schemaService, springwolfConfigProperties); + new SwaggerSchemaMapper(springwolfConfigProperties), + new ModelConvertersProvider(springwolfConfigProperties, List.of(new SchemaTitleModelConverter()))); + private final ComponentsService componentsService = new DefaultComponentsService(schemaService); private static final ObjectMapper objectMapper = Json.mapper().enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); 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..b7fbfd552 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,8 +11,9 @@ 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.SwaggerSchemaMapper; 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.github.springwolf.core.fixtures.ClasspathUtil; @@ -51,12 +52,12 @@ class DefaultXmlComponentsServiceIntegrationTest { private final SpringwolfConfigProperties configProperties = new SpringwolfConfigProperties(); private final SwaggerSchemaService schemaService = new SwaggerSchemaService( - List.of(new SchemaTitleModelConverter()), + configProperties, List.of(new ExampleGeneratorPostProcessor(new SchemaWalkerProvider(List.of( new DefaultSchemaWalker<>(new ExampleXmlValueGenerator(new DefaultExampleXmlValueSerializer())))))), - new SwaggerSchemaUtil(), - configProperties); - private final ComponentsService componentsService = new DefaultComponentsService(schemaService, configProperties); + new SwaggerSchemaMapper(configProperties), + new ModelConvertersProvider(configProperties, List.of(new SchemaTitleModelConverter()))); + private final ComponentsService componentsService = new DefaultComponentsService(schemaService); @Test void getSchemas() throws Exception { 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..0473df7ef 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,8 +16,9 @@ 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.SwaggerSchemaMapper; 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.github.springwolf.core.fixtures.ClasspathUtil; @@ -54,13 +55,12 @@ class DefaultYamlComponentsServiceIntegrationTest { new ExampleJsonValueGenerator(), new DefaultExampleYamlValueSerializer(), springwolfConfigProperties); private final SwaggerSchemaService schemaService = new SwaggerSchemaService( - List.of(new SchemaTitleModelConverter()), + springwolfConfigProperties, List.of(new ExampleGeneratorPostProcessor( new SchemaWalkerProvider(List.of(new DefaultSchemaWalker<>(exampleYamlValueGenerator))))), - new SwaggerSchemaUtil(), - new SpringwolfConfigProperties()); - private final ComponentsService componentsService = - new DefaultComponentsService(schemaService, springwolfConfigProperties); + new SwaggerSchemaMapper(springwolfConfigProperties), + new ModelConvertersProvider(springwolfConfigProperties, List.of(new SchemaTitleModelConverter()))); + private final ComponentsService componentsService = new DefaultComponentsService(schemaService); private static final ObjectMapper objectMapper = Json.mapper().enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaMapperTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaMapperTest.java new file mode 100644 index 000000000..ada1865f2 --- /dev/null +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaMapperTest.java @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: Apache-2.0 +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; +import io.github.springwolf.asyncapi.v3.model.schema.SchemaType; +import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaMapper; +import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat; +import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; +import io.swagger.v3.oas.models.ExternalDocumentation; +import io.swagger.v3.oas.models.media.Discriminator; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.Schema; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class SwaggerSchemaMapperTest { + + private final SpringwolfConfigProperties configProperties = new SpringwolfConfigProperties(); + private final SwaggerSchemaMapper swaggerSchemaMapper = new SwaggerSchemaMapper(configProperties); + + @AfterEach + void tearDown() { + configProperties + .getDocket() + .setPayloadSchemaFormat( + new SpringwolfConfigProperties().getDocket().getPayloadSchemaFormat()); + } + + @Nested + class MapSchemaOrRef { + + @Test + void mapToOpenApiV3Schema() { + // given + configProperties.getDocket().setPayloadSchemaFormat(PayloadSchemaFormat.OPENAPI_V3); + + ObjectSchema schema = new ObjectSchema(); + schema.setType(SchemaType.STRING); + ExternalDocumentation externalDocs = new ExternalDocumentation(); + externalDocs.setDescription("description"); + externalDocs.setUrl("url"); + schema.setExternalDocs(externalDocs); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchemaOrRef(schema); + + // 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 + configProperties.getDocket().setPayloadSchemaFormat(PayloadSchemaFormat.OPENAPI_V3_1); + + ObjectSchema schema = new ObjectSchema(); + schema.setType(SchemaType.STRING); + ExternalDocumentation externalDocs = new ExternalDocumentation(); + externalDocs.setDescription("description"); + externalDocs.setUrl("url"); + schema.setExternalDocs(externalDocs); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchemaOrRef(schema); + + // 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 + configProperties.getDocket().setPayloadSchemaFormat(PayloadSchemaFormat.OPENAPI_V3); + + ObjectSchema schema = new ObjectSchema(); + schema.set$ref("#/components/schemas/MySchema"); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchemaOrRef(schema); + + // then + assertThat(componentSchema.getReference()).isEqualTo(new SchemaReference("#/components/schemas/MySchema")); + } + + @Test + void mapSchema() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setType(SchemaType.STRING); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchemaOrRef(schema); + + // then + assertThat(componentSchema.getSchema().getType()).containsExactly(SchemaType.STRING); + } + } + + @Nested + class MapSchema { + + @Test + void mapToOpenApiV3Schema() { + // given + configProperties.getDocket().setPayloadSchemaFormat(PayloadSchemaFormat.OPENAPI_V3); + + ObjectSchema schema = new ObjectSchema(); + schema.setType(SchemaType.STRING); + ExternalDocumentation externalDocs = new ExternalDocumentation(); + externalDocs.setDescription("description"); + externalDocs.setUrl("url"); + schema.setExternalDocs(externalDocs); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // 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 + configProperties.getDocket().setPayloadSchemaFormat(PayloadSchemaFormat.OPENAPI_V3_1); + + ObjectSchema schema = new ObjectSchema(); + schema.setType(SchemaType.STRING); + ExternalDocumentation externalDocs = new ExternalDocumentation(); + externalDocs.setDescription("description"); + externalDocs.setUrl("url"); + schema.setExternalDocs(externalDocs); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // 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 + ObjectSchema schema = new ObjectSchema(); + ExternalDocumentation externalDocs = new ExternalDocumentation(); + externalDocs.setDescription("description"); + externalDocs.setUrl("url"); + schema.setExternalDocs(externalDocs); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getExternalDocs().getDescription()) + .isEqualTo(externalDocs.getDescription()); + assertThat(componentSchema.getSchema().getExternalDocs().getUrl()).isEqualTo(externalDocs.getUrl()); + } + + @Test + void mapDeprecated() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setDeprecated(true); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getDeprecated()).isTrue(); + } + + @Test + void mapTitle() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setTitle("title"); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getTitle()).isEqualTo(schema.getTitle()); + } + + @Test + void mapType() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setType(SchemaType.STRING); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getType()).containsExactly(schema.getType()); + } + + @Test + void mapProperties() { + // given + ObjectSchema schema = new ObjectSchema(); + ObjectSchema property = new ObjectSchema(); + property.setType(SchemaType.STRING); + schema.addProperty("property", property); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(((ComponentSchema) + componentSchema.getSchema().getProperties().get("property")) + .getSchema() + .getType()) + .containsExactly(property.getType()); + } + + @Test + void mapDescription() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setDescription("description"); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getDescription()).isEqualTo(schema.getDescription()); + } + + @Test + void mapFormat() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setFormat("format"); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getFormat()).isEqualTo(schema.getFormat()); + } + + @Test + void mapPattern() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setPattern("pattern"); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getPattern()).isEqualTo(schema.getPattern()); + } + + @Test + void mapExclusiveMinimum() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setMinimum(BigDecimal.ONE); + schema.setExclusiveMinimum(true); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getExclusiveMinimum()).isEqualTo(schema.getMinimum()); + assertThat(componentSchema.getSchema().getMinimum()).isNull(); + } + + @Test + void mapExclusiveMinimumValue() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setExclusiveMinimumValue(BigDecimal.ONE); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getExclusiveMinimum()).isEqualTo(schema.getExclusiveMinimumValue()); + assertThat(componentSchema.getSchema().getMinimum()).isNull(); + } + + @Test + void mapExclusiveMaximum() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setMaximum(BigDecimal.ONE); + schema.setExclusiveMaximum(true); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getExclusiveMaximum()).isEqualTo(schema.getMaximum()); + assertThat(componentSchema.getSchema().getMaximum()).isNull(); + } + + @Test + void mapExclusiveMaximumValue() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setExclusiveMaximumValue(BigDecimal.ONE); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getExclusiveMaximum()).isEqualTo(schema.getExclusiveMaximumValue()); + assertThat(componentSchema.getSchema().getMaximum()).isNull(); + } + + @Test + void mapMinimum() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setMinimum(BigDecimal.ONE); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getMinimum()).isEqualTo(schema.getMinimum()); + assertThat(componentSchema.getSchema().getExclusiveMinimum()).isNull(); + } + + @Test + void mapMaximum() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setMaximum(BigDecimal.ONE); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getMaximum()).isEqualTo(schema.getMaximum()); + assertThat(componentSchema.getSchema().getExclusiveMaximum()).isNull(); + } + + @Test + void mapMultipleOf() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setMultipleOf(BigDecimal.ONE); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getMultipleOf()).isEqualTo(schema.getMultipleOf()); + } + + @Test + void mapMinLength() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setMinLength(1); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getMinLength()).isEqualTo(schema.getMinLength()); + } + + @Test + void mapMaxLength() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setMaxLength(1); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getMaxLength()).isEqualTo(schema.getMaxLength()); + } + + @Test + void mapEnum() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setEnum(List.of("enum1", "enum2")); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getEnumValues()).isEqualTo(schema.getEnum()); + } + + @Test + void mapExample() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setExample("example"); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getExamples()).isEqualTo(List.of(schema.getExample())); + } + + @Test + void mapAdditionalProperties() { + // given + ObjectSchema schema = new ObjectSchema(); + ObjectSchema additionalProperties = new ObjectSchema(); + additionalProperties.setType(SchemaType.STRING); + schema.setAdditionalProperties(additionalProperties); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema + .getSchema() + .getAdditionalProperties() + .getSchema() + .getType()) + .containsExactly(additionalProperties.getType()); + } + + @Test + void mapRequired() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setRequired(List.of("required")); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + assertThat(componentSchema.getSchema().getRequired()).isEqualTo(schema.getRequired()); + } + + @Test + void mapDiscriminator() { + // given + ObjectSchema schema = new ObjectSchema(); + Discriminator discriminator = new Discriminator(); + discriminator.setPropertyName("name"); + schema.setDiscriminator(discriminator); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + + assertThat(componentSchema.getSchema().getDiscriminator()).isEqualTo("name"); + } + + @Test + void mapAllOf() { + // given + ObjectSchema schema = new ObjectSchema(); + ObjectSchema allOf = new ObjectSchema(); + allOf.setType(SchemaType.STRING); + schema.addAllOfItem(allOf); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + + assertThat((componentSchema.getSchema().getAllOf().get(0).getSchema()).getType()) + .containsExactly(allOf.getType()); + } + + @Test + void mapOneOf() { + // given + ObjectSchema schema = new ObjectSchema(); + ObjectSchema oneOf = new ObjectSchema(); + oneOf.setType(SchemaType.STRING); + schema.addOneOfItem(oneOf); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + + assertThat((componentSchema.getSchema().getOneOf().get(0).getSchema()).getType()) + .containsExactly(oneOf.getType()); + } + + @Test + void mapAnyOf() { + // given + ObjectSchema schema = new ObjectSchema(); + ObjectSchema anyOf = new ObjectSchema(); + anyOf.setType(SchemaType.STRING); + schema.addAnyOfItem(anyOf); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat((componentSchema.getSchema().getAnyOf().get(0).getSchema()).getType()) + .containsExactly(anyOf.getType()); + } + + @Test + void mapConst() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setConst("const"); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat(componentSchema.getSchema().getConstValue()).isEqualTo(schema.getConst()); + } + + @Test + void mapNot() { + // given + ObjectSchema schema = new ObjectSchema(); + ObjectSchema not = new ObjectSchema(); + not.setType(SchemaType.STRING); + schema.setNot(not); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat((componentSchema.getSchema().getNot().getSchema()).getType()) + .containsExactly(not.getType()); + } + + @Test + void mapItems() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.type(SchemaType.ARRAY); + ObjectSchema item = new ObjectSchema(); + item.setType(SchemaType.STRING); + schema.setItems(item); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat((componentSchema.getSchema().getItems().getSchema()).getType()) + .containsExactly(item.getType()); + } + + @Test + void mapUniqueItems() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setUniqueItems(false); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat(componentSchema.getSchema().getUniqueItems()).isEqualTo(schema.getUniqueItems()); + } + + @Test + void mapMinItems() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setMinItems(1); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat(componentSchema.getSchema().getMinItems()).isEqualTo(schema.getMinItems()); + } + + @Test + void mapMaxItems() { + // given + ObjectSchema schema = new ObjectSchema(); + schema.setMaxItems(10); + + // when + ComponentSchema componentSchema = swaggerSchemaMapper.mapSchema(schema); + + // then + // ensure that componentSchema contains an AsnycApi SchemaObjekt. + assertThat(componentSchema.getSchema()).isNotNull(); + assertThat(componentSchema.getSchema().getMaxItems()).isEqualTo(schema.getMaxItems()); + } + } + + @Nested + class MapToSwagger { + @Test + void mapDescription() { + // given + SchemaObject schema = new SchemaObject(); + schema.setDescription("description"); + + // when + Schema swaggerSchema = swaggerSchemaMapper.mapToSwagger(schema); + + // then + assertThat(swaggerSchema.getDescription()).isEqualTo(schema.getDescription()); + } + + @Test + void mapExamples() { + // given + SchemaObject schema = new SchemaObject(); + schema.setExamples(List.of("example1", "example2")); + + // when + Schema swaggerSchema = swaggerSchemaMapper.mapToSwagger(schema); + + // then + assertThat(swaggerSchema.getExamples()).isEqualTo(schema.getExamples()); + } + + @Test + void mapEnum() { + // given + SchemaObject schema = new SchemaObject(); + schema.setEnumValues(List.of("enum1", "enum2")); + + // when + Schema swaggerSchema = swaggerSchemaMapper.mapToSwagger(schema); + + // then + assertThat(swaggerSchema.getEnum()).isEqualTo(schema.getEnumValues()); + } + + @Test + void mapNullableEnum() { + // given + SchemaObject schema = new SchemaObject(); + schema.setEnumValues(Stream.of("enum1", "enum2", null).toList()); + schema.setTypes(Set.of(SchemaType.STRING, SchemaType.NULL)); // nullable + + // when + Schema swaggerSchema = swaggerSchemaMapper.mapToSwagger(schema); + + // then + assertThat(swaggerSchema.getEnum()).isEqualTo(schema.getEnumValues()); + assertThat(swaggerSchema.getTypes()).isEqualTo(schema.getType()); + } + + @Test + void mapType() { + // given + SchemaObject schema = new SchemaObject(); + schema.setType(SchemaType.STRING); + + // when + Schema swaggerSchema = swaggerSchemaMapper.mapToSwagger(schema); + + // then + assertThat(swaggerSchema.getType()).isEqualTo(SchemaType.STRING); + } + } +} 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 deleted file mode 100644 index 4216ca489..000000000 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java +++ /dev/null @@ -1,567 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.springwolf.core.asyncapi.components; - -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.asyncapi.v3.model.schema.SchemaType; -import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; -import io.swagger.v3.oas.models.ExternalDocumentation; -import io.swagger.v3.oas.models.media.Discriminator; -import io.swagger.v3.oas.models.media.ObjectSchema; -import io.swagger.v3.oas.models.media.Schema; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import java.math.BigDecimal; -import java.util.List; -import java.util.Set; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; - -class SwaggerSchemaUtilTest { - private final SwaggerSchemaUtil swaggerSchemaUtil = new SwaggerSchemaUtil(); - - @Nested - class MapSchemaOrRef { - - @Test - void mapReference() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.set$ref("#/components/schemas/MySchema"); - - // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema); - - // then - assertThat(componentSchema.getReference()).isEqualTo(new SchemaReference("#/components/schemas/MySchema")); - } - - @Test - void mapSchema() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setType(SchemaType.STRING); - - // when - ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema); - - // then - assertThat(componentSchema.getSchema().getType()).containsExactly(SchemaType.STRING); - } - } - - @Nested - class MapSchema { - @Test - void mapExternalDocs() { - // given - ObjectSchema schema = new ObjectSchema(); - ExternalDocumentation externalDocs = new ExternalDocumentation(); - externalDocs.setDescription("description"); - externalDocs.setUrl("url"); - schema.setExternalDocs(externalDocs); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getExternalDocs().getDescription()).isEqualTo(externalDocs.getDescription()); - assertThat(componentSchema.getExternalDocs().getUrl()).isEqualTo(externalDocs.getUrl()); - } - - @Test - void mapDeprecated() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setDeprecated(true); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getDeprecated()).isEqualTo(true); - } - - @Test - void mapTitle() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setTitle("title"); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getTitle()).isEqualTo(schema.getTitle()); - } - - @Test - void mapType() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setType(SchemaType.STRING); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getType()).containsExactly(schema.getType()); - } - - @Test - void mapProperties() { - // given - ObjectSchema schema = new ObjectSchema(); - ObjectSchema property = new ObjectSchema(); - property.setType(SchemaType.STRING); - schema.addProperty("property", property); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(((ComponentSchema) componentSchema.getProperties().get("property")) - .getSchema() - .getType()) - .containsExactly(property.getType()); - } - - @Test - void mapDescription() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setDescription("description"); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getDescription()).isEqualTo(schema.getDescription()); - } - - @Test - void mapFormat() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setFormat("format"); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getFormat()).isEqualTo(schema.getFormat()); - } - - @Test - void mapPattern() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setPattern("pattern"); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getPattern()).isEqualTo(schema.getPattern()); - } - - @Test - void mapExclusiveMinimum() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setMinimum(BigDecimal.ONE); - schema.setExclusiveMinimum(true); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getExclusiveMinimum()).isEqualTo(schema.getMinimum()); - assertThat(componentSchema.getMinimum()).isNull(); - } - - @Test - void mapExclusiveMinimumValue() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setExclusiveMinimumValue(BigDecimal.ONE); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getExclusiveMinimum()).isEqualTo(schema.getExclusiveMinimumValue()); - assertThat(componentSchema.getMinimum()).isNull(); - } - - @Test - void mapExclusiveMaximum() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setMaximum(BigDecimal.ONE); - schema.setExclusiveMaximum(true); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getExclusiveMaximum()).isEqualTo(schema.getMaximum()); - assertThat(componentSchema.getMaximum()).isNull(); - } - - @Test - void mapExclusiveMaximumValue() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setExclusiveMaximumValue(BigDecimal.ONE); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getExclusiveMaximum()).isEqualTo(schema.getExclusiveMaximumValue()); - assertThat(componentSchema.getMaximum()).isNull(); - } - - @Test - void mapMinimum() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setMinimum(BigDecimal.ONE); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getMinimum()).isEqualTo(schema.getMinimum()); - assertThat(componentSchema.getExclusiveMinimum()).isNull(); - } - - @Test - void mapMaximum() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setMaximum(BigDecimal.ONE); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getMaximum()).isEqualTo(schema.getMaximum()); - assertThat(componentSchema.getExclusiveMaximum()).isNull(); - } - - @Test - void mapMultipleOf() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setMultipleOf(BigDecimal.ONE); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getMultipleOf()).isEqualTo(schema.getMultipleOf()); - } - - @Test - void mapMinLength() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setMinLength(1); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getMinLength()).isEqualTo(schema.getMinLength()); - } - - @Test - void mapMaxLength() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setMaxLength(1); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getMaxLength()).isEqualTo(schema.getMaxLength()); - } - - @Test - void mapEnum() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setEnum(List.of("enum1", "enum2")); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getEnumValues()).isEqualTo(schema.getEnum()); - } - - @Test - void mapExample() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setExample("example"); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getExamples()).isEqualTo(List.of(schema.getExample())); - } - - @Test - void mapAdditionalProperties() { - // given - ObjectSchema schema = new ObjectSchema(); - ObjectSchema additionalProperties = new ObjectSchema(); - additionalProperties.setType(SchemaType.STRING); - schema.setAdditionalProperties(additionalProperties); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getAdditionalProperties().getSchema().getType()) - .containsExactly(additionalProperties.getType()); - } - - @Test - void mapRequired() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setRequired(List.of("required")); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getRequired()).isEqualTo(schema.getRequired()); - } - - @Test - void mapDiscriminator() { - // given - ObjectSchema schema = new ObjectSchema(); - Discriminator discriminator = new Discriminator(); - discriminator.setPropertyName("name"); - schema.setDiscriminator(discriminator); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getDiscriminator()).isEqualTo("name"); - } - - @Test - void mapAllOf() { - // given - ObjectSchema schema = new ObjectSchema(); - ObjectSchema allOf = new ObjectSchema(); - allOf.setType(SchemaType.STRING); - schema.addAllOfItem(allOf); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat((componentSchema.getAllOf().get(0).getSchema()).getType()) - .containsExactly(allOf.getType()); - } - - @Test - void mapOneOf() { - // given - ObjectSchema schema = new ObjectSchema(); - ObjectSchema oneOf = new ObjectSchema(); - oneOf.setType(SchemaType.STRING); - schema.addOneOfItem(oneOf); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat((componentSchema.getOneOf().get(0).getSchema()).getType()) - .containsExactly(oneOf.getType()); - } - - @Test - void mapAnyOf() { - // given - ObjectSchema schema = new ObjectSchema(); - ObjectSchema anyOf = new ObjectSchema(); - anyOf.setType(SchemaType.STRING); - schema.addAnyOfItem(anyOf); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat((componentSchema.getAnyOf().get(0).getSchema()).getType()) - .containsExactly(anyOf.getType()); - } - - @Test - void mapConst() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setConst("const"); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getConstValue()).isEqualTo(schema.getConst()); - } - - @Test - void mapNot() { - // given - ObjectSchema schema = new ObjectSchema(); - ObjectSchema not = new ObjectSchema(); - not.setType(SchemaType.STRING); - schema.setNot(not); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat((componentSchema.getNot().getSchema()).getType()).containsExactly(not.getType()); - } - - @Test - void mapItems() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.type(SchemaType.ARRAY); - ObjectSchema item = new ObjectSchema(); - item.setType(SchemaType.STRING); - schema.setItems(item); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat((componentSchema.getItems().getSchema()).getType()).containsExactly(item.getType()); - } - - @Test - void mapUniqueItems() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setUniqueItems(false); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getUniqueItems()).isEqualTo(schema.getUniqueItems()); - } - - @Test - void mapMinItems() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setMinItems(1); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getMinItems()).isEqualTo(schema.getMinItems()); - } - - @Test - void mapMaxItems() { - // given - ObjectSchema schema = new ObjectSchema(); - schema.setMaxItems(10); - - // when - SchemaObject componentSchema = swaggerSchemaUtil.mapSchema(schema); - - // then - assertThat(componentSchema.getMaxItems()).isEqualTo(schema.getMaxItems()); - } - } - - @Nested - class MapToSwagger { - @Test - void mapDescription() { - // given - SchemaObject schema = new SchemaObject(); - schema.setDescription("description"); - - // when - Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema); - - // then - assertThat(componentSchema.getDescription()).isEqualTo(schema.getDescription()); - } - - @Test - void mapExamples() { - // given - SchemaObject schema = new SchemaObject(); - schema.setExamples(List.of("example1", "example2")); - - // when - Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema); - - // then - assertThat(componentSchema.getExamples()).isEqualTo(schema.getExamples()); - } - - @Test - void mapEnum() { - // given - SchemaObject schema = new SchemaObject(); - schema.setEnumValues(List.of("enum1", "enum2")); - - // when - Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema); - - // then - assertThat(componentSchema.getEnum()).isEqualTo(schema.getEnumValues()); - } - - @Test - void mapNullableEnum() { - // given - SchemaObject schema = new SchemaObject(); - schema.setEnumValues(Stream.of("enum1", "enum2", null).toList()); - schema.setTypes(Set.of(SchemaType.STRING, SchemaType.NULL)); // nullable - - // when - Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema); - - // then - assertThat(componentSchema.getEnum()).isEqualTo(schema.getEnumValues()); - assertThat(componentSchema.getTypes()).isEqualTo(schema.getType()); - } - - @Test - void mapType() { - // given - SchemaObject schema = new SchemaObject(); - schema.setType(SchemaType.STRING); - - // when - Schema componentSchema = swaggerSchemaUtil.mapToSwagger(schema); - - // then - assertThat(componentSchema.getType()).isEqualTo(SchemaType.STRING); - } - } -} 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/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java index e80ad1ace..7d37bf7b0 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,8 +29,9 @@ 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.SwaggerSchemaMapper; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; -import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.swagger.v3.oas.annotations.Hidden; import lombok.Data; @@ -60,7 +61,7 @@ SpringAnnotationClassLevelChannelsScannerIntegrationTest.TestBindingFactory.class, DefaultComponentsService.class, SwaggerSchemaService.class, - SwaggerSchemaUtil.class, + SwaggerSchemaMapper.class, PayloadMethodParameterService.class, PayloadAsyncOperationService.class, PayloadService.class, @@ -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..2ee501e75 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,8 +29,9 @@ 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.SwaggerSchemaMapper; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; -import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.swagger.v3.oas.annotations.Hidden; import lombok.Data; @@ -62,7 +63,7 @@ SpringAnnotationMethodLevelChannelsScannerIntegrationTest.TestBindingFactory.class, DefaultComponentsService.class, SwaggerSchemaService.class, - SwaggerSchemaUtil.class, + SwaggerSchemaMapper.class, PayloadMethodParameterService.class, PayloadAsyncOperationService.class, PayloadService.class, @@ -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..e438262ea 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,14 +5,14 @@ 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.SwaggerSchemaMapper; 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.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,15 @@ 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 SpringwolfConfigProperties configProperties = new SpringwolfConfigProperties(); + + private final SwaggerSchemaService schemaService = new SwaggerSchemaService( + configProperties, + List.of(), + new SwaggerSchemaMapper(configProperties), + new ModelConvertersProvider(configProperties, List.of(titleModelConverter))); private final HeaderClassExtractor headerClassExtractor = new HeaderClassExtractor(schemaService); private final PayloadSchemaObject payloadSchemaName = new PayloadSchemaObject( @@ -34,15 +41,6 @@ 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); - } - @AfterAll static void tearDownClass() { ModelConverters.getInstance().removeConverter(titleModelConverter); 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/schemas/SwaggerSchemaServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java index 6cc062bdb..7d8559384 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 @@ -47,29 +47,26 @@ class SwaggerSchemaServiceTest { @BeforeEach void setUp() { + SpringwolfConfigProperties configProperties = new SpringwolfConfigProperties(); schemaService = new SwaggerSchemaService( - List.of(new ModelConverterNativeClass.Converter()), + configProperties, List.of(schemasPostProcessor, schemasPostProcessor2), - new SwaggerSchemaUtil(), - new SpringwolfConfigProperties()); + new SwaggerSchemaMapper(configProperties), + new ModelConvertersProvider(configProperties, List.of(new ModelConverterNativeClass.Converter()))); } @Test void classWithSchemaAnnotationWithAsyncapiSchemaformat() { - ComponentSchema schema = schemaService - .resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant") - .rootSchema(); + SwaggerSchemaService.ExtractedSchemas extractedSchemas = + schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant"); - assertThat(schema.getReference().getRef()).isEqualTo("#/components/schemas/DifferentName"); - } - - @Test - void classWithSchemaAnnotationWithOpenapiSchemaformat() { - ComponentSchema schema = schemaService - .resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant") - .rootSchema(); + 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 @@ -78,8 +75,11 @@ void getDefinitionWithoutFqnClassName() throws Exception { SpringwolfConfigProperties properties = new SpringwolfConfigProperties(); properties.setUseFqn(false); - SwaggerSchemaService schemaServiceWithFqn = - new SwaggerSchemaService(List.of(), List.of(), new SwaggerSchemaUtil(), properties); + SwaggerSchemaService schemaServiceWithFqn = new SwaggerSchemaService( + properties, + List.of(), + new SwaggerSchemaMapper(properties), + new ModelConvertersProvider(properties, List.of())); // when Class clazz = 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-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..6e5451b02 --- /dev/null +++ b/springwolf-examples/springwolf-kafka-example/src/test/resources/asyncapi.openapiv31.json @@ -0,0 +1,1685 @@ +{ + "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": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "description": "There can be headers, but they are not explicitly documented.", + "example": { }, + "properties": { }, + "title": "HeadersNotDocumented" + } + }, + "HeadersNotUsed": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "description": "No headers are present.", + "example": { }, + "properties": { }, + "title": "HeadersNotUsed" + } + }, + "PayloadNotUsed": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "description": "No payload specified", + "example": { }, + "properties": { }, + "title": "PayloadNotUsed" + } + }, + "SpringDefaultHeaderAndCloudEvent": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "description": "Spring __TypeId__ and CloudEvent Headers", + "example": { + "__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" + }, + "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" + ] + } + }, + "title": "SpringDefaultHeaderAndCloudEvent" + } + }, + "SpringKafkaDefaultHeaders-AnotherPayloadAvroDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "io.github.springwolf.examples.kafka.dto.avro.AnotherPayloadAvroDto" + }, + "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" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-AnotherPayloadAvroDto" + } + }, + "SpringKafkaDefaultHeaders-AnotherPayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.AnotherPayloadDto" + }, + "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" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-AnotherPayloadDto" + } + }, + "SpringKafkaDefaultHeaders-AnotherTopic": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "string", + "my_uuid_field": "3fa85f64-5717-4562-b3fc-2c963f66afa6" + }, + "properties": { + "__TypeId__": { + "type": "string", + "description": "Type ID" + }, + "my_uuid_field": { + "type": "string", + "format": "uuid", + "description": "Event identifier" + } + }, + "title": "SpringKafkaDefaultHeaders-AnotherTopic" + } + }, + "SpringKafkaDefaultHeaders-ExamplePayloadDto-1610003719": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.ExamplePayloadDto", + "kafka_offset": 0, + "kafka_receivedMessageKey": "\"string\"", + "org.springframework.kafka.listener.adapter.ConsumerRecordMetadata": { } + }, + "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", + "example": 0 + }, + "kafka_receivedMessageKey": { + "type": "string", + "example": "\"string\"" + }, + "kafka_recordMetadata": { + "example": { }, + "title": "ConsumerRecordMetadata" + } + }, + "title": "SpringKafkaDefaultHeaders-ExamplePayloadDto-1610003719" + } + }, + "SpringKafkaDefaultHeaders-Message": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "io.github.springwolf.examples.kafka.dto.proto.ExamplePayloadProtobufDto.Message" + }, + "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" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-Message" + } + }, + "SpringKafkaDefaultHeaders-MonetaryAmount": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "javax.money.MonetaryAmount" + }, + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "javax.money.MonetaryAmount" + ], + "examples": [ + "javax.money.MonetaryAmount" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-MonetaryAmount" + } + }, + "SpringKafkaDefaultHeaders-PayloadNotUsed": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "PayloadNotUsed" + }, + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "PayloadNotUsed" + ], + "examples": [ + "PayloadNotUsed" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-PayloadNotUsed" + } + }, + "SpringKafkaDefaultHeaders-RequiredAndNullablePayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.RequiredAndNullablePayloadDto" + }, + "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" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-RequiredAndNullablePayloadDto" + } + }, + "SpringKafkaDefaultHeaders-VehicleBase": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.discriminator.VehicleBase" + }, + "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" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-VehicleBase" + } + }, + "SpringKafkaDefaultHeaders-XmlPayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.XmlPayloadDto" + }, + "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" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-XmlPayloadDto" + } + }, + "SpringKafkaDefaultHeaders-YamlPayloadDto": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "io.github.springwolf.examples.kafka.dtos.YamlPayloadDto" + }, + "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" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-YamlPayloadDto" + } + }, + "SpringKafkaDefaultHeaders-integer": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "java.lang.Integer" + }, + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "java.lang.Integer" + ], + "examples": [ + "java.lang.Integer" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-integer" + } + }, + "SpringKafkaDefaultHeaders-string": { + "schemaFormat": "application/vnd.oai.openapi;version=3.1.0", + "schema": { + "type": "object", + "example": { + "__TypeId__": "java.lang.String" + }, + "properties": { + "__TypeId__": { + "type": "string", + "description": "Spring Type Id Header", + "enum": [ + "java.lang.String" + ], + "examples": [ + "java.lang.String" + ] + } + }, + "title": "SpringKafkaDefaultHeaders-string" + } + }, + "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-1610003719" + }, + "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" + } + ] + } + } +} 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..872978099 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,8 +30,9 @@ 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.SwaggerSchemaMapper; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; -import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.configuration.docket.DefaultAsyncApiDocketService; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common.FunctionalChannelBeanBuilder; @@ -73,7 +74,7 @@ SwaggerSchemaService.class, PayloadService.class, PayloadExtractor.class, - SwaggerSchemaUtil.class, + SwaggerSchemaMapper.class, TypeExtractor.class, DefaultSchemaWalker.class, SchemaWalkerProvider.class, @@ -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..10341bb50 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,8 +11,9 @@ 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.SwaggerSchemaMapper; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; -import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.github.springwolf.core.controller.PublishingPayloadCreator; import io.github.springwolf.core.controller.dtos.MessageDto; @@ -58,11 +59,12 @@ DefaultComponentsService.class, SwaggerSchemaService.class, PayloadService.class, - SwaggerSchemaUtil.class, + SwaggerSchemaMapper.class, DefaultSchemaWalker.class, 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..2cb7fdc0a 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,8 +11,9 @@ 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.SwaggerSchemaMapper; import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService; -import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaUtil; import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties; import io.github.springwolf.core.controller.PublishingPayloadCreator; import io.github.springwolf.core.controller.dtos.MessageDto; @@ -56,13 +57,14 @@ DefaultComponentsService.class, SwaggerSchemaService.class, PayloadService.class, - SwaggerSchemaUtil.class, + SwaggerSchemaMapper.class, PayloadExtractor.class, TypeExtractor.class, DefaultSchemaWalker.class, SchemaWalkerProvider.class, ExampleJsonValueGenerator.class, SpringwolfConfigProperties.class, + ModelConvertersProvider.class }) @TestPropertySource( properties = {