Skip to content

Commit 0293459

Browse files
committed
Spring Framework 7 - API versioning support #2975
1 parent 6e8a280 commit 0293459

File tree

133 files changed

+4098
-101
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

133 files changed

+4098
-101
lines changed

pom.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<parent>
1212
<groupId>org.springframework.boot</groupId>
1313
<artifactId>spring-boot-starter-parent</artifactId>
14-
<version>4.0.0-RC1</version>
14+
<version>4.0.0-SNAPSHOT</version>
1515
</parent>
1616

1717
<licenses>
@@ -65,6 +65,7 @@
6565
</spring-security-oauth2-authorization-server.version>
6666
<scalar.version>0.3.12</scalar.version>
6767
<skipPublishing>false</skipPublishing>
68+
<spring-framework.version>7.0.0-SNAPSHOT</spring-framework.version>
6869
</properties>
6970

7071
<dependencyManagement>
@@ -248,4 +249,15 @@
248249
</build>
249250
</profile>
250251
</profiles>
252+
253+
<repositories>
254+
<repository>
255+
<id>spring-snapshots</id>
256+
<name>Spring Snapshots</name>
257+
<url>https://repo.spring.io/snapshot</url>
258+
<snapshots>
259+
<enabled>true</enabled>
260+
</snapshots>
261+
</repository>
262+
</repositories>
251263
</project>

springdoc-openapi-starter-common/src/main/java/org/springdoc/api/AbstractOpenApiResource.java

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343
import java.util.LinkedHashMap;
4444
import java.util.List;
4545
import java.util.Locale;
46+
import java.util.Locale.LanguageRange;
4647
import java.util.Map;
48+
import java.util.Map.Entry;
4749
import java.util.Objects;
4850
import java.util.Optional;
4951
import java.util.Set;
@@ -111,6 +113,7 @@
111113
import org.springdoc.core.utils.PropertyResolverUtils;
112114
import org.springdoc.core.utils.SpringDocAnnotationsUtils;
113115
import org.springdoc.core.utils.SpringDocUtils;
116+
import org.springdoc.core.versions.SpringDocVersionStrategy;
114117

115118
import org.springframework.aop.support.AopUtils;
116119
import org.springframework.beans.factory.ObjectFactory;
@@ -369,7 +372,7 @@ protected OpenAPI getOpenApi(String serverBaseUrl, Locale locale) {
369372
.filter(controller -> (AnnotationUtils.findAnnotation(controller.getValue().getClass(),
370373
Hidden.class) == null))
371374
.filter(controller -> !isHiddenRestControllers(controller.getValue().getClass()))
372-
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a1, a2) -> a1));
375+
.collect(Collectors.toMap(Entry::getKey, Entry::getValue, (a1, a2) -> a1));
373376

