Skip to content

Commit cefd3e6

Browse files
author
Marcin Zarebski
committed
Extract nested parameter object fields (excluding collections)
1 parent 785f0be commit cefd3e6

File tree

4 files changed

+155
-54
lines changed

4 files changed

+155
-54
lines changed

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

Lines changed: 63 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -10,86 +10,65 @@
1010
import java.util.Collections;
1111
import java.util.HashSet;
1212
import java.util.List;
13+
import java.util.Map;
1314
import java.util.Objects;
15+
import java.util.Optional;
16+
import java.util.OptionalDouble;
17+
import java.util.OptionalInt;
18+
import java.util.OptionalLong;
1419
import java.util.Set;
1520
import java.util.stream.Stream;
1621

1722
import io.swagger.v3.oas.annotations.Parameter;
1823
import org.apache.commons.lang3.ArrayUtils;
1924

2025
import org.springframework.core.MethodParameter;
21-
import org.springframework.lang.NonNull;
2226
import org.springframework.lang.Nullable;
2327

2428
class MethodParameterPojoExtractor {
25-
private static final Set<Class<?>> SIMPLE_TYPES;
26-
private static final Set<Class<?>> COLLECTION_TYPES;
27-
28-
static {
29-
Set<Class<?>> simpleTypes = new HashSet<>();
30-
simpleTypes.add(boolean.class);
31-
simpleTypes.add(char.class);
32-
simpleTypes.add(byte.class);
33-
simpleTypes.add(short.class);
34-
simpleTypes.add(int.class);
35-
simpleTypes.add(long.class);
36-
simpleTypes.add(float.class);
37-
simpleTypes.add(double.class);
38-
39-
simpleTypes.add(Boolean.class);
40-
simpleTypes.add(Character.class);
41-
simpleTypes.add(Byte.class);
42-
simpleTypes.add(Short.class);
43-
simpleTypes.add(Integer.class);
44-
simpleTypes.add(Long.class);
45-
simpleTypes.add(Float.class);
46-
simpleTypes.add(Double.class);
47-
48-
simpleTypes.add(CharSequence.class);
49-
simpleTypes.add(Number.class);
50-
51-
SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
52-
53-
Set<Class<?>> collectionTypes = new HashSet<>();
54-
collectionTypes.add(Iterable.class);
55-
56-
COLLECTION_TYPES = Collections.unmodifiableSet(collectionTypes);
29+
static Stream<MethodParameter> extractFrom(Class<?> clazz) {
30+
return extractFrom(clazz, "");
5731
}
5832

59-
private static final Nullable NULLABLE_ANNOTATION = new Nullable() {
60-
@Override
61-
public Class<? extends Annotation> annotationType() {
62-
return Nullable.class;
63-
}
64-
};
65-
66-
@NonNull
67-
static Stream<MethodParameter> extractFrom(Class<?> clazz) {
33+
private static Stream<MethodParameter> extractFrom(Class<?> clazz, String fieldNamePrefix) {
6834
return allFieldsOf(clazz).stream()
69-
.flatMap(f -> fromGetterOfField(clazz, f))
35+
.flatMap(f -> fromGetterOfField(clazz, f, fieldNamePrefix))
7036
.filter(Objects::nonNull);
7137
}
7238

73-
private static Stream<MethodParameter> fromGetterOfField(Class<?> paramClass, Field field) {
39+
private static Stream<MethodParameter> fromGetterOfField(Class<?> paramClass, Field field, String fieldNamePrefix) {
40+
if (isSimpleType(field.getType())) {
41+
return fromSimpleClass(paramClass, field, fieldNamePrefix);
42+
}
43+
else {
44+
return extractFrom(field.getType(), fieldNamePrefix + field.getName() + ".");
45+
}
46+
}
47+
48+
private static Stream<MethodParameter> fromSimpleClass(Class<?> paramClass, Field field, String fieldNamePrefix) {
49+
Annotation[] fieldAnnotations = field.getDeclaredAnnotations();
50+
if (isOptional(field)) {
51+
fieldAnnotations = ArrayUtils.add(fieldAnnotations, NULLABLE_ANNOTATION);
52+
}
7453
try {
75-
Annotation[] filedAnnotations = field.getDeclaredAnnotations();
76-
Parameter parameter = field.getAnnotation(Parameter.class);
77-
if (parameter != null && !parameter.required()) {
78-
filedAnnotations = ArrayUtils.add(filedAnnotations, NULLABLE_ANNOTATION);
79-
}
80-
Annotation[] filedAnnotationsNew = filedAnnotations;
54+
Annotation[] finalFieldAnnotations = fieldAnnotations;
8155
return Stream.of(Introspector.getBeanInfo(paramClass).getPropertyDescriptors())
8256
.filter(d -> d.getName().equals(field.getName()))
8357
.map(PropertyDescriptor::getReadMethod)
8458
.filter(Objects::nonNull)
8559
.map(method -> new MethodParameter(method, -1))
86-
.map(param -> new DelegatingMethodParameter(param, field.getName(), filedAnnotationsNew));
60+
.map(param -> new DelegatingMethodParameter(param, fieldNamePrefix + field.getName(), finalFieldAnnotations));
8761
}
8862
catch (IntrospectionException e) {
8963
return Stream.of();
9064
}
9165
}
9266

67+
private static boolean isOptional(Field field) {
68+
Parameter parameter = field.getAnnotation(Parameter.class);
69+
return parameter == null || !parameter.required();
70+
}
71+
9372
private static List<Field> allFieldsOf(Class<?> clazz) {
9473
List<Field> fields = new ArrayList<>();
9574
do {
@@ -98,4 +77,37 @@ private static List<Field> allFieldsOf(Class<?> clazz) {
9877
} while (clazz != null);
9978
return fields;
10079
}
80+
81+
private static boolean isSimpleType(Class<?> clazz) {
82+
if (clazz.isPrimitive()) return true;
83+
if (clazz.isArray()) return true;
84+
if (clazz.isEnum()) return true;
85+
return SIMPLE_TYPES.stream().anyMatch(c -> c.isAssignableFrom(clazz));
86+
}
87+
88+
private static final Nullable NULLABLE_ANNOTATION = new Nullable() {
89+
@Override
90+
public Class<? extends Annotation> annotationType() {
91+
return Nullable.class;
92+
}
93+
};
94+
95+
private static final Set<Class<?>> SIMPLE_TYPES;
96+
97+
static {
98+
Set<Class<?>> simpleTypes = new HashSet<>();
99+
simpleTypes.add(Boolean.class);
100+
simpleTypes.add(Character.class);
101+
simpleTypes.add(Number.class);
102+
simpleTypes.add(CharSequence.class);
103+
simpleTypes.add(Optional.class);
104+
simpleTypes.add(OptionalInt.class);
105+
simpleTypes.add(OptionalLong.class);
106+
simpleTypes.add(OptionalDouble.class);
107+
108+
simpleTypes.add(Map.class);
109+
simpleTypes.add(Iterable.class);
110+
111+
SIMPLE_TYPES = Collections.unmodifiableSet(simpleTypes);
112+
}
101113
}

springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/InheritedRequestParams.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package test.org.springdoc.api.app102;
22

3+
import javax.validation.constraints.NotBlank;
4+
35
import io.swagger.v3.oas.annotations.Parameter;
46

57
public class InheritedRequestParams extends RequestParams {
68
@Parameter(description = "parameter from child of RequestParams")
9+
@NotBlank
710
private String childParam;
811

912
public String getChildParam() {

springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/RequestParams.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package test.org.springdoc.api.app102;
22

3+
import java.math.BigInteger;
4+
import java.util.List;
35
import java.util.Optional;
46

57
import io.swagger.v3.oas.annotations.Parameter;
@@ -8,6 +10,29 @@
810

911
public class RequestParams {
1012

13+
public static class Nested {
14+
private String param1;
15+
private BigInteger param2;
16+
17+
@Parameter(description = "nested string parameter")
18+
public String getParam1() {
19+
return param1;
20+
}
21+
22+
public void setParam1(String param1) {
23+
this.param1 = param1;
24+
}
25+
26+
@Parameter(description = "nested BigInteger parameter")
27+
public BigInteger getParam2() {
28+
return param2;
29+
}
30+
31+
public void setParam2(BigInteger param2) {
32+
this.param2 = param2;
33+
}
34+
}
35+
1136
@Parameter(description = "string parameter")
1237
private String stringParam;
1338

@@ -24,6 +49,10 @@ public class RequestParams {
2449
@Nullable
2550
private String intParam3;
2651

52+
private Nested nested;
53+
54+
private List<Nested> nestedList;
55+
2756
public String getStringParam() {
2857
return stringParam;
2958
}
@@ -72,4 +101,20 @@ public String getStringParam2() {
72101
public void setStringParam2(String stringParam2) {
73102
this.stringParam2 = stringParam2;
74103
}
104+
105+
public Nested getNested() {
106+
return nested;
107+
}
108+
109+
public void setNested(Nested nested) {
110+
this.nested = nested;
111+
}
112+
113+
public List<Nested> getNestedList() {
114+
return nestedList;
115+
}
116+
117+
public void setNestedList(List<Nested> nestedList) {
118+
this.nestedList = nestedList;
119+
}
75120
}

springdoc-openapi-webmvc-core/src/test/resources/results/app102.json

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"name": "childParam",
3131
"in": "query",
3232
"description": "parameter from child of RequestParams",
33-
"required": false,
33+
"required": true,
3434
"schema": {
3535
"type": "string"
3636
}
@@ -47,7 +47,7 @@
4747
{
4848
"name": "stringParam1",
4949
"in": "query",
50-
"required": true,
50+
"required": false,
5151
"schema": {
5252
"type": "string"
5353
}
@@ -86,6 +86,33 @@
8686
"schema": {
8787
"type": "string"
8888
}
89+
},
90+
{
91+
"name": "nested.param1",
92+
"in": "query",
93+
"required": false,
94+
"schema": {
95+
"type": "string"
96+
}
97+
},
98+
{
99+
"name": "nested.param2",
100+
"in": "query",
101+
"required": false,
102+
"schema": {
103+
"type": "integer"
104+
}
105+
},
106+
{
107+
"name": "nestedList",
108+
"in": "query",
109+
"required": false,
110+
"schema": {
111+
"type": "array",
112+
"items": {
113+
"$ref": "#/components/schemas/Nested"
114+
}
115+
}
89116
}
90117
],
91118
"responses": {
@@ -96,5 +123,19 @@
96123
}
97124
}
98125
},
99-
"components": {}
126+
"components": {
127+
"schemas": {
128+
"Nested": {
129+
"type": "object",
130+
"properties": {
131+
"param1": {
132+
"type": "string"
133+
},
134+
"param2": {
135+
"type": "integer"
136+
}
137+
}
138+
}
139+
}
140+
}
100141
}

0 commit comments

Comments
 (0)