/
Docket.java
580 lines (528 loc) · 20.7 KB
/
Docket.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
/*
*
* Copyright 2015-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
*/
package springfox.documentation.spring.web.plugins;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import org.springframework.http.HttpMethod;
import org.springframework.web.bind.annotation.RequestMethod;
import springfox.documentation.PathProvider;
import springfox.documentation.annotations.Incubating;
import springfox.documentation.schema.AlternateTypeRule;
import springfox.documentation.schema.CodeGenGenericTypeNamingStrategy;
import springfox.documentation.schema.DefaultGenericTypeNamingStrategy;
import springfox.documentation.schema.WildcardType;
import springfox.documentation.service.ApiDescription;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiListingReference;
import springfox.documentation.service.Operation;
import springfox.documentation.service.RequestParameter;
import springfox.documentation.service.Response;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.service.Server;
import springfox.documentation.service.Tag;
import springfox.documentation.service.VendorExtension;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.GenericTypeNamingStrategy;
import springfox.documentation.spi.service.DocumentationPlugin;
import springfox.documentation.spi.service.contexts.ApiSelector;
import springfox.documentation.spi.service.contexts.DocumentationContext;
import springfox.documentation.spi.service.contexts.DocumentationContextBuilder;
import springfox.documentation.spi.service.contexts.SecurityContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import static java.util.Optional.*;
import static java.util.stream.Collectors.*;
import static springfox.documentation.builders.BuilderDefaults.*;
import static springfox.documentation.schema.AlternateTypeRules.*;
/**
* A builder which is intended to be the primary interface into the Springfox framework.
* Provides sensible defaults and convenience methods for configuration.
*/
@SuppressWarnings("deprecation")
public class Docket implements DocumentationPlugin {
public static final String DEFAULT_GROUP_NAME = "default";
private final DocumentationType documentationType;
private final List<SecurityContext> securityContexts = new ArrayList<>();
private final Map<RequestMethod, List<springfox.documentation.service.ResponseMessage>> responseMessages
= new HashMap<>();
private final Map<HttpMethod, List<Response>> responses = new HashMap<>();
private final List<springfox.documentation.service.Parameter> globalOperationParameters = new ArrayList<>();
private final List<Function<TypeResolver, AlternateTypeRule>> ruleBuilders = new ArrayList<>();
private final Set<Class> ignorableParameterTypes = new HashSet<>();
private final Set<String> protocols = new HashSet<>();
private final Set<String> produces = new LinkedHashSet<>();
private final Set<String> consumes = new LinkedHashSet<>();
private final Set<ResolvedType> additionalModels = new HashSet<>();
private final Set<Tag> tags = new HashSet<>();
private final List<Server> servers = new ArrayList<>();
private PathProvider pathProvider;
private List<SecurityScheme> securitySchemes;
private Comparator<ApiListingReference> apiListingReferenceOrdering;
private Comparator<ApiDescription> apiDescriptionOrdering;
private Comparator<Operation> operationOrdering;
private ApiInfo apiInfo = ApiInfo.DEFAULT;
private String groupName = DEFAULT_GROUP_NAME;
private boolean enabled = true;
private GenericTypeNamingStrategy genericsNamingStrategy = new DefaultGenericTypeNamingStrategy();
private boolean applyDefaultResponseMessages = true;
private String host = "";
private Optional<String> pathMapping = empty();
private ApiSelector apiSelector = ApiSelector.DEFAULT;
private boolean enableUrlTemplating = false;
private final List<VendorExtension> vendorExtensions = new ArrayList<>();
private final List<RequestParameter> globalRequestParameters = new ArrayList<>();
public Docket(DocumentationType documentationType) {
this.documentationType = documentationType;
}
/**
* Add to the api's vendor extensions
*
* @param vendorExtensions Indicates the vendor extension information
* @return this Docket
*/
public Docket extensions(List<VendorExtension> vendorExtensions) {
this.vendorExtensions.addAll(vendorExtensions);
return this;
}
/**
* Sets the api's meta information as included in the json ResourceListing response.
*
* @param apiInfo Indicates the api information
* @return this Docket
*/
public Docket apiInfo(ApiInfo apiInfo) {
this.apiInfo = defaultIfAbsent(apiInfo, this.apiInfo);
return this;
}
/**
* Configures the global io.swagger.model.SecurityScheme's applicable to all or some of the api
* operations. The configuration of which operations have associated SecuritySchemes is configured with
* springfox.swagger.plugins.Docket#securityContexts
*
* @param securitySchemes a list of security schemes
* @return this Docket
*/
public Docket securitySchemes(List<SecurityScheme> securitySchemes) {
this.securitySchemes = securitySchemes;
return this;
}
/**
* Configures which api operations (via regex patterns) and HTTP methods to apply security contexts to apis.
*
* @param securityContexts - defines security requirements for the apis
* @return this Docket
*/
public Docket securityContexts(List<SecurityContext> securityContexts) {
this.securityContexts.addAll(securityContexts);
return this;
}
/**
* If more than one instance of Docket exists, each one must have a unique groupName as
* supplied by this method. Defaults to "default".
*
* @param groupName - the unique identifier of this swagger group/configuration
* @return this Docket
*/
public Docket groupName(String groupName) {
this.groupName = defaultIfAbsent(groupName, this.groupName);
return this;
}
/**
* Determines the generated, swagger specific, urls.
* <p>
* By default, relative urls are generated. If absolute urls are required, supply an implementation of
* AbsoluteSwaggerPathProvider
*
* @param pathProvider - provides an alternate implementation of path provider
* @return this Docket
* @see springfox.documentation.spring.web.paths.DefaultPathProvider
*/
public Docket pathProvider(PathProvider pathProvider) {
this.pathProvider = pathProvider;
return this;
}
/**
* Overrides the default http response messages at the http request method level.
* <p>
* To set specific response messages for specific api operations use the swagger core annotations on
* the appropriate controller methods.
*
* @param requestMethod - http request method for which to apply the message
* @param responseMessages - the message
* @return this Docket
* {@code See swagger annotations <code>@ApiResponse</code>, <code>@ApiResponses</code> }.
* @see springfox.documentation.spi.service.contexts.Defaults#defaultResponseMessages()
* Use {@link Docket#responses} instead
* @deprecated @since 3.0.0
*/
@Deprecated
public Docket globalResponseMessage(
RequestMethod requestMethod,
List<springfox.documentation.service.ResponseMessage> responseMessages) {
this.responseMessages.put(requestMethod, responseMessages);
return this;
}
/**
* Overrides the default http response messages at the http request method level.
* <p>
* To set specific response messages for specific api operations use the swagger core annotations on
* the appropriate controller methods.
*
* @param httpMethod - http request method for which to apply the message
* @param responses - the message
* @return this Docket
* {@code See swagger annotations <code>@ApiResponse</code>, <code>@ApiResponses</code> }.
* @see springfox.documentation.spi.service.contexts.Defaults#defaultResponseMessages()
*/
//TODO: Fix this builder
public Docket globalResponses(
HttpMethod httpMethod,
List<Response> responses) {
this.responses.put(httpMethod, responses);
return this;
}
/**
* Adds default parameters which will be applied to all operations.
*
* @param operationParameters parameters which will be globally applied to all operations
* @return this Docket use @see {@link Docket#globalRequestParameters} instead
* @deprecated
*/
@Deprecated
public Docket globalOperationParameters(List<springfox.documentation.service.Parameter> operationParameters) {
this.globalOperationParameters.addAll(nullToEmptyList(operationParameters));
return this;
}
/**
* Adds default parameters which will be applied to all operations.
*
* @param globalRequestParameters parameters which will be globally applied to all operations
* @return this Docket
*/
//TODO: Make this fluent
public Docket globalRequestParameters(List<RequestParameter> globalRequestParameters) {
this.globalRequestParameters.addAll(nullToEmptyList(globalRequestParameters));
return this;
}
/**
* Adds ignored controller method parameter types so that the framework does not generate swagger model or parameter
* information for these specific types.
* e.g. HttpServletRequest/HttpServletResponse which are already included in the pre-configured ignored types.
*
* @param classes the classes to ignore
* @return this Docket
* @see springfox.documentation.spi.service.contexts.Defaults#defaultIgnorableParameterTypes()
*/
public Docket ignoredParameterTypes(Class... classes) {
this.ignorableParameterTypes.addAll(Arrays.asList(classes));
return this;
}
public Docket produces(Set<String> produces) {
this.produces.addAll(produces);
return this;
}
public Docket consumes(Set<String> consumes) {
this.consumes.addAll(consumes);
return this;
}
@Incubating("2.3")
public Docket host(String host) {
this.host = defaultIfAbsent(host, this.host);
return this;
}
public Docket protocols(Set<String> protocols) {
this.protocols.addAll(protocols);
return this;
}
/**
* Adds model substitution rules (alternateTypeRules)
*
* @param alternateTypeRules - rules to be applied
* @return this Docket
* @see springfox.documentation.schema.AlternateTypeRules#newRule(java.lang.reflect.Type,
* java.lang.reflect.Type)
*/
public Docket alternateTypeRules(AlternateTypeRule... alternateTypeRules) {
this.ruleBuilders.addAll(Stream.of(alternateTypeRules).map(identityRuleBuilder()).collect(toList()));
return this;
}
/**
* Provide an ordering schema for operations
* <p>
* NOTE: @see <a href="https://github.com/springfox/springfox/issues/732">#732</a> in case you're wondering why
* specifying position might not work.
*
* @param operationOrdering - ordering of the operations
* @return this Docket
*/
public Docket operationOrdering(Comparator<Operation> operationOrdering) {
this.operationOrdering = operationOrdering;
return this;
}
/**
* Directly substitutes a model class with the supplied substitute
* e.g
* <code>directModelSubstitute(LocalDate.class, Date.class)</code>
* would substitute LocalDate with Date
*
* @param clazz class to substitute
* @param with the class which substitutes 'clazz'
* @return this Docket
*/
public Docket directModelSubstitute(final Class clazz, final Class with) {
this.ruleBuilders.add(newSubstitutionFunction(clazz, with));
return this;
}
/**
* Substitutes each generic class with it's direct parameterized type. Use this method to
* only for types with a single parameterized type. e.g. <code>List<T> or ResponseEntity<T></code>
* <code>.genericModelSubstitutes(ResponseEntity.class)</code>
* would substitute ResponseEntity <MyModel> with MyModel
*
* @param genericClasses - generic classes on which to apply generic model substitution.
* @return this Docket
*/
public Docket genericModelSubstitutes(Class... genericClasses) {
for (Class clz : genericClasses) {
this.ruleBuilders.add(newGenericSubstitutionFunction(clz));
}
return this;
}
/**
* Allows ignoring predefined response message defaults
*
* @param apply flag to determine if the default response messages are used
* true - the default response messages are added to the global response messages
* false - the default response messages are not added to the global response messages
* @return this Docket
*/
public Docket useDefaultResponseMessages(boolean apply) {
this.applyDefaultResponseMessages = apply;
return this;
}
/**
* Controls how ApiListingReference's are sorted.
* i.e the ordering of the api's within the swagger Resource Listing.
* The default sort is Lexicographically by the ApiListingReference's path
* <p>
* NOTE: @see <a href="https://github.com/springfox/springfox/issues/732">#732</a> in case you're wondering why
* specifying position might not work.
*
* @param apiListingReferenceOrdering - ordering of the api listing references
* @return this Docket
*/
public Docket apiListingReferenceOrdering(Comparator<ApiListingReference> apiListingReferenceOrdering) {
this.apiListingReferenceOrdering = apiListingReferenceOrdering;
return this;
}
/**
* Controls how <code>io.swagger.model.ApiDescription</code>'s are ordered.
* The default sort is Lexicographically by the ApiDescription's path.
* <p>
* NOTE: @see <a href="https://github.com/springfox/springfox/issues/732">#732</a> in case you're wondering why
* specifying position might not work.
*
* @param apiDescriptionOrdering - ordering of the api descriptions
* @return this Docket
* @see springfox.documentation.spring.web.scanners.ApiListingScanner
*/
public Docket apiDescriptionOrdering(Comparator<ApiDescription> apiDescriptionOrdering) {
this.apiDescriptionOrdering = apiDescriptionOrdering;
return this;
}
/**
* Hook to externally control auto initialization of this swagger plugin instance.
* Typically used if defer initialization.
*
* @param externallyConfiguredFlag - true to turn it on, false to turn it off
* @return this Docket
*/
public Docket enable(boolean externallyConfiguredFlag) {
this.enabled = externallyConfiguredFlag;
return this;
}
/**
* Set this to true in order to make the documentation code generation friendly
*
* @param forCodeGen - true|false determines the naming strategy used
* @return this Docket
*/
public Docket forCodeGeneration(boolean forCodeGen) {
if (forCodeGen) {
genericsNamingStrategy = new CodeGenGenericTypeNamingStrategy();
}
return this;
}
/**
* Extensibility mechanism to add a servlet path mapping, if there is one, to the apis base path.
*
* @param path - path that acts as a prefix to the api base path
* @return this Docket
*/
public Docket pathMapping(String path) {
this.pathMapping = ofNullable(path);
return this;
}
/**
* Decides whether to use url templating for paths. This is especially useful when you have search api's that
* might have multiple request mappings for each search use case.
* <p>
* This is an incubating feature that may not continue to be supported after the swagger specification is modified
* to accommodate the use case as described in issue #711
*
* @param enabled - when true it enables rfc6570 url templates
* @return this Docket
*/
@Incubating("2.1.0")
public Docket enableUrlTemplating(boolean enabled) {
this.enableUrlTemplating = enabled;
return this;
}
/**
* Method to add additional models that are not part of any annotation or are perhaps implicit
*
* @param first - at least one is required
* @param remaining - possible collection of more
* @return on-going docket
* @since 2.4.0
*/
public Docket additionalModels(ResolvedType first, ResolvedType... remaining) {
additionalModels.add(first);
additionalModels.addAll(Arrays.stream(remaining).collect(toSet()));
return this;
}
/**
* Method to add global tags to the docket
*
* @param first - at least one tag is required to use this method
* @param remaining - remaining tags
* @return this Docket
*/
public Docket tags(Tag first, Tag... remaining) {
tags.add(first);
tags.addAll(Arrays.stream(remaining).collect(toSet()));
return this;
}
/**
* Method to add global tags to the docket
*
* @param first - at least one tag is required to use this method
* @param remaining - remaining tags
* @return this Docket
*/
public Docket servers(Server first, Server... remaining) {
servers.add(first);
servers.addAll(Arrays.stream(nullToEmptyArray(remaining)).collect(toSet()));
return this;
}
/**
* Initiates a builder for api selection.
*
* @return api selection builder. To complete building the api selector, the build method of the api selector
* needs to be called, this will automatically fall back to building the docket when the build method is called.
*/
public ApiSelectorBuilder select() {
return new ApiSelectorBuilder(this);
}
/**
* Builds the Docket by merging/overlaying user specified values.
* It is not necessary to call this method when defined as a spring bean.
* NOTE: Calling this method more than once has no effect.
*
* @see DocumentationPluginsBootstrapper
*/
public DocumentationContext configure(DocumentationContextBuilder builder) {
return builder
.apiInfo(apiInfo)
.selector(apiSelector)
.applyDefaultResponseMessages(applyDefaultResponseMessages)
.additionalResponseMessages(responseMessages)
.additionalResponses(responses)
.additionalOperationParameters(globalOperationParameters)
.additionalRequestParameters(globalRequestParameters)
.additionalIgnorableTypes(ignorableParameterTypes)
.ruleBuilders(ruleBuilders)
.groupName(groupName)
.pathProvider(pathProvider)
.securityContexts(securityContexts)
.securitySchemes(securitySchemes)
.apiListingReferenceOrdering(apiListingReferenceOrdering)
.apiDescriptionOrdering(apiDescriptionOrdering)
.operationOrdering(operationOrdering)
.produces(produces)
.consumes(consumes)
.host(host)
.protocols(protocols)
.genericsNaming(genericsNamingStrategy)
.pathMapping(pathMapping)
.enableUrlTemplating(enableUrlTemplating)
.additionalModels(additionalModels)
.tags(tags)
.vendorExtentions(vendorExtensions)
.servers(servers)
.build();
}
@Override
public String getGroupName() {
return groupName;
}
public boolean isEnabled() {
return enabled;
}
@Override
public DocumentationType getDocumentationType() {
return documentationType;
}
@Override
public boolean supports(DocumentationType delimiter) {
return documentationType.equals(delimiter);
}
private Function<AlternateTypeRule, Function<TypeResolver, AlternateTypeRule>> identityRuleBuilder() {
return this::identityFunction;
}
private Function<TypeResolver, AlternateTypeRule> identityFunction(final AlternateTypeRule rule) {
return typeResolver -> rule;
}
Docket selector(ApiSelector apiSelector) {
this.apiSelector = apiSelector;
return this;
}
private Function<TypeResolver, AlternateTypeRule> newSubstitutionFunction(final Class clazz, final Class with) {
return typeResolver -> newRule(
typeResolver.resolve(clazz),
typeResolver.resolve(with),
DIRECT_SUBSTITUTION_RULE_ORDER);
}
private Function<TypeResolver, AlternateTypeRule> newGenericSubstitutionFunction(final Class clz) {
return typeResolver -> newRule(
typeResolver.resolve(clz, WildcardType.class),
typeResolver.resolve(WildcardType.class),
GENERIC_SUBSTITUTION_RULE_ORDER);
}
}