374377
Map<String, Object> findControllerAdvice = openAPIService.getControllerAdviceMap();
375378
if (OpenApiVersion.OPENAPI_3_1 == springDocConfigProperties.getApiDocs().getVersion()) {
@@ -434,7 +437,7 @@ private Locale selectLocale(Locale inputLocale) {
434437
List<String> allowedLocales = springDocConfigProperties.getAllowedLocales();
435438
if (!CollectionUtils.isEmpty(allowedLocales)) {
436439
Locale bestMatchingAllowedLocale = Locale.lookup(
437-
Locale.LanguageRange.parse(inputLocale.toLanguageTag()),
440+
LanguageRange.parse(inputLocale.toLanguageTag()),
438441
allowedLocales.stream().map(Locale::forLanguageTag).toList()
439442
);
440443

@@ -568,7 +571,9 @@ protected void calculatePath(HandlerMethod handlerMethod, RouterOperation router
568571
String[] methodProduces = routerOperation.getProduces();
569572
String[] headers = routerOperation.getHeaders();
570573
Map<String, String> queryParams = routerOperation.getQueryParams();
571-
574+
SpringDocVersionStrategy springDocVersionStrategy = routerOperation.getSpringDocVersionStrategy();
575+
if (springDocVersionStrategy != null)
576+
queryParams = springDocVersionStrategy.updateQueryParams(queryParams);
572577
Components components = openAPI.getComponents();
573578
Paths paths = openAPI.getPaths();
574579

@@ -590,7 +595,7 @@ protected void calculatePath(HandlerMethod handlerMethod, RouterOperation router
590595
RequestMapping reqMappingClass = AnnotatedElementUtils.findMergedAnnotation(handlerMethod.getBeanType(),
591596
RequestMapping.class);
592597

593-
MethodAttributes methodAttributes = new MethodAttributes(springDocConfigProperties.getDefaultConsumesMediaType(), springDocConfigProperties.getDefaultProducesMediaType(), methodConsumes, methodProduces, headers, locale);
598+
MethodAttributes methodAttributes = new MethodAttributes(springDocConfigProperties.getDefaultConsumesMediaType(), springDocConfigProperties.getDefaultProducesMediaType(), methodConsumes, methodProduces, headers, springDocVersionStrategy, locale);
594599
methodAttributes.setMethodOverloaded(existingOperation != null);
595600
//Use the javadoc return if present
596601
if (javadocProvider != null) {
@@ -655,7 +660,7 @@ protected void calculatePath(HandlerMethod handlerMethod, RouterOperation router
655660
}
656661
}
657662

658-
Set<io.swagger.v3.oas.annotations.callbacks.Callback> apiCallbacks = AnnotatedElementUtils.findMergedRepeatableAnnotations(method, io.swagger.v3.oas.annotations.callbacks.Callback.class);
663+
Set<Callback> apiCallbacks = AnnotatedElementUtils.findMergedRepeatableAnnotations(method, Callback.class);
659664

660665
// callbacks
661666
buildCallbacks(openAPI, methodAttributes, operation, apiCallbacks);
@@ -757,7 +762,9 @@ protected void calculatePath(RouterOperation routerOperation, Locale locale, Ope
757762
String[] methodProduces = routerOperation.getProduces();
758763
String[] headers = routerOperation.getHeaders();
759764
Map<String, String> queryParams = routerOperation.getQueryParams();
760-
765+
SpringDocVersionStrategy springDocVersionStrategy = routerOperation.getSpringDocVersionStrategy();
766+
if(springDocVersionStrategy != null)
767+
queryParams = springDocVersionStrategy.updateQueryParams(queryParams);
761768
Paths paths = openAPI.getPaths();
762769
Map<HttpMethod, Operation> operationMap = null;
763770
if (paths.containsKey(operationPath)) {
@@ -766,7 +773,7 @@ protected void calculatePath(RouterOperation routerOperation, Locale locale, Ope
766773
}
767774
for (RequestMethod requestMethod : routerOperation.getMethods()) {
768775
Operation existingOperation = getExistingOperation(operationMap, requestMethod);
769-
MethodAttributes methodAttributes = new MethodAttributes(springDocConfigProperties.getDefaultConsumesMediaType(), springDocConfigProperties.getDefaultProducesMediaType(), methodConsumes, methodProduces, headers, locale);
776+
MethodAttributes methodAttributes = new MethodAttributes(springDocConfigProperties.getDefaultConsumesMediaType(), springDocConfigProperties.getDefaultProducesMediaType(), methodConsumes, methodProduces, headers, springDocVersionStrategy, locale);
770777
methodAttributes.setMethodOverloaded(existingOperation != null);
771778
Operation operation = getOperation(routerOperation, existingOperation);
772779
if (apiOperation != null)
@@ -812,19 +819,20 @@ private RouterOperation customizeDataRestRouterOperation(RouterOperation routerO
812819
/**
813820
* Calculate path.
814821
*
815-
* @param handlerMethod the handler method
816-
* @param operationPath the operation path
817-
* @param requestMethods the request methods
818-
* @param consumes the consumes
819-
* @param produces the produces
820-
* @param headers the headers
821-
* @param params the params
822-
* @param locale the locale
823-
* @param openAPI the open api
822+
* @param handlerMethod the handler method
823+
* @param operationPath the operation path
824+
* @param requestMethods the request methods
825+
* @param consumes the consumes
826+
* @param produces the produces
827+
* @param headers the headers
828+
* @param params the params
829+
* @param versionStrategy the version strategy
830+
* @param locale the locale
831+
* @param openAPI the open api
824832
*/
825833
protected void calculatePath(HandlerMethod handlerMethod, String operationPath,
826-
Set<RequestMethod> requestMethods, String[] consumes, String[] produces, String[] headers, String[] params, Locale locale, OpenAPI openAPI) {
827-
this.calculatePath(handlerMethod, new RouterOperation(operationPath, requestMethods.toArray(new RequestMethod[requestMethods.size()]), consumes, produces, headers, params), locale, openAPI);
834+
Set<RequestMethod> requestMethods, String[] consumes, String[] produces, String[] headers, String[] params, SpringDocVersionStrategy versionStrategy, Locale locale, OpenAPI openAPI) {
835+
this.calculatePath(handlerMethod, new RouterOperation(operationPath, requestMethods.toArray(new RequestMethod[requestMethods.size()]), consumes, produces, headers, params, versionStrategy), locale, openAPI);
828836
}
829837

830838
/**
@@ -1227,10 +1235,16 @@ private void fillParametersList(Operation operation, Map<String, String> queryPa
12271235
}
12281236
});
12291237
if (!CollectionUtils.isEmpty(queryParams)) {
1230-
for (Map.Entry<String, String> entry : queryParams.entrySet()) {
1231-
io.swagger.v3.oas.models.parameters.Parameter parameter = new io.swagger.v3.oas.models.parameters.Parameter();
1232-
parameter.setName(entry.getKey());
1233-
parameter.setSchema(new StringSchema()._default(entry.getValue()));
1238+
Map<String, String> versionDefaultMap = null;
1239+
if(methodAttributes.getSpringDocVersionStrategy() != null)
1240+
versionDefaultMap= methodAttributes.getSpringDocVersionStrategy().getVersionDefaultMap();
1241+
for (Entry<String, String> entry : queryParams.entrySet()) {
1242+
Parameter parameter = new Parameter();
1243+
String name = entry.getKey();
1244+
String value = entry.getValue();
1245+
String defaultValue = (versionDefaultMap != null) ? versionDefaultMap.get(name) : value;
1246+
parameter.setName(name);
1247+
parameter.setSchema(new StringSchema()._default(defaultValue)._enum(Collections.singletonList(value)));
12341248
parameter.setRequired(true);
12351249
parameter.setIn(ParameterIn.QUERY.toString());
12361250
GenericParameterService.mergeParameter(parametersList, parameter);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@
5454

5555
import org.springframework.core.MethodParameter;
5656
import org.springframework.core.ResolvableType;
57+
import org.springframework.data.core.TypeInformation;
5758
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
5859
import org.springframework.data.querydsl.binding.QuerydslBindings;
5960
import org.springframework.data.querydsl.binding.QuerydslBindingsFactory;
6061
import org.springframework.data.querydsl.binding.QuerydslPredicate;
61-
import org.springframework.data.util.TypeInformation;
6262
import org.springframework.util.CollectionUtils;
6363
import org.springframework.web.method.HandlerMethod;
6464

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ else if (ControllerType.PROPERTY.equals(controllerType))
285285
MethodResourceMapping methodResourceMapping, HandlerMethod handlerMethod,
286286
RequestMethod requestMethod, ResourceMetadata resourceMetadata, String
287287
operationPath, ControllerType controllerType) {
288-
RouterOperation routerOperation = new RouterOperation(operationPath, new RequestMethod[] { requestMethod }, null, null, null, null);
288+
RouterOperation routerOperation = new RouterOperation(operationPath, new RequestMethod[] { requestMethod }, null, null, null, null,null);
289289
MethodAttributes methodAttributes = new MethodAttributes(springDocConfigProperties.getDefaultConsumesMediaType(), springDocConfigProperties.getDefaultProducesMediaType(), dataRestRepository.getLocale());
290290
methodAttributes.calculateConsumesProduces(handlerMethod.getMethod());
291291
routerOperation.setConsumes(methodAttributes.getMethodConsumes());

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/fn/RouterOperation.java

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.apache.commons.lang3.ArrayUtils;
3636
import org.apache.commons.lang3.StringUtils;
3737
import org.springdoc.core.fn.builders.operation.Builder;
38+
import org.springdoc.core.versions.SpringDocVersionStrategy;
3839

3940
import org.springframework.web.bind.annotation.RequestMethod;
4041

@@ -107,6 +108,11 @@ public class RouterOperation implements Comparable<RouterOperation> {
107108
*/
108109
private io.swagger.v3.oas.models.Operation operationModel;
109110

111+
/**
112+
* The Spring doc version strategy.
113+
*/
114+
private SpringDocVersionStrategy springDocVersionStrategy;
115+
110116
/**
111117
* Instantiates a new Router operation.
112118
*/
@@ -154,20 +160,22 @@ public RouterOperation(org.springdoc.core.annotations.RouterOperation routerOper
154160
/**
155161
* Instantiates a new Router operation.
156162
*
157-
* @param path the path
158-
* @param methods the methods
159-
* @param consumes the consumes
160-
* @param produces the produces
161-
* @param headers the headers
162-
* @param params the params
163-
*/
164-
public RouterOperation(String path, RequestMethod[] methods, String[] consumes, String[] produces, String[] headers, String[] params) {
163+
* @param path the path
164+
* @param methods the methods
165+
* @param consumes the consumes
166+
* @param produces the produces
167+
* @param headers the headers
168+
* @param params the params
169+
* @param springDocVersionStrategy the version strategy
170+
*/
171+
public RouterOperation(String path, RequestMethod[] methods, String[] consumes, String[] produces, String[] headers, String[] params, SpringDocVersionStrategy springDocVersionStrategy) {
165172
this.path = path;
166173
this.methods = methods;
167174
this.consumes = consumes;
168175
this.produces = produces;
169176
this.headers = headers;
170177
this.params = params;
178+
this.springDocVersionStrategy = springDocVersionStrategy;
171179
}
172180

173181
/**
@@ -422,6 +430,24 @@ public void setOperationModel(io.swagger.v3.oas.models.Operation operationModel)
422430
this.operationModel = operationModel;
423431
}
424432

433+
/**
434+
* Gets version strategy.
435+
*
436+
* @return the version strategy
437+
*/
438+
public SpringDocVersionStrategy getSpringDocVersionStrategy() {
439+
return springDocVersionStrategy;
440+
}
441+
442+
/**
443+
* Sets version strategy.
444+
*
445+
* @param springDocVersionStrategy the version strategy
446+
*/
447+
public void setVersionStrategy(SpringDocVersionStrategy springDocVersionStrategy) {
448+
this.springDocVersionStrategy = springDocVersionStrategy;
449+
}
450+
425451
@Override
426452
public int compareTo(RouterOperation routerOperation) {
427453
int result = path.compareTo(routerOperation.getPath());

0 commit comments

Comments
 (0)