Skip to content

Commit e0bc5b9

Browse files
committed
Changes report: Change so useReturnTypeSchema is considered on an http code level #3141
1 parent c833910 commit e0bc5b9

6 files changed

Lines changed: 178 additions & 90 deletions

File tree

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/MethodAttributes.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public class MethodAttributes {
137137
/**
138138
* The Use return type schema.
139139
*/
140-
private boolean useReturnTypeSchema;
140+
private final Map<String, Boolean> useReturnTypeSchema = new LinkedHashMap<>();
141141

142142
/**
143143
* The Spring doc version strategy.
@@ -585,21 +585,22 @@ public Locale getLocale() {
585585
}
586586

587587
/**
588-
* Is use return type schema boolean.
588+
* Gets use return type schema.
589589
*
590-
* @return the boolean
590+
* @return the use return type schema
591591
*/
592-
public boolean isUseReturnTypeSchema() {
592+
public Map<String, Boolean> getUseReturnTypeSchema() {
593593
return useReturnTypeSchema;
594594
}
595595

596596
/**
597-
* Sets use return type schema.
597+
* Put use return type schema.
598598
*
599+
* @param responseCode the response code
599600
* @param useReturnTypeSchema the use return type schema
600601
*/
601-
public void setUseReturnTypeSchema(boolean useReturnTypeSchema) {
602-
this.useReturnTypeSchema = useReturnTypeSchema;
602+
public void putUseReturnTypeSchema(String responseCode, boolean useReturnTypeSchema) {
603+
this.useReturnTypeSchema.put(responseCode, useReturnTypeSchema);
603604
}
604605

605606
/**

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service/GenericResponseService.java

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,6 @@
6060
import io.swagger.v3.oas.models.responses.ApiResponses;
6161
import org.apache.commons.lang3.ArrayUtils;
6262
import org.apache.commons.lang3.StringUtils;
63-
import org.slf4j.Logger;
64-
import org.slf4j.LoggerFactory;
6563
import org.springdoc.core.models.ControllerAdviceInfo;
6664
import org.springdoc.core.models.MethodAdviceInfo;
6765
import org.springdoc.core.models.MethodAttributes;
@@ -113,11 +111,6 @@ public class GenericResponseService implements ApplicationContextAware {
113111
*/
114112
private static final String EXTENSION_EXCEPTION_CLASSES = "x-exception-class";
115113

116-
/**
117-
* The constant LOGGER.
118-
*/
119-
private static final Logger LOGGER = LoggerFactory.getLogger(GenericResponseService.class);
120-
121114
/**
122115
* The Response entity exception handler class.
123116
*/
@@ -180,22 +173,22 @@ public GenericResponseService(OperationService operationService,
180173
* @param components the components
181174
* @param apiResponsesOp the api responses op
182175
* @param methodAttributes the method attributes
183-
* @param apiResponseAnnotations the api response annotations
176+
* @param apiResponseAnnotation the api response annotations
184177
* @param apiResponse the api response
185178
* @param openapi31 the openapi 31
186179
*/
187180
public static void buildContentFromDoc(Components components, ApiResponses apiResponsesOp,
188181
MethodAttributes methodAttributes,
189-
io.swagger.v3.oas.annotations.responses.ApiResponse apiResponseAnnotations,
182+
io.swagger.v3.oas.annotations.responses.ApiResponse apiResponseAnnotation,
190183
ApiResponse apiResponse, boolean openapi31) {
191184

192-
methodAttributes.setUseReturnTypeSchema(apiResponseAnnotations.useReturnTypeSchema());
193-
io.swagger.v3.oas.annotations.media.Content[] contentdoc = apiResponseAnnotations.content();
185+
methodAttributes.putUseReturnTypeSchema(apiResponseAnnotation.responseCode(), apiResponseAnnotation.useReturnTypeSchema());
186+
io.swagger.v3.oas.annotations.media.Content[] contentdoc = apiResponseAnnotation.content();
194187
Optional<Content> optionalContent = getContent(contentdoc, new String[0],
195188
methodAttributes.getMethodProduces(), null, components, methodAttributes.getJsonViewAnnotation(), openapi31);
196-
if (apiResponsesOp.containsKey(apiResponseAnnotations.responseCode())) {
189+
if (apiResponsesOp.containsKey(apiResponseAnnotation.responseCode())) {
197190
// Merge with the existing content
198-
Content existingContent = apiResponsesOp.get(apiResponseAnnotations.responseCode()).getContent();
191+
Content existingContent = apiResponsesOp.get(apiResponseAnnotation.responseCode()).getContent();
199192
if (optionalContent.isPresent()) {
200193
Content newContent = optionalContent.get();
201194
if (methodAttributes.isMethodOverloaded() && existingContent != null) {
@@ -217,6 +210,7 @@ public static void buildContentFromDoc(Components components, ApiResponses apiRe
217210
}
218211
}
219212

213+
220214
/**
221215
* Sets description.
222216
*
@@ -387,17 +381,17 @@ private Map<String, ApiResponse> computeResponseFromDoc(Components components, M
387381
Set<io.swagger.v3.oas.annotations.responses.ApiResponse> responsesArray = getApiResponses(Objects.requireNonNull(methodParameter.getMethod()));
388382
if (!responsesArray.isEmpty()) {
389383
methodAttributes.setWithApiResponseDoc(true);
390-
for (io.swagger.v3.oas.annotations.responses.ApiResponse apiResponseAnnotations : responsesArray) {
391-
String httpCode = apiResponseAnnotations.responseCode();
384+
for (io.swagger.v3.oas.annotations.responses.ApiResponse apiResponseAnnotation : responsesArray) {
385+
String httpCode = apiResponseAnnotation.responseCode();
392386
ApiResponse apiResponse = new ApiResponse();
393-
if (StringUtils.isNotBlank(apiResponseAnnotations.ref())) {
394-
apiResponse.$ref(apiResponseAnnotations.ref());
395-
apiResponsesOp.addApiResponse(apiResponseAnnotations.responseCode(), apiResponse);
387+
if (StringUtils.isNotBlank(apiResponseAnnotation.ref())) {
388+
apiResponse.$ref(apiResponseAnnotation.ref());
389+
apiResponsesOp.addApiResponse(apiResponseAnnotation.responseCode(), apiResponse);
396390
continue;
397391
}
398-
apiResponse.setDescription(propertyResolverUtils.resolve(apiResponseAnnotations.description(), methodAttributes.getLocale()));
399-
buildContentFromDoc(components, apiResponsesOp, methodAttributes, apiResponseAnnotations, apiResponse, openapi31);
400-
Map<String, Object> extensions = AnnotationsUtils.getExtensions(propertyResolverUtils.isOpenapi31(), apiResponseAnnotations.extensions());
392+
apiResponse.setDescription(propertyResolverUtils.resolve(apiResponseAnnotation.description(), methodAttributes.getLocale()));
393+
buildContentFromDoc(components, apiResponsesOp, methodAttributes, apiResponseAnnotation, apiResponse, openapi31);
394+
Map<String, Object> extensions = AnnotationsUtils.getExtensions(propertyResolverUtils.isOpenapi31(), apiResponseAnnotation.extensions());
401395
if (!CollectionUtils.isEmpty(extensions)) {
402396
if (propertyResolverUtils.isResolveExtensionsProperties()) {
403397
Map<String, Object> extensionsResolved = propertyResolverUtils.resolveExtensions(locale, extensions);
@@ -407,7 +401,7 @@ private Map<String, ApiResponse> computeResponseFromDoc(Components components, M
407401
apiResponse.extensions(extensions);
408402
}
409403
}
410-
SpringDocAnnotationsUtils.getHeaders(apiResponseAnnotations.headers(), components, methodAttributes.getJsonViewAnnotation(), openapi31)
404+
SpringDocAnnotationsUtils.getHeaders(apiResponseAnnotation.headers(), components, methodAttributes.getJsonViewAnnotation(), openapi31)
411405
.ifPresent(apiResponse::headers);
412406
apiResponsesOp.addApiResponse(httpCode, apiResponse);
413407
}
@@ -622,8 +616,7 @@ else if (CollectionUtils.isEmpty(apiResponse.getContent()))
622616
setDescription(httpCode, apiResponse);
623617
}
624618
}
625-
if (apiResponse.getContent() != null && (methodAttributes.isUseReturnTypeSchema() ||
626-
((isGeneric || methodAttributes.isMethodOverloaded()) && methodAttributes.isNoApiResponseDoc()))) {
619+
if (apiResponse.getContent() != null && shouldCalculateContent(methodAttributes, isGeneric, httpCode)) {
627620
// Merge with existing schema
628621
Content existingContent = apiResponse.getContent();
629622
Type type = GenericTypeResolver.resolveType(methodParameter.getGenericParameterType(), methodParameter.getContainingClass());
@@ -642,6 +635,28 @@ else if (CollectionUtils.isEmpty(apiResponse.getContent()))
642635
apiResponsesOp.addApiResponse(httpCode, apiResponse);
643636
}
644637

638+
/**
639+
* Whether to consider calculating additional content.
640+
*
641+
* @param methodAttributes the method attributes
642+
* @param isGeneric the is generic
643+
* @param httpCode the http code
644+
*/
645+
private boolean shouldCalculateContent(MethodAttributes methodAttributes, boolean isGeneric, String httpCode) {
646+
return useReturnTypeSchema(methodAttributes, httpCode) ||
647+
((isGeneric || methodAttributes.isMethodOverloaded()) && methodAttributes.isNoApiResponseDoc());
648+
}
649+
650+
/**
651+
* Whether to use return type schema.
652+
*
653+
* @param methodAttributes the method attributes
654+
* @param httpCode the http code
655+
*/
656+
private boolean useReturnTypeSchema(MethodAttributes methodAttributes, String httpCode) {
657+
return methodAttributes.getUseReturnTypeSchema().getOrDefault(httpCode, false);
658+
}
659+
645660
/**
646661
* Evaluate response status string.
647662
*

springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app226/HelloController.java

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
import io.swagger.v3.oas.annotations.media.Content;
66
import io.swagger.v3.oas.annotations.media.ExampleObject;
7+
import io.swagger.v3.oas.annotations.media.Schema;
78
import io.swagger.v3.oas.annotations.responses.ApiResponse;
89

10+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
911
import org.springframework.web.bind.annotation.PostMapping;
1012
import org.springframework.web.bind.annotation.RequestMapping;
1113
import org.springframework.web.bind.annotation.RestController;
@@ -17,20 +19,35 @@
1719
@RequestMapping
1820
public class HelloController {
1921

22+
public record Error(String message) {
23+
24+
}
25+
2026
@PostMapping("/testBoolean")
21-
@ApiResponse(
22-
useReturnTypeSchema = true,
23-
responseCode = "200",
24-
description = "OK",
25-
content = {
26-
@Content(
27-
mediaType = "*/*",
28-
examples =
29-
@ExampleObject(
30-
name = "success",
31-
value = "..."))
32-
}
33-
)
27+
@ApiResponses(value = {
28+
@ApiResponse(
29+
useReturnTypeSchema = true,
30+
responseCode = "200",
31+
description = "OK",
32+
content = {
33+
@Content(
34+
mediaType = "*/*",
35+
examples =
36+
@ExampleObject(
37+
name = "success",
38+
value = "..."))
39+
}
40+
),
41+
@ApiResponse(
42+
responseCode = "400",
43+
description = "OK",
44+
content = {
45+
@Content(
46+
mediaType = "*/*",
47+
schema = @Schema(implementation = Error.class))
48+
}
49+
)
50+
})
3451
public Map<String, String> HelloController() {
3552
return null;
3653
}

springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v31/app226/HelloController.java

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
import io.swagger.v3.oas.annotations.media.Content;
66
import io.swagger.v3.oas.annotations.media.ExampleObject;
7+
import io.swagger.v3.oas.annotations.media.Schema;
78
import io.swagger.v3.oas.annotations.responses.ApiResponse;
89

10+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
911
import org.springframework.web.bind.annotation.PostMapping;
1012
import org.springframework.web.bind.annotation.RequestMapping;
1113
import org.springframework.web.bind.annotation.RestController;
@@ -17,20 +19,35 @@
1719
@RequestMapping
1820
public class HelloController {
1921

22+
public record Error(String message) {
23+
24+
}
25+
2026
@PostMapping("/testBoolean")
21-
@ApiResponse(
22-
useReturnTypeSchema = true,
23-
responseCode = "200",
24-
description = "OK",
25-
content = {
26-
@Content(
27-
mediaType = "*/*",
28-
examples =
29-
@ExampleObject(
30-
name = "success",
31-
value = "..."))
32-
}
33-
)
27+
@ApiResponses(value = {
28+
@ApiResponse(
29+
useReturnTypeSchema = true,
30+
responseCode = "200",
31+
description = "OK",
32+
content = {
33+
@Content(
34+
mediaType = "*/*",
35+
examples =
36+
@ExampleObject(
37+
name = "success",
38+
value = "..."))
39+
}
40+
),
41+
@ApiResponse(
42+
responseCode = "400",
43+
description = "OK",
44+
content = {
45+
@Content(
46+
mediaType = "*/*",
47+
schema = @Schema(implementation = Error.class))
48+
}
49+
)
50+
})
3451
public Map<String, String> HelloController() {
3552
return null;
3653
}

springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app226.json

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,31 @@
3636
}
3737
}
3838
}
39+
},
40+
"400" : {
41+
"description" : "OK",
42+
"content" : {
43+
"*/*" : {
44+
"schema" : {
45+
"$ref" : "#/components/schemas/Error"
46+
}
47+
}
48+
}
3949
}
4050
}
4151
}
4252
}
4353
},
44-
"components": {}
54+
"components": {
55+
"schemas" : {
56+
"Error" : {
57+
"type" : "object",
58+
"properties" : {
59+
"message" : {
60+
"type" : "string"
61+
}
62+
}
63+
}
64+
}
65+
}
4566
}

0 commit comments

Comments
 (0)