18
18
19
19
package org .springdoc .core ;
20
20
21
- import java .lang .annotation .Annotation ;
22
- import java .lang .reflect .Method ;
23
- import java .math .BigDecimal ;
24
- import java .util .ArrayList ;
25
- import java .util .Arrays ;
26
- import java .util .HashMap ;
27
- import java .util .LinkedHashMap ;
28
- import java .util .List ;
29
- import java .util .Map ;
30
- import java .util .Objects ;
31
- import java .util .Optional ;
32
- import java .util .Set ;
33
- import java .util .stream .Collectors ;
34
- import java .util .stream .Stream ;
35
-
36
- import javax .validation .constraints .DecimalMax ;
37
- import javax .validation .constraints .DecimalMin ;
38
- import javax .validation .constraints .Max ;
39
- import javax .validation .constraints .Min ;
40
- import javax .validation .constraints .NotNull ;
41
- import javax .validation .constraints .Pattern ;
42
- import javax .validation .constraints .Size ;
43
-
44
21
import com .fasterxml .jackson .annotation .JsonView ;
45
22
import io .swagger .v3 .oas .annotations .enums .ParameterIn ;
46
23
import io .swagger .v3 .oas .models .Components ;
53
30
import io .swagger .v3 .oas .models .parameters .Parameter ;
54
31
import io .swagger .v3 .oas .models .parameters .RequestBody ;
55
32
import org .apache .commons .lang3 .StringUtils ;
33
+ import org .springdoc .api .annotations .ParameterObject ;
56
34
import org .springdoc .core .customizers .OperationCustomizer ;
57
35
import org .springdoc .core .customizers .ParameterCustomizer ;
58
-
59
36
import org .springframework .core .LocalVariableTableParameterNameDiscoverer ;
60
37
import org .springframework .core .MethodParameter ;
61
38
import org .springframework .core .annotation .AnnotatedElementUtils ;
62
39
import org .springframework .http .HttpMethod ;
63
40
import org .springframework .util .CollectionUtils ;
64
41
import org .springframework .validation .BindingResult ;
65
42
import org .springframework .validation .Errors ;
66
- import org .springframework .web .bind .annotation .CookieValue ;
67
- import org .springframework .web .bind .annotation .PathVariable ;
68
- import org .springframework .web .bind .annotation .RequestAttribute ;
69
- import org .springframework .web .bind .annotation .RequestHeader ;
70
- import org .springframework .web .bind .annotation .RequestMethod ;
71
- import org .springframework .web .bind .annotation .RequestParam ;
72
- import org .springframework .web .bind .annotation .ValueConstants ;
43
+ import org .springframework .web .bind .annotation .*;
73
44
import org .springframework .web .bind .support .SessionStatus ;
74
45
import org .springframework .web .context .request .NativeWebRequest ;
75
46
import org .springframework .web .context .request .WebRequest ;
76
47
import org .springframework .web .method .HandlerMethod ;
77
48
import org .springframework .web .util .UriComponentsBuilder ;
78
49
50
+ import javax .validation .constraints .*;
51
+ import java .lang .annotation .Annotation ;
52
+ import java .lang .reflect .Method ;
53
+ import java .math .BigDecimal ;
54
+ import java .util .*;
55
+ import java .util .stream .Collectors ;
56
+ import java .util .stream .Stream ;
57
+
79
58
import static org .springdoc .core .Constants .OPENAPI_ARRAY_TYPE ;
80
59
import static org .springdoc .core .Constants .OPENAPI_STRING_TYPE ;
81
60
import static org .springdoc .core .Constants .QUERY_PARAM ;
@@ -160,42 +139,57 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
160
139
// requests
161
140
String [] pNames = this .localSpringDocParameterNameDiscoverer .getParameterNames (handlerMethod .getMethod ());
162
141
MethodParameter [] parameters = handlerMethod .getMethodParameters ();
163
- String [] reflectionParametersNames = Arrays .stream (parameters ).map (MethodParameter ::getParameterName ).toArray (String []::new );
164
- if (pNames == null )
165
- pNames = reflectionParametersNames ;
142
+
143
+ List <MethodParameter > explodedParameters = new ArrayList <>();
144
+ for (int i = 0 ; i < parameters .length ; ++i ) {
145
+ MethodParameter p = parameters [i ];
146
+ if (p .hasParameterAnnotation (ParameterObject .class )) {
147
+ Class <?> paramClass = p .getParameterType ();
148
+ Stream .of (paramClass .getDeclaredFields ())
149
+ .map (f -> DelegatingMethodParameter .fromGetterOfField (paramClass , f ))
150
+ .filter (Objects ::nonNull )
151
+ .forEach (explodedParameters ::add );
152
+ } else {
153
+ String name = pNames != null ? pNames [i ] : p .getParameterName ();
154
+ explodedParameters .add (new DelegatingMethodParameter (p , name , null ));
155
+ }
156
+ }
157
+ parameters = explodedParameters .toArray (new MethodParameter [0 ]);
158
+
166
159
RequestBodyInfo requestBodyInfo = new RequestBodyInfo ();
167
160
List <Parameter > operationParameters = (operation .getParameters () != null ) ? operation .getParameters () : new ArrayList <>();
168
161
Map <String , io .swagger .v3 .oas .annotations .Parameter > parametersDocMap = getApiParameters (handlerMethod .getMethod ());
169
162
Components components = openAPI .getComponents ();
170
163
171
- for (int i = 0 ; i < pNames . length ; i ++ ) {
164
+ for (MethodParameter methodParameter : parameters ) {
172
165
// check if query param
173
166
Parameter parameter = null ;
174
- final String pName = pNames [i ] == null ? reflectionParametersNames [i ] : pNames [i ];
175
- MethodParameter methodParameter = parameters [i ];
176
167
io .swagger .v3 .oas .annotations .Parameter parameterDoc = methodParameter .getParameterAnnotation (io .swagger .v3 .oas .annotations .Parameter .class );
177
- if (parameterDoc == null )
178
- parameterDoc = parametersDocMap .get (pName );
168
+ if (parameterDoc == null ) {
169
+ parameterDoc = parametersDocMap .get (methodParameter .getParameterName ());
170
+ }
179
171
// use documentation as reference
180
172
if (parameterDoc != null ) {
181
- if (parameterDoc .hidden ())
173
+ if (parameterDoc .hidden ()) {
182
174
continue ;
175
+ }
183
176
parameter = parameterBuilder .buildParameterFromDoc (parameterDoc , null ,
184
177
methodAttributes .getJsonViewAnnotation ());
185
178
}
186
179
187
180
if (!isParamToIgnore (methodParameter )) {
188
- ParameterInfo parameterInfo = new ParameterInfo (pName , methodParameter , parameter );
181
+ ParameterInfo parameterInfo = new ParameterInfo (methodParameter . getParameterName () , methodParameter , parameter );
189
182
parameter = buildParams (parameterInfo , components , requestMethod ,
190
183
methodAttributes .getJsonViewAnnotation ());
191
184
// Merge with the operation parameters
192
185
parameter = parameterBuilder .mergeParameter (operationParameters , parameter );
193
186
List <Annotation > parameterAnnotations = Arrays .asList (methodParameter .getParameterAnnotations ());
194
- if (isValidParameter (parameter ))
187
+ if (isValidParameter (parameter )) {
195
188
applyBeanValidatorAnnotations (parameter , parameterAnnotations );
196
- else if (!RequestMethod .GET .equals (requestMethod )) {
197
- if (operation .getRequestBody () != null )
189
+ } else if (!RequestMethod .GET .equals (requestMethod )) {
190
+ if (operation .getRequestBody () != null ) {
198
191
requestBodyInfo .setRequestBody (operation .getRequestBody ());
192
+ }
199
193
requestBodyBuilder .calculateRequestBodyInfo (components , methodAttributes ,
200
194
parameterInfo , requestBodyInfo );
201
195
applyBeanValidatorAnnotations (requestBodyInfo .getRequestBody (), parameterAnnotations );
@@ -205,7 +199,7 @@ else if (!RequestMethod.GET.equals(requestMethod)) {
205
199
}
206
200
207
201
LinkedHashMap <String , Parameter > map = getParameterLinkedHashMap (components , methodAttributes , operationParameters , parametersDocMap );
208
- setParams (operation , new ArrayList <Parameter >(map .values ()), requestBodyInfo );
202
+ setParams (operation , new ArrayList <>(map .values ()), requestBodyInfo );
209
203
// allow for customisation
210
204
return customiseOperation (operation , handlerMethod );
211
205
}
@@ -297,7 +291,7 @@ else if (pathVar != null) {
297
291
String name = StringUtils .isBlank (pathVar .value ()) ? pName : pathVar .value ();
298
292
parameterInfo .setpName (name );
299
293
// check if PATH PARAM
300
- requestInfo = new RequestInfo (ParameterIn .PATH .toString (), pathVar .value (), Boolean . TRUE , null );
294
+ requestInfo = new RequestInfo (ParameterIn .PATH .toString (), pathVar .value (), ! methodParameter . isOptional () , null );
301
295
parameter = buildParam (parameterInfo , components , requestInfo , jsonView );
302
296
}
303
297
else if (cookieValue != null ) {
@@ -307,7 +301,7 @@ else if (cookieValue != null) {
307
301
}
308
302
// By default
309
303
if (RequestMethod .GET .equals (requestMethod ) || (parameterInfo .getParameterModel () != null && ParameterIn .PATH .toString ().equals (parameterInfo .getParameterModel ().getIn ())))
310
- parameter = this .buildParam (QUERY_PARAM , components , parameterInfo , Boolean . TRUE , null , jsonView );
304
+ parameter = this .buildParam (QUERY_PARAM , components , parameterInfo , ! methodParameter . isOptional () , null , jsonView );
311
305
312
306
return parameter ;
313
307
}
0 commit comments