Skip to content

Commit a717f58

Browse files
committed
feat: tests for schemaformat option for payload schemas added
1 parent 277bfd2 commit a717f58

File tree

14 files changed

+1587
-14
lines changed

14 files changed

+1587
-14
lines changed

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/examples/walkers/DefaultSchemaWalker.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.swagger.v3.oas.models.media.StringSchema;
88
import lombok.RequiredArgsConstructor;
99
import lombok.extern.slf4j.Slf4j;
10+
import org.springframework.lang.Nullable;
1011
import org.springframework.util.CollectionUtils;
1112

1213
import java.text.SimpleDateFormat;
@@ -175,7 +176,14 @@ private Optional<T> buildExampleFromUnvisitedSchema(
175176
return composedSchemaExample;
176177
}
177178

178-
String type = schema.getType();
179+
// schema may be an openapi v3 or v3.1 schema. While v3 uses an simple 'type' field, v3.1 supports a set of
180+
// types, for example ["string", "null"].
181+
182+
String type = getTypeForExampleValue(schema);
183+
if (type == null) {
184+
return Optional.empty();
185+
}
186+
179187
return switch (type) {
180188
case "array" -> buildArrayExample(schema, definitions, visited);
181189
case "boolean" -> exampleValueGenerator.createBooleanExample(DEFAULT_BOOLEAN_EXAMPLE, schema);
@@ -235,6 +243,33 @@ private String getFirstEnumValue(Schema schema) {
235243
return null;
236244
}
237245

246+
/**
247+
* looks in schemas openapi-v3 'type' and openapi-v3.1 'types' fields to
248+
* find the best candidate to use as an example value.
249+
*
250+
* @param schema
251+
* @return the type to use for example values, or null if no suitable type was found.
252+
*/
253+
@Nullable
254+
String getTypeForExampleValue(Schema schema) {
255+
// if the single type field is present, it has precedence over the types field
256+
if (schema.getType() != null) {
257+
return schema.getType();
258+
}
259+
260+
Set<String> types = schema.getTypes();
261+
262+
if (types == null || types.isEmpty()) {
263+
return null;
264+
}
265+
266+
return types.stream()
267+
.filter(t -> !"null".equals(t))
268+
.sorted() // sort types to be deterministic
269+
.findFirst()
270+
.orElse(null);
271+
}
272+
238273
private Optional<T> buildFromComposedSchema(
239274
Optional<String> name, Schema schema, Map<String, Schema> definitions, Set<Schema> visited) {
240275
final List<Schema> schemasAllOf = schema.getAllOf();

springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ private void markSchemas(AsyncAPI fullAsyncApi, MarkingContext markingContext, S
203203
* properties, allOf, anyOf, oneOf, not- and items references. Trys to deduce the schema id from the ref path and
204204
* returns a Set of detected schema ids.
205205
*
206-
* @param markingContext the current {@link MarkingContext}
206+
* @param markingContext the current {@link MarkingContext}
207207
* @param componentSchema the {@link ComponentSchema} to analyze
208208
* @return Set of schema ids representing nested schema refs
209209
*/
@@ -217,14 +217,16 @@ private static Set<String> findUnmarkedNestedSchemas(
217217
if (componentSchema.getMultiFormatSchema() != null) {
218218
MultiFormatSchema multiFormatSchema = componentSchema.getMultiFormatSchema();
219219

220-
// Currently we support async_api and open_api format.
220+
// Currently we support async_api and open_api v3 and v3.1 format.
221221
// The concrete schemaformat mediatype can contain json/yaml postfix, so we check wether the begin of the
222222
// media type matches.
223-
if (multiFormatSchema.getSchemaFormat().startsWith(SchemaFormat.ASYNCAPI_V3.toString())
223+
String schemaFormat = multiFormatSchema.getSchemaFormat();
224+
if (schemaFormat.startsWith(SchemaFormat.ASYNCAPI_V3.toString())
224225
&& multiFormatSchema.getSchema() instanceof SchemaObject schemaObject) {
225226
return findUnmarkedNestedSchemasForAsyncAPISchema(markingContext, schemaObject);
226227
}
227-
if (multiFormatSchema.getSchemaFormat().startsWith(SchemaFormat.OPENAPI_V3.toString())
228+
if ((schemaFormat.startsWith(SchemaFormat.OPENAPI_V3.toString())
229+
|| schemaFormat.startsWith(SchemaFormat.OPENAPI_V3_1.toString()))
228230
&& multiFormatSchema.getSchema() instanceof Schema openapiSchema) {
229231
return findUnmarkedNestedSchemasForOpenAPISchema(markingContext, openapiSchema);
230232
}
@@ -240,7 +242,7 @@ private static Set<String> findUnmarkedNestedSchemas(
240242
* returns a Set of detected schema ids.
241243
*
242244
* @param markingContext the current {@link MarkingContext}
243-
* @param schema the {@link SchemaObject} to analyze
245+
* @param schema the {@link SchemaObject} to analyze
244246
* @return Set of schema ids representing nested schema refs
245247
*/
246248
private static Set<String> findUnmarkedNestedSchemasForAsyncAPISchema(
@@ -276,7 +278,7 @@ private static Set<String> findUnmarkedNestedSchemasForAsyncAPISchema(
276278
* returns a Set of detected schema ids.
277279
*
278280
* @param markingContext the current {@link MarkingContext}
279-
* @param openapiSchema the Swagger {@link Schema} to analyze
281+
* @param openapiSchema the Swagger {@link Schema} to analyze
280282
* @return Set of schema ids representing nested schema refs
281283
*/
282284
private static Set<String> findUnmarkedNestedSchemasForOpenAPISchema(

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,12 @@ public MessageObject buildMessage(AsyncOperation operationData, Method method) {
4242
Map<String, MessageBinding> messageBinding =
4343
AsyncAnnotationUtil.processMessageBindingFromAnnotation(method, messageBindingProcessors);
4444

45-
var messagePayload = MessagePayload.of(
46-
MultiFormatSchema.builder().schema(payloadSchema.payload()).build());
45+
// if payloadSchema.payload is already an instance of MultiFormatSchema, do not wrap again.
46+
MultiFormatSchema multiFormatSchema = (payloadSchema.payload() instanceof MultiFormatSchema mfs)
47+
? mfs
48+
: MultiFormatSchema.builder().schema(payloadSchema.payload()).build();
49+
50+
var messagePayload = MessagePayload.of(multiFormatSchema);
4751

4852
var builder = MessageObject.builder()
4953
.messageId(payloadSchema.name())

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ public ComponentSchema mapSchemaOrRef(Schema swaggerSchema, SchemaFormat schemaF
6262
*/
6363
public ComponentSchema mapSchema(Schema swaggerSchema, SchemaFormat schemaFormat) {
6464
return switch (schemaFormat) {
65-
case OPENAPI_V3, OPENAPI_V3_1 ->
66-
ComponentSchema.of(new MultiFormatSchema(schemaFormat.value, swaggerSchema));
65+
case OPENAPI_V3, OPENAPI_V3_1 -> ComponentSchema.of(
66+
new MultiFormatSchema(schemaFormat.value, swaggerSchema));
6767
case ASYNCAPI_V3 -> ComponentSchema.of(mapSwaggerSchemaToAsyncApiSchema(swaggerSchema));
6868
default -> throw new IllegalArgumentException("SchemaFormat " + schemaFormat + " is not supported");
6969
};

springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/components/SwaggerSchemaUtilTest.java

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package io.github.springwolf.core.asyncapi.components;
33

44
import io.github.springwolf.asyncapi.v3.model.components.ComponentSchema;
5+
import io.github.springwolf.asyncapi.v3.model.schema.MultiFormatSchema;
56
import io.github.springwolf.asyncapi.v3.model.schema.SchemaFormat;
67
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
78
import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference;
@@ -27,6 +28,48 @@ class SwaggerSchemaUtilTest {
2728
@Nested
2829
class MapSchemaOrRef {
2930

31+
@Test
32+
void mapToOpenApiV3Schema() {
33+
// given
34+
ObjectSchema schema = new ObjectSchema();
35+
schema.setType(SchemaType.STRING);
36+
ExternalDocumentation externalDocs = new ExternalDocumentation();
37+
externalDocs.setDescription("description");
38+
externalDocs.setUrl("url");
39+
schema.setExternalDocs(externalDocs);
40+
41+
// when
42+
ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, SchemaFormat.OPENAPI_V3);
43+
44+
// then
45+
// componentSchema should contain a MultiFormatSchema with the original openapi schema
46+
MultiFormatSchema multiFormatSchema = componentSchema.getMultiFormatSchema();
47+
assertThat(multiFormatSchema).isNotNull();
48+
assertThat(multiFormatSchema.getSchema()).isSameAs(schema);
49+
assertThat(multiFormatSchema.getSchemaFormat()).isEqualTo(SchemaFormat.OPENAPI_V3.value);
50+
}
51+
52+
@Test
53+
void mapToOpenApiV31Schema() {
54+
// given
55+
ObjectSchema schema = new ObjectSchema();
56+
schema.setType(SchemaType.STRING);
57+
ExternalDocumentation externalDocs = new ExternalDocumentation();
58+
externalDocs.setDescription("description");
59+
externalDocs.setUrl("url");
60+
schema.setExternalDocs(externalDocs);
61+
62+
// when
63+
ComponentSchema componentSchema = swaggerSchemaUtil.mapSchemaOrRef(schema, SchemaFormat.OPENAPI_V3_1);
64+
65+
// then
66+
// componentSchema should contain a MultiFormatSchema with the original openapi schema
67+
MultiFormatSchema multiFormatSchema = componentSchema.getMultiFormatSchema();
68+
assertThat(multiFormatSchema).isNotNull();
69+
assertThat(multiFormatSchema.getSchema()).isSameAs(schema);
70+
assertThat(multiFormatSchema.getSchemaFormat()).isEqualTo(SchemaFormat.OPENAPI_V3_1.value);
71+
}
72+
3073
@Test
3174
void mapReference() {
3275
// given
@@ -56,6 +99,49 @@ void mapSchema() {
5699

57100
@Nested
58101
class MapSchema {
102+
103+
@Test
104+
void mapToOpenApiV3Schema() {
105+
// given
106+
ObjectSchema schema = new ObjectSchema();
107+
schema.setType(SchemaType.STRING);
108+
ExternalDocumentation externalDocs = new ExternalDocumentation();
109+
externalDocs.setDescription("description");
110+
externalDocs.setUrl("url");
111+
schema.setExternalDocs(externalDocs);
112+
113+
// when
114+
ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.OPENAPI_V3);
115+
116+
// then
117+
// componentSchema should contain a MultiFormatSchema with the original openapi schema
118+
MultiFormatSchema multiFormatSchema = componentSchema.getMultiFormatSchema();
119+
assertThat(multiFormatSchema).isNotNull();
120+
assertThat(multiFormatSchema.getSchema()).isSameAs(schema);
121+
assertThat(multiFormatSchema.getSchemaFormat()).isEqualTo(SchemaFormat.OPENAPI_V3.value);
122+
}
123+
124+
@Test
125+
void mapToOpenApiV31Schema() {
126+
// given
127+
ObjectSchema schema = new ObjectSchema();
128+
schema.setType(SchemaType.STRING);
129+
ExternalDocumentation externalDocs = new ExternalDocumentation();
130+
externalDocs.setDescription("description");
131+
externalDocs.setUrl("url");
132+
schema.setExternalDocs(externalDocs);
133+
134+
// when
135+
ComponentSchema componentSchema = swaggerSchemaUtil.mapSchema(schema, SchemaFormat.OPENAPI_V3_1);
136+
137+
// then
138+
// componentSchema should contain a MultiFormatSchema with the original openapi schema
139+
MultiFormatSchema multiFormatSchema = componentSchema.getMultiFormatSchema();
140+
assertThat(multiFormatSchema).isNotNull();
141+
assertThat(multiFormatSchema.getSchema()).isSameAs(schema);
142+
assertThat(multiFormatSchema.getSchemaFormat()).isEqualTo(SchemaFormat.OPENAPI_V3_1.value);
143+
}
144+
59145
@Test
60146
void mapExternalDocs() {
61147
// given
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
package io.github.springwolf.core.asyncapi.components.examples.walkers;
3+
4+
import io.swagger.v3.oas.models.media.Schema;
5+
import org.junit.jupiter.api.Test;
6+
7+
import java.util.Set;
8+
9+
import static org.assertj.core.api.Assertions.assertThat;
10+
11+
class DefaultSchemaWalkerTest {
12+
13+
@Test
14+
void getTypeForExampleValue() {
15+
DefaultSchemaWalker schemaWalker = new DefaultSchemaWalker<>(null);
16+
17+
Schema schema1 = new Schema(); // no types defined.
18+
Schema schema2 = new Schema().type("string");
19+
Schema schema3 = new Schema().types(Set.of("string", "null"));
20+
Schema schema4 = new Schema().types(Set.of("string", "integer"));
21+
22+
assertThat(schemaWalker.getTypeForExampleValue(schema1)).isNull();
23+
assertThat(schemaWalker.getTypeForExampleValue(schema2)).isEqualTo("string");
24+
assertThat(schemaWalker.getTypeForExampleValue(schema3)).isEqualTo("string");
25+
assertThat(schemaWalker.getTypeForExampleValue(schema4)).isEqualTo("integer");
26+
}
27+
}

springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/schemas/SwaggerSchemaServiceTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ void classWithSchemaAnnotationWithAsyncapiSchemaformat() {
6767
@Test
6868
void classWithSchemaAnnotationWithOpenapiSchemaformat() {
6969
ComponentSchema schema = schemaService
70-
.resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.ASYNCAPI_V3)
70+
.resolveSchema(ClassWithSchemaAnnotation.class, "content-type-not-relevant", SchemaFormat.OPENAPI_V3)
7171
.rootSchema();
7272

7373
assertThat(schema.getReference().getRef()).isEqualTo("#/components/schemas/DifferentName");
@@ -106,7 +106,7 @@ void postProcessorsAreCalledWithAsyncapiSchemaformat() {
106106

107107
@Test
108108
void postProcessorsAreCalledWithOpenapiSchemaformat() {
109-
schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "some-content-type", SchemaFormat.ASYNCAPI_V3);
109+
schemaService.resolveSchema(ClassWithSchemaAnnotation.class, "some-content-type", SchemaFormat.OPENAPI_V3);
110110

111111
verify(schemasPostProcessor).process(any(), any(), eq("some-content-type"));
112112
verify(schemasPostProcessor2).process(any(), any(), eq("some-content-type"));

0 commit comments

Comments
 (0)