Skip to content

Commit aee7cc2

Browse files
committed
Merge branch 'fix/kotlin' of github.com:mymx2/springdoc-openapi into mymx2-fix/kotlin
2 parents da85bc2 + 12dbff1 commit aee7cc2

File tree

28 files changed

+1399
-184
lines changed

28 files changed

+1399
-184
lines changed

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocKotlinConfiguration.kt

+5-8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import org.springdoc.core.customizers.KotlinDeprecatedPropertyCustomizer
3131
import org.springdoc.core.customizers.ParameterCustomizer
3232
import org.springdoc.core.providers.ObjectMapperProvider
3333
import org.springdoc.core.utils.Constants
34+
import org.springdoc.core.utils.SchemaUtils
3435
import org.springdoc.core.utils.SpringDocUtils
3536
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
3637
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass
@@ -75,17 +76,13 @@ class SpringDocKotlinConfiguration() {
7576
}
7677

7778
/**
78-
* Kotlin springdoc-openapi ParameterCustomizer
79+
* Kotlin springdoc-openapi ParameterCustomizer.
80+
* deprecated as not anymore required, use [SchemaUtils.fieldNullable]
7981
*
8082
* @return the nullable Kotlin Request Parameter Customizer
83+
* @see SchemaUtils.fieldNullable()
8184
*/
82-
@Bean
83-
@Lazy(false)
84-
@ConditionalOnProperty(
85-
name = [Constants.SPRINGDOC_NULLABLE_REQUEST_PARAMETER_ENABLED],
86-
matchIfMissing = true
87-
)
88-
@ConditionalOnMissingBean
85+
@Deprecated("Deprecated since 2.8.7", level = DeprecationLevel.ERROR)
8986
fun nullableKotlinRequestParameterCustomizer(): ParameterCustomizer {
9087
return ParameterCustomizer { parameterModel, methodParameter ->
9188
if (parameterModel == null) return@ParameterCustomizer null

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/DelegatingMethodParameterCustomizer.java

+15
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,27 @@
2727
package org.springdoc.core.customizers;
2828

2929
import org.springframework.core.MethodParameter;
30+
import org.springframework.lang.Nullable;
31+
32+
import java.util.List;
3033

3134
/**
3235
* The interface Delegating method parameter customizer.
36+
* @author dyun
3337
*/
3438
@FunctionalInterface
3539
public interface DelegatingMethodParameterCustomizer {
40+
/**
41+
* Customize.
42+
* tip: parameters include the parent fields, you can choose how to deal with the methodParameters
43+
*
44+
* @param originalParameter the original parameter
45+
* @param methodParameters the exploded parameters
46+
*/
47+
@Nullable
48+
default void customizeList(MethodParameter originalParameter, List<MethodParameter> methodParameters) {
49+
methodParameters.forEach(parameter -> customize(originalParameter, parameter));
50+
}
3651

3752
/**
3853
* Customize.

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRequestService.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ private void addParameters(OpenAPI openAPI, RequestMethod requestMethod, MethodA
227227
MethodParameter methodParameter, ParameterInfo parameterInfo, Parameter parameter) {
228228
List<Annotation> parameterAnnotations = Arrays.asList(getParameterAnnotations(methodParameter));
229229
if (requestBuilder.isValidParameter(parameter,methodAttributes)) {
230-
requestBuilder.applyBeanValidatorAnnotations(parameter, parameterAnnotations, parameterInfo.isParameterObject());
230+
requestBuilder.applyBeanValidatorAnnotations(methodParameter, parameter, parameterAnnotations, parameterInfo.isParameterObject());
231231
operation.addParametersItem(parameter);
232232
}
233233
else if (!RequestMethod.GET.equals(requestMethod)) {

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/DelegatingMethodParameter.java

+23-7
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.util.List;
3939
import java.util.Objects;
4040
import java.util.Optional;
41+
import java.util.concurrent.CopyOnWriteArrayList;
4142

4243
import org.apache.commons.lang3.ArrayUtils;
4344
import org.apache.commons.lang3.reflect.FieldUtils;
@@ -88,6 +89,11 @@ public class DelegatingMethodParameter extends MethodParameter {
8889
*/
8990
private final boolean isParameterObject;
9091

92+
/**
93+
* If Is parameter object. then The Field should be not null
94+
*/
95+
private final Field field;
96+
9197
/**
9298
* The Method annotations.
9399
*/
@@ -108,9 +114,10 @@ public class DelegatingMethodParameter extends MethodParameter {
108114
* @param isParameterObject the is parameter object
109115
* @param isNotRequired the is required
110116
*/
111-
DelegatingMethodParameter(MethodParameter delegate, String parameterName, Annotation[] additionalParameterAnnotations, Annotation[] methodAnnotations, boolean isParameterObject, boolean isNotRequired) {
117+
DelegatingMethodParameter(MethodParameter delegate, String parameterName, Annotation[] additionalParameterAnnotations, Annotation[] methodAnnotations, boolean isParameterObject, Field field, boolean isNotRequired) {
112118
super(delegate);
113119
this.delegate = delegate;
120+
this.field = field;
114121
this.additionalParameterAnnotations = additionalParameterAnnotations;
115122
this.parameterName = parameterName;
116123
this.isParameterObject = isParameterObject;
@@ -139,14 +146,14 @@ public static MethodParameter[] customize(String[] pNames, MethodParameter[] par
139146
.anyMatch(annotation -> Arrays.asList(RequestBody.class, RequestPart.class).contains(annotation.annotationType()));
140147
if (!MethodParameterPojoExtractor.isSimpleType(paramClass)
141148
&& (hasFlatAnnotation || (defaultFlatParamObject && !hasNotFlatAnnotation && !AbstractRequestService.isRequestTypeToIgnore(paramClass)))) {
142-
MethodParameterPojoExtractor.extractFrom(paramClass).forEach(methodParameter -> {
143-
optionalDelegatingMethodParameterCustomizers.ifPresent(delegatingMethodParameterCustomizers -> delegatingMethodParameterCustomizers.forEach(customizer -> customizer.customize(p, methodParameter)));
144-
explodedParameters.add(methodParameter);
145-
});
149+
List<MethodParameter> flatParams = new CopyOnWriteArrayList<>();
150+
MethodParameterPojoExtractor.extractFrom(paramClass).forEach(flatParams::add);
151+
optionalDelegatingMethodParameterCustomizers.orElseGet(ArrayList::new).forEach(cz -> cz.customizeList(p, flatParams));
152+
explodedParameters.addAll(flatParams);
146153
}
147154
else {
148155
String name = pNames != null ? pNames[i] : p.getParameterName();
149-
explodedParameters.add(new DelegatingMethodParameter(p, name, null, null, false, false));
156+
explodedParameters.add(new DelegatingMethodParameter(p, name, null, null, false, null, false));
150157
}
151158
}
152159
return explodedParameters.toArray(new MethodParameter[0]);
@@ -297,4 +304,13 @@ public boolean isParameterObject() {
297304
return isParameterObject;
298305
}
299306

300-
}
307+
/**
308+
* Gets field. If Is parameter object. then The Field should be not null
309+
* @return the field
310+
* @see #isParameterObject
311+
*/
312+
@Nullable
313+
public Field getField() {
314+
return field;
315+
}
316+
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/MethodParameterPojoExtractor.java

+12-66
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,7 @@
4646
import java.time.ZoneOffset;
4747
import java.util.ArrayList;
4848
import java.util.Arrays;
49-
import java.util.Collection;
5049
import java.util.HashSet;
51-
import java.util.LinkedHashSet;
5250
import java.util.List;
5351
import java.util.Map;
5452
import java.util.Objects;
@@ -61,17 +59,16 @@
6159
import java.util.concurrent.atomic.AtomicInteger;
6260
import java.util.concurrent.atomic.AtomicLong;
6361
import java.util.function.Predicate;
64-
import java.util.stream.Collectors;
6562
import java.util.stream.Stream;
6663

6764
import io.swagger.v3.core.util.PrimitiveType;
6865
import io.swagger.v3.oas.annotations.Parameter;
6966
import io.swagger.v3.oas.annotations.media.Schema;
7067

68+
import org.springdoc.core.utils.SchemaUtils;
7169
import org.springframework.core.GenericTypeResolver;
7270
import org.springframework.core.MethodParameter;
7371

74-
import static org.springdoc.core.service.AbstractRequestService.hasNotNullAnnotation;
7572
import static org.springdoc.core.utils.Constants.DOT;
7673

7774
/**
@@ -174,13 +171,13 @@ private static Stream<MethodParameter> fromGetterOfField(Class<?> paramClass, Fi
174171
else {
175172
Parameter parameter = field.getAnnotation(Parameter.class);
176173
Schema schema = field.getAnnotation(Schema.class);
177-
boolean visible = resolveVisible(parameter, schema);
174+
boolean visible = SchemaUtils.swaggerVisible(schema, parameter);
178175
if (!visible) {
179176
return Stream.empty();
180177
}
181178
String prefix = fieldNamePrefix + resolveName(parameter, schema).orElse(field.getName()) + DOT;
182-
boolean isNullable = isNullable(field.getDeclaredAnnotations());
183-
return extractFrom(type, prefix, parentRequired && resolveRequired(schema, parameter, isNullable));
179+
boolean fieldRequired = SchemaUtils.fieldRequired(field, schema, parameter);
180+
return extractFrom(type, prefix, parentRequired && fieldRequired);
184181
}
185182
}
186183

@@ -208,46 +205,6 @@ private static Optional<String> resolveNameFromSchema(Schema schema) {
208205
return Optional.of(schema.name());
209206
}
210207

211-
private static boolean resolveVisible(Parameter parameter, Schema schema) {
212-
if (parameter != null) {
213-
return !parameter.hidden();
214-
}
215-
if (schema != null) {
216-
return !schema.hidden();
217-
}
218-
return true;
219-
}
220-
221-
private static boolean resolveRequired(Schema schema, Parameter parameter, boolean nullable) {
222-
if (parameter != null) {
223-
return resolveRequiredFromParameter(parameter, nullable);
224-
}
225-
if (schema != null) {
226-
return resolveRequiredFromSchema(schema, nullable);
227-
}
228-
return !nullable;
229-
}
230-
231-
private static boolean resolveRequiredFromParameter(Parameter parameter, boolean nullable) {
232-
if (parameter.required()) {
233-
return true;
234-
}
235-
return !nullable;
236-
}
237-
238-
private static boolean resolveRequiredFromSchema(Schema schema, boolean nullable) {
239-
if (schema.required()) {
240-
return true;
241-
}
242-
else if (schema.requiredMode() == Schema.RequiredMode.REQUIRED) {
243-
return true;
244-
}
245-
else if (schema.requiredMode() == Schema.RequiredMode.NOT_REQUIRED) {
246-
return false;
247-
}
248-
return !nullable;
249-
}
250-
251208
/**
252209
* Extract the type
253210
*
@@ -277,20 +234,21 @@ private static Class<?> extractType(Class<?> paramClass, Field field) {
277234
* @param fieldNamePrefix the field name prefix
278235
* @return the stream
279236
*/
280-
private static Stream<MethodParameter> fromSimpleClass(Class<?> paramClass, Field field, String fieldNamePrefix, boolean isParentRequired) {
237+
private static Stream<MethodParameter> fromSimpleClass(Class<?> paramClass, Field field, String fieldNamePrefix, boolean parentRequired) {
281238
Annotation[] fieldAnnotations = field.getDeclaredAnnotations();
282239
try {
283240
Parameter parameter = field.getAnnotation(Parameter.class);
284241
Schema schema = field.getAnnotation(Schema.class);
285-
boolean isNullable = isNullable(fieldAnnotations);
286-
boolean isNotRequired = !(isParentRequired && resolveRequired(schema, parameter, isNullable));
242+
boolean fieldRequired = SchemaUtils.fieldRequired(field, schema, parameter);
243+
244+
boolean paramRequired = parentRequired && fieldRequired;
287245
if (paramClass.getSuperclass() != null && paramClass.isRecord()) {
288246
return Stream.of(paramClass.getRecordComponents())
289247
.filter(d -> d.getName().equals(field.getName()))
290248
.map(RecordComponent::getAccessor)
291249
.map(method -> new MethodParameter(method, -1))
292250
.map(methodParameter -> DelegatingMethodParameter.changeContainingClass(methodParameter, paramClass))
293-
.map(param -> new DelegatingMethodParameter(param, fieldNamePrefix + field.getName(), fieldAnnotations, param.getMethodAnnotations(), true, isNotRequired));
251+
.map(param -> new DelegatingMethodParameter(param, fieldNamePrefix + field.getName(), fieldAnnotations, param.getMethodAnnotations(), true, field, !paramRequired));
294252
}
295253
else
296254
return Stream.of(Introspector.getBeanInfo(paramClass).getPropertyDescriptors())
@@ -299,15 +257,15 @@ private static Stream<MethodParameter> fromSimpleClass(Class<?> paramClass, Fiel
299257
.filter(Objects::nonNull)
300258
.map(method -> new MethodParameter(method, -1))
301259
.map(methodParameter -> DelegatingMethodParameter.changeContainingClass(methodParameter, paramClass))
302-
.map(param -> new DelegatingMethodParameter(param, fieldNamePrefix + field.getName(), fieldAnnotations, param.getMethodAnnotations(), true, isNotRequired));
260+
.map(param -> new DelegatingMethodParameter(param, fieldNamePrefix + field.getName(), fieldAnnotations, param.getMethodAnnotations(), true, field, !paramRequired));
303261
}
304262
catch (IntrospectionException e) {
305263
return Stream.of();
306264
}
307265
}
308266

309267
/**
310-
* All fields of list.
268+
* All fields of list. include parent fields
311269
*
312270
* @param clazz the clazz
313271
* @return the list
@@ -370,17 +328,5 @@ public static void removeSimpleTypes(Class<?>... classes) {
370328
SIMPLE_TYPES.removeAll(Arrays.asList(classes));
371329
}
372330

373-
/**
374-
* Is nullable boolean.
375-
*
376-
* @param fieldAnnotations the field annotations
377-
* @return the boolean
378-
*/
379-
private static boolean isNullable(Annotation[] fieldAnnotations) {
380-
Collection<String> annotationSimpleNames = Arrays.stream(fieldAnnotations)
381-
.map(Annotation::annotationType)
382-
.map(Class::getSimpleName)
383-
.collect(Collectors.toCollection(LinkedHashSet::new));
384-
return !hasNotNullAnnotation(annotationSimpleNames);
385-
}
331+
386332
}

0 commit comments

Comments
 (0)