Skip to content

Commit a92b4e5

Browse files
tvahrstsam0r040
authored andcommitted
feat: schemaformat option for payload schemas added
1 parent fec5b2f commit a92b4e5

File tree

22 files changed

+322
-200
lines changed

22 files changed

+322
-200
lines changed

springwolf-add-ons/springwolf-json-schema/src/test/java/io/github/springwolf/addons/json_schema/JsonSchemaGeneratorTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.networknt.schema.JsonSchemaFactory;
88
import com.networknt.schema.SpecVersionDetector;
99
import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema;
10+
import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat;
1011
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
1112
import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference;
1213
import io.github.springwolf.asyncapi.v3.model.schema.SchemaType;
@@ -42,7 +43,7 @@ class JsonSchemaGeneratorTest {
4243
@MethodSource
4344
void validateJsonSchemaTest(String expectedJsonSchema, Supplier<Schema<?>> asyncApiSchema) throws Exception {
4445
// given
45-
SchemaObject actualSchema = swaggerSchemaUtil.mapSchema(asyncApiSchema.get());
46+
ComponentSchema actualSchema = swaggerSchemaUtil.mapSchema(asyncApiSchema.get(), SchemaFormat.ASYNCAPI_V3);
4647

4748
// when
4849
verifyValidJsonSchema(expectedJsonSchema);
@@ -67,7 +68,7 @@ void validateJsonSchemaTest(String expectedJsonSchema, Supplier<Schema<?>> async
6768
ComponentSchema.of(pongSchema));
6869

6970
// when
70-
Object jsonSchema = jsonSchemaGenerator.fromSchema(ComponentSchema.of(actualSchema), definitions);
71+
Object jsonSchema = jsonSchemaGenerator.fromSchema(actualSchema, definitions);
7172

7273
// then
7374
String jsonSchemaString = mapper.writeValueAsString(jsonSchema);

springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaFormat.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public enum SchemaFormat {
1212
ASYNCAPI_V3_JSON("application/vnd.aai.asyncapi+json;version=" + AsyncAPI.ASYNCAPI_DEFAULT_VERSION),
1313
ASYNCAPI_V3_YAML("application/vnd.aai.asyncapi+yaml;version=" + AsyncAPI.ASYNCAPI_DEFAULT_VERSION),
1414
OPENAPI_V3("application/vnd.oai.openapi;version=3.0.0"),
15+
OPENAPI_V3_1("application/vnd.oai.openapi;version=3.1.0"),
1516
OPENAPI_V3_JSON("application/vnd.oai.openapi+json;version=3.0.0"),
1617
OPENAPI_V3_YAML("application/vnd.oai.openapi+yaml;version=3.0.0"),
1718
JSON_SCHEMA_JSON("application/schema+json;version=draft-07"),

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference;
77
import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema;
88
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
9+
import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference;
910
import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties;
1011
import jakarta.annotation.Nullable;
1112

@@ -33,7 +34,7 @@ public interface ComponentsService {
3334
*
3435
* @param type Type to resolve a schema from
3536
* @param contentType Runtime ContentType of Schema
36-
* @return the root schema for the given type.
37+
* @return a {@link SchemaReference} referencing the root schema, or null if no schema could be resolved.
3738
*/
3839
@Nullable
3940
ComponentSchema resolvePayloadSchema(Type type, String contentType);
@@ -43,7 +44,7 @@ public interface ComponentsService {
4344
* @param headers the schema to register, typically a header schema
4445
* @return the title attribute of the given schema
4546
*/
46-
String registerSchema(SchemaObject headers);
47+
String registerSimpleSchema(SchemaObject headers);
4748

4849
/**
4950
* Provides a map of all registered messages.

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject;
66
import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference;
77
import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema;
8+
import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat;
89
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
910
import io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService;
11+
import io.github.springwolf.core.configuration.properties.PayloadSchemaFormat;
1012
import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties;
11-
import lombok.AllArgsConstructor;
1213
import lombok.extern.slf4j.Slf4j;
1314

1415
import java.lang.reflect.Type;
@@ -22,11 +23,10 @@
2223
* in the resulting AsyncApi object.
2324
*/
2425
@Slf4j
25-
@AllArgsConstructor
2626
public class DefaultComponentsService implements ComponentsService {
2727

2828
private final SwaggerSchemaService schemaService;
29-
private final SpringwolfConfigProperties springwolfConfigProperties;
29+
private final SchemaFormat payloadSchemaFormat;
3030

3131
/**
3232
* maps a schema name (key) to a detected corresponding {@link ComponentSchema}.
@@ -35,6 +35,15 @@ public class DefaultComponentsService implements ComponentsService {
3535

3636
private final Map<String, Message> messages = new HashMap<>();
3737

38+
public DefaultComponentsService(
39+
SwaggerSchemaService schemaService, SpringwolfConfigProperties springwolfConfigProperties) {
40+
this.schemaService = schemaService;
41+
42+
PayloadSchemaFormat payloadSchemaFormat =
43+
springwolfConfigProperties.getDocket().getPayloadSchemaFormat();
44+
this.payloadSchemaFormat = payloadSchemaFormat.getSchemaFormat();
45+
}
46+
3847
/**
3948
* Provides a map of all registered schemas.
4049
*
@@ -51,12 +60,13 @@ public Map<String, ComponentSchema> getSchemas() {
5160
*
5261
* @param type Type to resolve a schema from
5362
* @param contentType Runtime ContentType of Schema
54-
* @return the root schema for the given type.
63+
* @return the root schema for the given type
5564
*/
5665
@Override
5766
public ComponentSchema resolvePayloadSchema(Type type, String contentType) {
5867

59-
SwaggerSchemaService.ExtractedSchemas payload = schemaService.resolveSchema(type, contentType);
68+
SwaggerSchemaService.ExtractedSchemas payload =
69+
schemaService.resolveSchema(type, contentType, payloadSchemaFormat);
6070
payload.referencedSchemas().forEach(schemas::putIfAbsent);
6171
return payload.rootSchema();
6272
}
@@ -67,17 +77,17 @@ public ComponentSchema resolvePayloadSchema(Type type, String contentType) {
6777
* Use only with schemas with max. one level of properties. Providing {@link SchemaObject}s with deep
6878
* property hierarchy will result in an corrupted result.
6979
* <br/>
70-
* A typical usecase for this method is registering of header schemas, which have typically a simple structure.
80+
* A typical usecase for this method is registering of header schemas, which have typically a simple structure.
7181
*
7282
* @param headers the schema to register, typically a header schema
7383
* @return the title attribute of the given schema
7484
*/
7585
@Override
76-
public String registerSchema(SchemaObject headers) {
86+
public String registerSimpleSchema(SchemaObject headers) {
7787
log.debug("Registering schema for {}", headers.getTitle());
7888

79-
SchemaObject headerSchema = schemaService.extractSchema(headers);
80-
this.schemas.putIfAbsent(headers.getTitle(), ComponentSchema.of(headerSchema));
89+
ComponentSchema processedSchema = schemaService.postProcessSimpleSchema(headers);
90+
this.schemas.putIfAbsent(headers.getTitle(), processedSchema);
8191

8292
return headers.getTitle();
8393
}

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/headers/HeaderClassExtractor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ public SchemaObject extractHeader(Method method, PayloadSchemaObject payload) {
3434
Header headerAnnotation = argument.getAnnotation(Header.class);
3535
String headerName = getHeaderAnnotationName(headerAnnotation);
3636

37-
SwaggerSchemaService.ExtractedSchemas extractedSchema = schemaService.extractSchema(argument.getType());
37+
SwaggerSchemaService.ExtractedSchemas extractedSchema =
38+
schemaService.postProcessSimpleSchema(argument.getType());
3839
ComponentSchema rootComponentSchema = extractedSchema.rootSchema();
3940

4041
// to stay compatible with former versions.

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/AsyncAnnotationMessageService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public MessageObject buildMessage(AsyncOperation operationData, Method method) {
3737
PayloadSchemaObject payloadSchema = payloadAsyncOperationService.extractSchema(operationData, method);
3838

3939
SchemaObject headerSchema = AsyncAnnotationUtil.getAsyncHeaders(operationData, stringValueResolver);
40-
String headerSchemaName = this.componentsService.registerSchema(headerSchema);
40+
String headerSchemaName = this.componentsService.registerSimpleSchema(headerSchema);
4141

4242
Map<String, MessageBinding> messageBinding =
4343
AsyncAnnotationUtil.processMessageBindingFromAnnotation(method, messageBindingProcessors);

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessageService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public MessageObject buildMessage(
3131
MethodAnnotation annotation, PayloadSchemaObject payloadSchema, SchemaObject headers) {
3232
SchemaObject headerSchema = asyncHeadersBuilder.buildHeaders(payloadSchema);
3333
SchemaObject mergedHeaderSchema = HeaderSchemaObjectMerger.merge(headerSchema, headers);
34-
String headerModelName = componentsService.registerSchema(mergedHeaderSchema);
34+
String headerModelName = componentsService.registerSimpleSchema(mergedHeaderSchema);
3535

3636
Map<String, MessageBinding> messageBinding = bindingFactory.buildMessageBinding(annotation, mergedHeaderSchema);
3737

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/message/SpringAnnotationMessagesService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ private MessageObject buildMessage(ClassAnnotation classAnnotation, Method metho
6262
SchemaObject headerSchema = asyncHeadersBuilder.buildHeaders(payloadSchema);
6363
SchemaObject headers = headerClassExtractor.extractHeader(method, payloadSchema);
6464
SchemaObject mergedHeaderSchema = HeaderSchemaObjectMerger.merge(headerSchema, headers);
65-
String headerSchemaName = componentsService.registerSchema(mergedHeaderSchema);
65+
String headerSchemaName = componentsService.registerSimpleSchema(mergedHeaderSchema);
6666

6767
Map<String, MessageBinding> messageBinding = bindingFactory.buildMessageBinding(classAnnotation, headerSchema);
6868

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/internal/PayloadService.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ public PayloadSchemaObject buildSchema(Type payloadType) {
3636
return buildSchema(contentType, payloadType);
3737
}
3838

39+
/**
40+
* creates a {@link PayloadSchemaObject} from the given type and content type. Registers the created schema objects
41+
* with this {@link ComponentsService}.
42+
*
43+
* @param contentType
44+
* @param payloadType
45+
* @return
46+
*/
3947
public PayloadSchemaObject buildSchema(String contentType, Type payloadType) {
4048
String schemaName = componentsService.getSchemaName(payloadType);
4149
String simpleSchemaName = componentsService.getSimpleSchemaName(payloadType);
@@ -47,7 +55,7 @@ public PayloadSchemaObject buildSchema(String contentType, Type payloadType) {
4755
public PayloadSchemaObject useUnusedPayload() {
4856
ComponentSchema schema = PAYLOAD_NOT_USED.schema();
4957
if (schema != null && schema.getSchema() != null) {
50-
this.componentsService.registerSchema(schema.getSchema());
58+
this.componentsService.registerSimpleSchema(schema.getSchema());
5159
}
5260
return PAYLOAD_NOT_USED;
5361
}

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaService.java

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.fasterxml.jackson.databind.JavaType;
55
import io.github.springwolf.asyncapi.v3.model.ReferenceUtil;
66
import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema;
7+
import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat;
78
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
89
import io.github.springwolf.core.asyncapi.annotations.AsyncApiPayload;
910
import io.github.springwolf.core.asyncapi.components.postprocessors.SchemasPostProcessor;
@@ -35,7 +36,9 @@
3536

3637
@Slf4j
3738
public class SwaggerSchemaService {
38-
private final ModelConverters converter = ModelConverters.getInstance();
39+
private final ModelConverters converter_openapi30 = ModelConverters.getInstance(false);
40+
private final ModelConverters converter_openapi31 = ModelConverters.getInstance(true);
41+
3942
private final List<SchemasPostProcessor> schemaPostProcessors;
4043
private final SwaggerSchemaUtil swaggerSchemaUtil;
4144
private final SpringwolfConfigProperties properties;
@@ -46,7 +49,8 @@ public SwaggerSchemaService(
4649
SwaggerSchemaUtil swaggerSchemaUtil,
4750
SpringwolfConfigProperties properties) {
4851

49-
externalModelConverters.forEach(converter::addConverter);
52+
externalModelConverters.forEach(converter_openapi30::addConverter);
53+
externalModelConverters.forEach(converter_openapi31::addConverter);
5054
this.schemaPostProcessors = schemaPostProcessors;
5155
this.swaggerSchemaUtil = swaggerSchemaUtil;
5256
this.properties = properties;
@@ -68,7 +72,7 @@ public record ExtractedSchemas(ComponentSchema rootSchema, Map<String, Component
6872
* @param headers
6973
* @return
7074
*/
71-
public SchemaObject extractSchema(SchemaObject headers) {
75+
public ComponentSchema postProcessSimpleSchema(SchemaObject headers) {
7276
String schemaName = headers.getTitle();
7377

7478
// create a swagger schema to invoke the postprocessors. Copy attributes vom headers to (Swagger) headerSchema
@@ -91,24 +95,44 @@ public SchemaObject extractSchema(SchemaObject headers) {
9195
postProcessSchemas(newSchemasToProcess, new HashMap<>(newSchemasToProcess), DEFAULT_CONTENT_TYPE);
9296

9397
// convert Swagger schema back to an AsnycApi SchemaObject
94-
return swaggerSchemaUtil.mapSchema(headerSchema);
98+
return swaggerSchemaUtil.mapSchema(headerSchema, SchemaFormat.ASYNCAPI_V3);
9599
}
96100

97-
public ExtractedSchemas extractSchema(Class<?> type) {
98-
return this.resolveSchema(type, "");
101+
public ExtractedSchemas postProcessSimpleSchema(Class<?> type) {
102+
return this.resolveSchema(type, "", SchemaFormat.ASYNCAPI_V3);
99103
}
100104

101-
public ExtractedSchemas resolveSchema(Type type, String contentType) {
105+
/**
106+
* creates a {@link ExtractedSchemas} from the given type. The resulting ExtractedSchemas will contain the root
107+
* schema (which is always a schema ref) and a List of conrecte schemas referenced from the root schema.
108+
*
109+
* @param type
110+
* @param contentType
111+
* @param schemaFormat SchemaFormat to use
112+
* @return
113+
*/
114+
public ExtractedSchemas resolveSchema(Type type, String contentType, SchemaFormat schemaFormat) {
102115
String actualContentType =
103116
StringUtils.isBlank(contentType) ? properties.getDocket().getDefaultContentType() : contentType;
117+
118+
// use swagger to resolve type to a swagger ResolvedSchema Object.
119+
ModelConverters converterToUse =
120+
switch (schemaFormat) {
121+
case ASYNCAPI_V3 -> converter_openapi30;
122+
case OPENAPI_V3 -> converter_openapi30;
123+
case OPENAPI_V3_1 -> converter_openapi31;
124+
default -> throw new IllegalArgumentException("SchemaFormat not supported: " + schemaFormat);
125+
};
126+
104127
ResolvedSchema resolvedSchema = runWithFqnSetting(
105-
(unused) -> converter.resolveAsResolvedSchema(new AnnotatedType(type).resolveAsRef(true)));
128+
(unused) -> converterToUse.resolveAsResolvedSchema(new AnnotatedType(type).resolveAsRef(true)));
106129

107130
if (resolvedSchema == null) {
108131
// defaulting to stringSchema when resolvedSchema is null
109-
SchemaObject payloadSchema = swaggerSchemaUtil.mapSchema(
110-
PrimitiveType.fromType(String.class).createProperty());
111-
return new ExtractedSchemas(ComponentSchema.of(payloadSchema), Map.of());
132+
Schema<?> stringPropertySchema =
133+
PrimitiveType.fromType(String.class).createProperty();
134+
ComponentSchema stringComponentSchema = swaggerSchemaUtil.mapSchema(stringPropertySchema, schemaFormat);
135+
return new ExtractedSchemas(stringComponentSchema, Map.of());
112136
} else {
113137
Map<String, Schema> newSchemasToProcess = new LinkedHashMap<>(resolvedSchema.referencedSchemas);
114138
newSchemasToProcess.putIfAbsent(getNameFromType(type), resolvedSchema.schema);
@@ -117,7 +141,7 @@ public ExtractedSchemas resolveSchema(Type type, String contentType) {
117141
HashMap<String, Schema> processedSchemas = new HashMap<>(newSchemasToProcess);
118142
postProcessSchemas(newSchemasToProcess, processedSchemas, actualContentType);
119143

120-
return createExtractedSchemas(resolvedSchema.schema, processedSchemas);
144+
return createExtractedSchemas(resolvedSchema.schema, processedSchemas, schemaFormat);
121145
}
122146
}
123147

@@ -126,15 +150,14 @@ public ExtractedSchemas resolveSchema(Type type, String contentType) {
126150
*
127151
* @param rootSchema Swagger root schema
128152
* @param referencedSchemas all referenced swagger schemas
153+
* @param schemaFormat the schema-format of the schemas inside the resulting ExtractedSchemas
129154
* @return
130155
*/
131-
private ExtractedSchemas createExtractedSchemas(Schema rootSchema, Map<String, Schema> referencedSchemas) {
132-
ComponentSchema rootComponentSchema = swaggerSchemaUtil.mapSchemaOrRef(rootSchema);
133-
Map<String, SchemaObject> referencedSchemaObjects = swaggerSchemaUtil.mapSchemasMap(referencedSchemas);
134-
Map<String, ComponentSchema> referencedComponentSchemas = new HashMap<>();
135-
referencedSchemaObjects.forEach((schemaname, schemaobject) -> {
136-
referencedComponentSchemas.put(schemaname, ComponentSchema.of(schemaobject));
137-
});
156+
private ExtractedSchemas createExtractedSchemas(
157+
Schema rootSchema, Map<String, Schema> referencedSchemas, SchemaFormat schemaFormat) {
158+
ComponentSchema rootComponentSchema = swaggerSchemaUtil.mapSchemaOrRef(rootSchema, schemaFormat);
159+
Map<String, ComponentSchema> referencedComponentSchemas =
160+
swaggerSchemaUtil.mapSchemasMap(referencedSchemas, schemaFormat);
138161

139162
return new ExtractedSchemas(rootComponentSchema, referencedComponentSchemas);
140163
}

0 commit comments

Comments
 (0)