Skip to content

Commit 848f74c

Browse files
MikeEdgarAzquelt
andauthored
Backports 4.0.11 (#2282)
* Add option to disable sorted parameters (#2248) * Add option to disable sorted parameters Signed-off-by: Michael Edgar <[email protected]> * Document default property value, remove redundant parameter sort Signed-off-by: Michael Edgar <[email protected]> --------- Signed-off-by: Michael Edgar <[email protected]> * fix: use array item schema ref when available instead of inline schema (#2256) * fix: use array item schema ref when available instead of inline schema Signed-off-by: Michael Edgar <[email protected]> * Use `@Schema(type)` to determine items or merge strategy Dereference single-dimension array component when available in `readClassSchema` and `typeToSchema`. Signed-off-by: Michael Edgar <[email protected]> * Include all four varieties of array implementation in `@Schema` test Signed-off-by: Michael Edgar <[email protected]> --------- Signed-off-by: Michael Edgar <[email protected]> * fix: preserve Schema annotation `nullable` after setting type (#2257) * fix: preserve Schema annotation `nullable` after setting type Signed-off-by: Michael Edgar <[email protected]> * Avoid use of deprecated example property in test case Signed-off-by: Michael Edgar <[email protected]> * Add "else" branch in SchemaSupport#setTypeList, revert SchemaFactory chg Signed-off-by: Michael Edgar <[email protected]> --------- Signed-off-by: Michael Edgar <[email protected]> * fix: skip class introspection when `$ref` is derived from annotation (#2266) * fix: skip class introspection when implementation is given in annotation Signed-off-by: Michael Edgar <[email protected]> * fix: skip class introspection when `$ref` is derived from annotation Signed-off-by: Michael Edgar <[email protected]> Co-authored-by: Andrew Rouse <[email protected]> --------- Signed-off-by: Michael Edgar <[email protected]> Co-authored-by: Andrew Rouse <[email protected]> --------- Signed-off-by: Michael Edgar <[email protected]> Co-authored-by: Andrew Rouse <[email protected]>
1 parent a1ff079 commit 848f74c

File tree

18 files changed

+424
-47
lines changed

18 files changed

+424
-47
lines changed

README.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,11 @@ Set this value in order to change the maximum threshold for processed static fil
9595
mp.openapi.extensions.smallrye.merge-schema-examples
9696
----
9797
Set this boolean value to disable the merging of the deprecated `@Schema` `example` property into the `examples` array introduced in OAS 3.1.0. If not set, it will default to `true` the deprecated `example` will be mapped to the `examples` array in the OpenAPI model.
98+
99+
* Sorted Parameters
100+
+
101+
[source%nowrap]
102+
----
103+
mp.openapi.extensions.smallrye.sorted-parameters.enable
104+
----
105+
Set this boolean value to enable or disable the sorting of parameter array entries during annotation scanning. When enabled (set to `true`), parameters will be order either by their order within a `@Parameters` annotation on an operation method or (in the absence of that annotation) by their `$ref`, `in`, and `name` attributes. When disabled (set to `false`), parameters will be in the order they are encountered in the Java code. If not set, it will default to `true`.

core/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@
113113
<version>1.1.7</version>
114114
<scope>test</scope>
115115
</dependency>
116+
<dependency>
117+
<groupId>org.jetbrains</groupId>
118+
<artifactId>annotations</artifactId>
119+
<version>26.0.2</version>
120+
<scope>test</scope>
121+
</dependency>
116122
</dependencies>
117123

118124
<build>

core/src/main/java/io/smallrye/openapi/api/OpenApiConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,10 @@ default boolean mergeSchemaExamples() {
306306
return getConfigValue(SmallRyeOASConfig.SMALLRYE_MERGE_SCHEMA_EXAMPLES, Boolean.class, () -> Boolean.TRUE);
307307
}
308308

309+
default boolean sortedParametersEnable() {
310+
return getConfigValue(SmallRyeOASConfig.SMALLRYE_SORTED_PARAMETERS_ENABLE, Boolean.class, () -> Boolean.TRUE);
311+
}
312+
309313
default Integer getMaximumStaticFileSize() {
310314
return getConfigValue(SmallRyeOASConfig.MAXIMUM_STATIC_FILE_SIZE, Integer.class,
311315
() -> MAXIMUM_STATIC_FILE_SIZE_DEFAULT);

core/src/main/java/io/smallrye/openapi/api/SmallRyeOASConfig.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ private SmallRyeOASConfig() {
2424
private static final String SUFFIX_SORTED_PROPERTIES_ENABLE = "sorted-properties.enable";
2525
private static final String SUFFIX_REMOVE_UNUSED_SCHEMAS_ENABLE = "remove-unused-schemas.enable";
2626
private static final String SUFFIX_MERGE_SCHEMA_EXAMPLES = "merge-schema-examples";
27+
private static final String SUFFIX_SORTED_PARAMETERS_ENABLE = "sorted-parameters.enable";
2728
private static final String SMALLRYE_PREFIX = OASConfig.EXTENSIONS_PREFIX + VENDOR_NAME;
2829

2930
public static final String SMALLRYE_SCAN_DEPENDENCIES_DISABLE = SMALLRYE_PREFIX + SUFFIX_SCAN_DEPENDENCIES_DISABLE;
@@ -52,6 +53,8 @@ private SmallRyeOASConfig() {
5253

5354
public static final String SMALLRYE_MERGE_SCHEMA_EXAMPLES = SMALLRYE_PREFIX + SUFFIX_MERGE_SCHEMA_EXAMPLES;
5455

56+
public static final String SMALLRYE_SORTED_PARAMETERS_ENABLE = SMALLRYE_PREFIX + SUFFIX_SORTED_PARAMETERS_ENABLE;
57+
5558
public static final String SCAN_PROFILES = SMALLRYE_PREFIX + "scan.profiles";
5659

5760
public static final String SCAN_EXCLUDE_PROFILES = SMALLRYE_PREFIX + "scan.exclude.profiles";

core/src/main/java/io/smallrye/openapi/internal/models/media/SchemaSupport.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ private static List<SchemaType> getTypeList(Schema schema) {
167167
private static void setTypeList(Schema schema, List<SchemaType> types) {
168168
if (schema instanceof io.smallrye.openapi.internal.models.media.Schema) {
169169
((io.smallrye.openapi.internal.models.media.Schema) schema).setTypeList(types);
170+
} else {
171+
schema.setType(types);
170172
}
171-
schema.setType(types);
172173
}
173174
}

core/src/main/java/io/smallrye/openapi/runtime/io/schema/SchemaFactory.java

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ public static Schema readSchema(final AnnotationScannerContext context,
205205
schema.setDeprecated(readAttr(context, annotation, SchemaConstant.PROP_DEPRECATED, defaults));
206206

207207
final SchemaType type = readSchemaType(context, annotation, schema, defaults);
208-
SchemaSupport.setType(schema, readSchemaType(context, annotation, schema, defaults));
208+
SchemaSupport.setType(schema, type);
209209

210210
Object example = parseSchemaAttr(context, annotation, SchemaConstant.PROP_EXAMPLE, defaults, type);
211211

@@ -309,43 +309,46 @@ SchemaFactory.<Type[], List<Schema>> readAttr(context, annotation, SchemaConstan
309309
}
310310

311311
boolean namedComponent = Extensions.getName(schema) != null;
312+
Type implementation = context.annotations().value(annotation, SchemaConstant.PROP_IMPLEMENTATION);
312313

313314
if (JandexUtil.isSimpleClassSchema(annotation)) {
314-
Schema implSchema = readClassSchema(context,
315-
context.annotations().value(annotation, SchemaConstant.PROP_IMPLEMENTATION),
316-
!namedComponent);
315+
Schema implSchema = readClassSchema(context, implementation, !namedComponent);
317316
schema = MergeUtil.mergeObjects(implSchema, schema);
318317
} else if (JandexUtil.isSimpleArraySchema(context, annotation)) {
319-
Schema implSchema = readClassSchema(context,
320-
context.annotations().value(annotation, SchemaConstant.PROP_IMPLEMENTATION),
321-
!namedComponent);
318+
Schema implSchema = readClassSchema(context, implementation, !namedComponent);
322319
// If the @Schema annotation indicates an array type, then use the Schema
323320
// generated from the implementation Class as the "items" for the array.
324321
schema.setItems(implSchema);
325322
} else {
326-
schema = includeTypeSchema(context, schema,
327-
context.annotations().value(annotation, SchemaConstant.PROP_IMPLEMENTATION));
323+
List<SchemaType> declaredType = Optional
324+
.ofNullable(context.annotations().<String> value(annotation, SchemaConstant.PROP_TYPE))
325+
.map(SchemaFactory::parseSchemaType)
326+
.map(Collections::singletonList)
327+
.orElseGet(Collections::emptyList);
328+
329+
schema = includeTypeSchema(context, schema, implementation, declaredType);
328330
}
329331

330332
return schema;
331333
}
332334

333-
public static Schema includeTypeSchema(AnnotationScannerContext context, Schema schema, Type type) {
334-
Schema implSchema = null;
335-
335+
public static Schema includeTypeSchema(AnnotationScannerContext context, Schema schema, Type type,
336+
List<SchemaType> declaredType) {
336337
if (type != null /* && type.kind() == Kind.CLASS */) {
337-
implSchema = readClassSchema(context, type, false);
338-
}
339-
340-
if (schema.getType() != null && schema.getType().contains(Schema.SchemaType.ARRAY) && implSchema != null) {
341-
// If the @Schema annotation indicates an array type, then use the Schema
342-
// generated from the implementation Class as the "items" for the array.
343-
schema.setItems(implSchema);
344-
} else if (implSchema != null) {
345-
// If there is an impl class - merge the @Schema properties *onto* the schema
346-
// generated from the Class so that the annotation properties override the class
347-
// properties (as required by the MP+OAI spec).
348-
schema = MergeUtil.mergeObjects(implSchema, schema);
338+
Schema implSchema = readClassSchema(context, type, false);
339+
340+
if (implSchema != null) {
341+
if (declaredType.contains(Schema.SchemaType.ARRAY)) {
342+
// If the @Schema annotation indicates an array type, then use the Schema
343+
// generated from the implementation Class as the "items" for the array.
344+
schema.setItems(lookupRef(context, type, implSchema));
345+
} else {
346+
// If there is an impl class - merge the @Schema properties *onto* the schema
347+
// generated from the Class so that the annotation properties override the class
348+
// properties (as required by the MP+OAI spec).
349+
schema = MergeUtil.mergeObjects(implSchema, schema);
350+
}
351+
}
349352
}
350353

351354
return schema;
@@ -531,15 +534,20 @@ static Schema readClassSchema(final AnnotationScannerContext context, Type type,
531534
ArrayType array = type.asArrayType();
532535
int dimensions = array.dimensions();
533536
Type componentType = array.constituent();
537+
Schema itemSchema;
534538

535539
if (dimensions > 1) {
536540
// Recurse using a new array type with dimensions decremented
537-
schema.setItems(
538-
readClassSchema(context, ArrayType.create(componentType, dimensions - 1), schemaReferenceSupported));
541+
itemSchema = readClassSchema(context, ArrayType.create(componentType, dimensions - 1),
542+
schemaReferenceSupported);
539543
} else {
540544
// Recurse using the type of the array elements
541-
schema.setItems(readClassSchema(context, componentType, schemaReferenceSupported));
545+
itemSchema = readClassSchema(context, componentType, schemaReferenceSupported);
546+
// Maybe dereference
547+
itemSchema = lookupRef(context, componentType, itemSchema);
542548
}
549+
550+
schema.setItems(itemSchema);
543551
} else if (type.kind() == Type.Kind.PRIMITIVE) {
544552
schema = OpenApiDataObjectScanner.process(type.asPrimitiveType());
545553
} else {
@@ -597,15 +605,19 @@ public static Schema typeToSchema(final AnnotationScannerContext context, Type t
597605
ArrayType array = type.asArrayType();
598606
int dimensions = array.dimensions();
599607
Type componentType = array.constituent();
608+
Schema itemSchema;
600609

601610
if (dimensions > 1) {
602611
// Recurse using a new array type with dimensions decremented
603-
schema.setItems(
604-
typeToSchema(context, ArrayType.create(componentType, dimensions - 1), null));
612+
itemSchema = typeToSchema(context, ArrayType.create(componentType, dimensions - 1), null);
605613
} else {
606614
// Recurse using the type of the array elements
607-
schema.setItems(typeToSchema(context, componentType, null));
615+
itemSchema = typeToSchema(context, componentType, null);
616+
// Maybe dereference
617+
itemSchema = lookupRef(context, componentType, itemSchema);
608618
}
619+
620+
schema.setItems(itemSchema);
609621
} else if (type.kind() == Type.Kind.CLASS) {
610622
schema = introspectClassToSchema(context, type.asClassType(), true);
611623
} else if (type.kind() == Type.Kind.PRIMITIVE) {
@@ -727,7 +739,21 @@ public static Schema schemaRegistration(final AnnotationScannerContext context,
727739

728740
if (allowRegistration(context, schemaRegistry, type, schema)) {
729741
schema = schemaRegistry.register(type, context.getJsonViews(), schema);
730-
} else if (schemaRegistry != null && schemaRegistry.hasRef(type, context.getJsonViews())) {
742+
} else {
743+
schema = lookupRef(context, type, schema);
744+
}
745+
746+
return schema;
747+
}
748+
749+
/**
750+
* If the given type is found in the registry and has a reference, replace the
751+
* given schema with a schema reference. Otherwise, simply return the schema unaltered.
752+
*/
753+
static Schema lookupRef(AnnotationScannerContext context, Type type, Schema schema) {
754+
SchemaRegistry schemaRegistry = context.getSchemaRegistry();
755+
756+
if (schemaRegistry.hasRef(type, context.getJsonViews())) {
731757
schema = schemaRegistry.lookupRef(type, context.getJsonViews());
732758
}
733759

core/src/main/java/io/smallrye/openapi/runtime/scanner/OpenApiDataObjectScanner.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ private void depthFirstGraphSearch() {
256256
TypeUtil.mapDeprecated(context, currentClass, currentSchema::getDeprecated, currentSchema::setDeprecated);
257257
currentPathEntry.setSchema(currentSchema);
258258

259+
if (currentSchema.getRef() != null) {
260+
continue;
261+
}
262+
259263
if (!hasNonNullType(currentSchema)) {
260264
// If not schema has yet been set, consider this an "object"
261265
SchemaSupport.setType(currentSchema, Schema.SchemaType.OBJECT);
@@ -292,7 +296,7 @@ private void depthFirstGraphSearch() {
292296

293297
private void maybeRegisterSchema(Type currentType, Schema currentSchema, Schema entrySchema) {
294298
Schema ref = SchemaFactory.schemaRegistration(context, currentType, currentSchema);
295-
299+
Schema.SchemaType type = SchemaSupport.getNonNullType(currentSchema);
296300
/*
297301
* Ignore the returned ref when:
298302
*
@@ -301,7 +305,7 @@ private void maybeRegisterSchema(Type currentType, Schema currentSchema, Schema
301305
* - the target of the ref is the schema currently being processed and using the ref would
302306
* result in a self-referencing schema.
303307
*/
304-
if (ref != currentSchema && !currentSchema.getType().contains(Schema.SchemaType.OBJECT) && ref.getRef() != null) {
308+
if (ref != currentSchema && type != Schema.SchemaType.OBJECT && ref.getRef() != null) {
305309
Schema refTarget = context.getSchemaRegistry().lookupSchema(currentType, context.getJsonViews());
306310

307311
if (refTarget != entrySchema) {

core/src/main/java/io/smallrye/openapi/runtime/scanner/ResourceParameters.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,7 @@ public void setFormBodyContent(Content formBodyContent) {
130130
this.formBodyContent = formBodyContent;
131131
}
132132

133-
public void sort(List<Parameter> preferredOrder) {
134-
Comparator<Parameter> comparator = parameterComparator(preferredOrder);
135-
133+
public void sort(Comparator<Parameter> comparator) {
136134
if (pathItemParameters != null) {
137135
pathItemParameters.sort(comparator);
138136
}

core/src/main/java/io/smallrye/openapi/runtime/scanner/dataobject/AnnotationTargetProcessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ Schema processField() {
220220

221221
if (registrationCandidate) {
222222
if (fieldAssertionConflicts(fieldSchema, typeSchema)) {
223-
fieldSchema = SchemaFactory.includeTypeSchema(context, fieldSchema, fieldType);
223+
List<SchemaType> declaredType = Optional.ofNullable(fieldSchema.getType()).orElseGet(Collections::emptyList);
224+
fieldSchema = SchemaFactory.includeTypeSchema(context, fieldSchema, fieldType, declaredType);
224225
} else {
225226
typeProcessor.pushObjectStackInput();
226227
Schema registeredTypeSchema;

core/src/main/java/io/smallrye/openapi/runtime/scanner/spi/AbstractParameterProcessor.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.util.Arrays;
88
import java.util.Collection;
99
import java.util.Collections;
10+
import java.util.Comparator;
1011
import java.util.HashMap;
1112
import java.util.HashSet;
1213
import java.util.LinkedHashMap;
@@ -83,7 +84,7 @@ public abstract class AbstractParameterProcessor {
8384
* Collection of parameters scanned at the current level. This map contains
8485
* all parameter types except for form parameters and matrix parameters.
8586
*/
86-
protected Map<ParameterContextKey, ParameterContext> params = new HashMap<>();
87+
protected Map<ParameterContextKey, ParameterContext> params = new LinkedHashMap<>();
8788

8889
/**
8990
* Collection of form parameters found during scanning.
@@ -330,16 +331,21 @@ protected void processOperationParameters(MethodInfo resourceMethod, ResourcePar
330331
* Read the resource method and any method super classes/interfaces that it may override
331332
*/
332333
candidateMethods.stream()
333-
.flatMap(m -> m.annotations().stream().filter(a -> !JandexUtil.equals(a.target(), m)))
334+
.flatMap(m -> m.annotations().stream())
335+
.filter(a -> Objects.nonNull(a.target()))
336+
.filter(a -> a.target().kind() == Kind.METHOD_PARAMETER)
337+
.sorted(Comparator.comparing(a -> a.target().asMethodParameter().position()))
334338
.forEach(this::readAnnotatedType);
335339

336340
/*
337-
* Phase III - Read @Parameter(s) annotations directly on the record method
341+
* Phase III - Read @Parameter(s) annotations directly on the resource method
338342
*
339343
* Read the resource method and any method super classes/interfaces that it may override
340344
*/
341345
candidateMethods.stream()
342-
.flatMap(m -> m.annotations().stream().filter(a -> JandexUtil.equals(a.target(), m)))
346+
.flatMap(m -> m.annotations().stream())
347+
.filter(a -> Objects.nonNull(a.target()))
348+
.filter(a -> a.target().kind() == Kind.METHOD)
343349
.filter(a -> openApiParameterAnnotations.contains(a.name()))
344350
.forEach(this::readParameterAnnotation);
345351

@@ -367,7 +373,9 @@ protected void processFinalize(ClassInfo resourceClass, MethodInfo resourceMetho
367373
.forEach(parameters::addOperationParameter);
368374

369375
// Re-sort (names of matrix parameters may have changed)
370-
parameters.sort(preferredOrder);
376+
if (scannerContext.getConfig().sortedParametersEnable()) {
377+
parameters.sort(ResourceParameters.parameterComparator(preferredOrder));
378+
}
371379

372380
parameters.setFormBodyContent(getFormBodyContent());
373381
}
@@ -482,17 +490,14 @@ boolean templateParameterPatternEligible(Parameter param) {
482490
* @return list of {@link Parameter}s
483491
*/
484492
protected List<Parameter> getParameters(MethodInfo resourceMethod) {
485-
List<Parameter> parameters;
486-
487493
// Process any Matrix Parameters found
488494
mapMatrixParameters();
489495

490496
// Convert ParameterContext entries to MP-OAI Parameters
491-
parameters = this.params.values()
497+
List<Parameter> parameters = this.params.values()
492498
.stream()
493499
.map(context -> this.mapParameter(resourceMethod, context))
494500
.filter(Objects::nonNull)
495-
.sorted(ResourceParameters.parameterComparator(preferredOrder))
496501
.collect(Collectors.toList());
497502

498503
return parameters.isEmpty() ? null : parameters;

0 commit comments

Comments
 (0)