50
50
import java .util .concurrent .atomic .AtomicInteger ;
51
51
import java .util .concurrent .atomic .AtomicLong ;
52
52
import java .util .function .Predicate ;
53
+ import java .util .stream .Collectors ;
53
54
import java .util .stream .Stream ;
54
55
55
56
import io .swagger .v3 .core .util .PrimitiveType ;
56
57
import io .swagger .v3 .oas .annotations .Parameter ;
58
+ import io .swagger .v3 .oas .annotations .media .Schema ;
59
+ import org .springdoc .core .service .AbstractRequestService ;
57
60
58
61
import org .springframework .core .GenericTypeResolver ;
59
62
import org .springframework .core .MethodParameter ;
63
66
/**
64
67
* The type Method parameter pojo extractor.
65
68
*
66
- * @author bnasslahsen
69
+ * @author bnasslahsen, michael.clarke
67
70
*/
68
71
public class MethodParameterPojoExtractor {
69
72
@@ -113,20 +116,21 @@ private MethodParameterPojoExtractor() {
113
116
* @return the stream
114
117
*/
115
118
static Stream <MethodParameter > extractFrom (Class <?> clazz ) {
116
- return extractFrom (clazz , "" );
119
+ return extractFrom (clazz , "" , true );
117
120
}
118
121
119
122
/**
120
123
* Extract from stream.
121
124
*
122
125
* @param clazz the clazz
123
126
* @param fieldNamePrefix the field name prefix
127
+ * @param parentRequired whether the field that hold the class currently being inspected was required or optional
124
128
* @return the stream
125
129
*/
126
- private static Stream <MethodParameter > extractFrom (Class <?> clazz , String fieldNamePrefix ) {
130
+ private static Stream <MethodParameter > extractFrom (Class <?> clazz , String fieldNamePrefix , boolean parentRequired ) {
127
131
return allFieldsOf (clazz ).stream ()
128
132
.filter (field -> !field .getType ().equals (clazz ))
129
- .flatMap (f -> fromGetterOfField (clazz , f , fieldNamePrefix ))
133
+ .flatMap (f -> fromGetterOfField (clazz , f , fieldNamePrefix , parentRequired ))
130
134
.filter (Objects ::nonNull );
131
135
}
132
136
@@ -136,20 +140,95 @@ private static Stream<MethodParameter> extractFrom(Class<?> clazz, String fieldN
136
140
* @param paramClass the param class
137
141
* @param field the field
138
142
* @param fieldNamePrefix the field name prefix
143
+ * @param parentRequired whether the field that holds the class currently being examined was required or optional
139
144
* @return the stream
140
145
*/
141
- private static Stream <MethodParameter > fromGetterOfField (Class <?> paramClass , Field field , String fieldNamePrefix ) {
146
+ private static Stream <MethodParameter > fromGetterOfField (Class <?> paramClass , Field field , String fieldNamePrefix , boolean parentRequired ) {
142
147
Class <?> type = extractType (paramClass , field );
143
148
144
149
if (Objects .isNull (type ))
145
150
return Stream .empty ();
146
151
147
152
if (isSimpleType (type ))
148
- return fromSimpleClass (paramClass , field , fieldNamePrefix );
153
+ return fromSimpleClass (paramClass , field , fieldNamePrefix , parentRequired );
149
154
else {
150
- String prefix = fieldNamePrefix + field .getName () + DOT ;
151
- return extractFrom (type , prefix );
155
+ Parameter parameter = field .getAnnotation (Parameter .class );
156
+ Schema schema = field .getAnnotation (Schema .class );
157
+ boolean visible = resolveVisible (parameter , schema );
158
+ if (!visible ) {
159
+ return Stream .empty ();
160
+ }
161
+ String prefix = fieldNamePrefix + resolveName (parameter , schema ).orElse (field .getName ()) + DOT ;
162
+ boolean notNullAnnotationsPresent = AbstractRequestService .hasNotNullAnnotation (Arrays .stream (field .getDeclaredAnnotations ())
163
+ .map (Annotation ::annotationType )
164
+ .map (Class ::getSimpleName )
165
+ .collect (Collectors .toSet ()));
166
+ return extractFrom (type , prefix , parentRequired && resolveRequired (schema , parameter , !notNullAnnotationsPresent ));
167
+ }
168
+ }
169
+
170
+ private static Optional <String > resolveName (Parameter parameter , Schema schema ) {
171
+ if (parameter != null ) {
172
+ return resolveNameFromParameter (parameter );
173
+ }
174
+ if (schema != null ) {
175
+ return resolveNameFromSchema (schema );
176
+ }
177
+ return Optional .empty ();
178
+ }
179
+
180
+ private static Optional <String > resolveNameFromParameter (Parameter parameter ) {
181
+ if (parameter .name ().isEmpty ()) {
182
+ return Optional .empty ();
183
+ }
184
+ return Optional .of (parameter .name ());
185
+ }
186
+
187
+ private static Optional <String > resolveNameFromSchema (Schema schema ) {
188
+ if (schema .name ().isEmpty ()) {
189
+ return Optional .empty ();
190
+ }
191
+ return Optional .of (schema .name ());
192
+ }
193
+
194
+ private static boolean resolveVisible (Parameter parameter , Schema schema ) {
195
+ if (parameter != null ) {
196
+ return !parameter .hidden ();
152
197
}
198
+ if (schema != null ) {
199
+ return !schema .hidden ();
200
+ }
201
+ return true ;
202
+ }
203
+
204
+ private static boolean resolveRequired (Schema schema , Parameter parameter , boolean nullable ) {
205
+ if (parameter != null ) {
206
+ return resolveRequiredFromParameter (parameter , nullable );
207
+ }
208
+ if (schema != null ) {
209
+ return resolveRequiredFromSchema (schema , nullable );
210
+ }
211
+ return !nullable ;
212
+ }
213
+
214
+ private static boolean resolveRequiredFromParameter (Parameter parameter , boolean nullable ) {
215
+ if (parameter .required ()) {
216
+ return true ;
217
+ }
218
+ return !nullable ;
219
+ }
220
+
221
+ private static boolean resolveRequiredFromSchema (Schema schema , boolean nullable ) {
222
+ if (schema .required ()) {
223
+ return true ;
224
+ }
225
+ else if (schema .requiredMode () == Schema .RequiredMode .REQUIRED ) {
226
+ return true ;
227
+ }
228
+ else if (schema .requiredMode () == Schema .RequiredMode .NOT_REQUIRED ) {
229
+ return false ;
230
+ }
231
+ return !nullable ;
153
232
}
154
233
155
234
/**
@@ -181,18 +260,30 @@ private static Class<?> extractType(Class<?> paramClass, Field field) {
181
260
* @param fieldNamePrefix the field name prefix
182
261
* @return the stream
183
262
*/
184
- private static Stream <MethodParameter > fromSimpleClass (Class <?> paramClass , Field field , String fieldNamePrefix ) {
263
+ private static Stream <MethodParameter > fromSimpleClass (Class <?> paramClass , Field field , String fieldNamePrefix , boolean isParentRequired ) {
185
264
Annotation [] fieldAnnotations = field .getDeclaredAnnotations ();
186
265
try {
187
266
Parameter parameter = field .getAnnotation (Parameter .class );
188
- boolean isNotRequired = parameter == null || !parameter .required ();
267
+ Schema schema = field .getAnnotation (Schema .class );
268
+ boolean visible = resolveVisible (parameter , schema );
269
+ if (!visible ) {
270
+ return Stream .empty ();
271
+ }
272
+
273
+ boolean isNotRequired = !(isParentRequired && resolveRequired (schema , parameter , !AbstractRequestService .hasNotNullAnnotation (Arrays .stream (fieldAnnotations )
274
+ .map (Annotation ::annotationType )
275
+ .map (Class ::getSimpleName )
276
+ .collect (Collectors .toSet ()))));
277
+ Annotation [] notNullFieldAnnotations = Arrays .stream (fieldAnnotations )
278
+ .filter (annotation -> AbstractRequestService .hasNotNullAnnotation (List .of (annotation .annotationType ().getSimpleName ())))
279
+ .toArray (Annotation []::new );
189
280
if (paramClass .getSuperclass () != null && paramClass .isRecord ()) {
190
281
return Stream .of (paramClass .getRecordComponents ())
191
282
.filter (d -> d .getName ().equals (field .getName ()))
192
283
.map (RecordComponent ::getAccessor )
193
284
.map (method -> new MethodParameter (method , -1 ))
194
285
.map (methodParameter -> DelegatingMethodParameter .changeContainingClass (methodParameter , paramClass ))
195
- .map (param -> new DelegatingMethodParameter (param , fieldNamePrefix + field .getName (), fieldAnnotations , param .getMethodAnnotations (), true , isNotRequired ));
286
+ .map (param -> new DelegatingMethodParameter (param , fieldNamePrefix + field .getName (), fieldAnnotations , param .getMethodAnnotations (), notNullFieldAnnotations , true , isNotRequired ));
196
287
197
288
}
198
289
else
@@ -202,7 +293,7 @@ private static Stream<MethodParameter> fromSimpleClass(Class<?> paramClass, Fiel
202
293
.filter (Objects ::nonNull )
203
294
.map (method -> new MethodParameter (method , -1 ))
204
295
.map (methodParameter -> DelegatingMethodParameter .changeContainingClass (methodParameter , paramClass ))
205
- .map (param -> new DelegatingMethodParameter (param , fieldNamePrefix + field .getName (), fieldAnnotations , param .getMethodAnnotations (), true , isNotRequired ));
296
+ .map (param -> new DelegatingMethodParameter (param , fieldNamePrefix + field .getName (), fieldAnnotations , param .getMethodAnnotations (), notNullFieldAnnotations , true , isNotRequired ));
206
297
}
207
298
catch (IntrospectionException e ) {
208
299
return Stream .of ();
0 commit comments