Skip to content

Commit 972934e

Browse files
Change so that useReturnTypeSchema is considered on an http code level rather than on a method level
1 parent d504fd8 commit 972934e

File tree

6 files changed

+177
-83
lines changed

6 files changed

+177
-83
lines changed

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
@@ -134,7 +134,7 @@ public class MethodAttributes {
134134
/**
135135
* The Use return type schema.
136136
*/
137-
private boolean useReturnTypeSchema;
137+
private final Map<String, Boolean> useReturnTypeSchema = new LinkedHashMap<>();
138138

139139
/**
140140
* Instantiates a new Method attributes.
@@ -529,20 +529,21 @@ public Locale getLocale() {
529529
}
530530

531531
/**
532-
* Is use return type schema boolean.
532+
* Gets use return type schema.
533533
*
534-
* @return the boolean
534+
* @return the use return type schema
535535
*/
536-
public boolean isUseReturnTypeSchema() {
536+
public Map<String, Boolean> getUseReturnTypeSchema() {
537537
return useReturnTypeSchema;
538538
}
539539

540540
/**
541-
* Sets use return type schema.
541+
* Put use return type schema.
542542
*
543+
* @param responseCode the response code
543544
* @param useReturnTypeSchema the use return type schema
544545
*/
545-
public void setUseReturnTypeSchema(boolean useReturnTypeSchema) {
546-
this.useReturnTypeSchema = useReturnTypeSchema;
546+
public void putUseReturnTypeSchema(String responseCode, Boolean useReturnTypeSchema) {
547+
this.useReturnTypeSchema.put(responseCode, useReturnTypeSchema);
547548
}
548549
}

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

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -180,22 +180,22 @@ public GenericResponseService(OperationService operationService,
180180
* @param components the components
181181
* @param apiResponsesOp the api responses op
182182
* @param methodAttributes the method attributes
183-
* @param apiResponseAnnotations the api response annotations
183+
* @param apiResponseAnnotation the api response annotation
184184
* @param apiResponse the api response
185185
* @param openapi31 the openapi 31
186186
*/
187187
public static void buildContentFromDoc(Components components, ApiResponses apiResponsesOp,
188188
MethodAttributes methodAttributes,
189-
io.swagger.v3.oas.annotations.responses.ApiResponse apiResponseAnnotations,
189+
io.swagger.v3.oas.annotations.responses.ApiResponse apiResponseAnnotation,
190190
ApiResponse apiResponse, boolean openapi31) {
191191

192-
methodAttributes.setUseReturnTypeSchema(apiResponseAnnotations.useReturnTypeSchema());
193-
io.swagger.v3.oas.annotations.media.Content[] contentdoc = apiResponseAnnotations.content();
192+
methodAttributes.putUseReturnTypeSchema(apiResponseAnnotation.responseCode(), apiResponseAnnotation.useReturnTypeSchema());
193+
io.swagger.v3.oas.annotations.media.Content[] contentdoc = apiResponseAnnotation.content();
194194
Optional<Content> optionalContent = getContent(contentdoc, new String[0],
195195
methodAttributes.getMethodProduces(), null, components, methodAttributes.getJsonViewAnnotation(), openapi31);
196-
if (apiResponsesOp.containsKey(apiResponseAnnotations.responseCode())) {
196+
if (apiResponsesOp.containsKey(apiResponseAnnotation.responseCode())) {
197197
// Merge with the existing content
198-
Content existingContent = apiResponsesOp.get(apiResponseAnnotations.responseCode()).getContent();
198+
Content existingContent = apiResponsesOp.get(apiResponseAnnotation.responseCode()).getContent();
199199
if (optionalContent.isPresent()) {
200200
Content newContent = optionalContent.get();
201201
if (methodAttributes.isMethodOverloaded() && existingContent != null) {
@@ -387,17 +387,17 @@ private Map<String, ApiResponse> computeResponseFromDoc(Components components, M
387387
Set<io.swagger.v3.oas.annotations.responses.ApiResponse> responsesArray = getApiResponses(Objects.requireNonNull(methodParameter.getMethod()));
388388
if (!responsesArray.isEmpty()) {
389389
methodAttributes.setWithApiResponseDoc(true);
390-
for (io.swagger.v3.oas.annotations.responses.ApiResponse apiResponseAnnotations : responsesArray) {
391-
String httpCode = apiResponseAnnotations.responseCode();
390+
for (io.swagger.v3.oas.annotations.responses.ApiResponse apiResponseAnnotation : responsesArray) {
391+
String httpCode = apiResponseAnnotation.responseCode();
392392
ApiResponse apiResponse = new ApiResponse();
393-
if (StringUtils.isNotBlank(apiResponseAnnotations.ref())) {
394-
apiResponse.$ref(apiResponseAnnotations.ref());
395-
apiResponsesOp.addApiResponse(apiResponseAnnotations.responseCode(), apiResponse);
393+
if (StringUtils.isNotBlank(apiResponseAnnotation.ref())) {
394+
apiResponse.$ref(apiResponseAnnotation.ref());
395+
apiResponsesOp.addApiResponse(apiResponseAnnotation.responseCode(), apiResponse);
396396
continue;
397397
}
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());
398+
apiResponse.setDescription(propertyResolverUtils.resolve(apiResponseAnnotation.description(), methodAttributes.getLocale()));
399+
buildContentFromDoc(components, apiResponsesOp, methodAttributes, apiResponseAnnotation, apiResponse, openapi31);
400+
Map<String, Object> extensions = AnnotationsUtils.getExtensions(propertyResolverUtils.isOpenapi31(), apiResponseAnnotation.extensions());
401401
if (!CollectionUtils.isEmpty(extensions)) {
402402
if (propertyResolverUtils.isResolveExtensionsProperties()) {
403403
Map<String, Object> extensionsResolved = propertyResolverUtils.resolveExtensions(locale, extensions);
@@ -407,7 +407,7 @@ private Map<String, ApiResponse> computeResponseFromDoc(Components components, M
407407
apiResponse.extensions(extensions);
408408
}
409409
}
410-
SpringDocAnnotationsUtils.getHeaders(apiResponseAnnotations.headers(), components, methodAttributes.getJsonViewAnnotation(), openapi31)
410+
SpringDocAnnotationsUtils.getHeaders(apiResponseAnnotation.headers(), components, methodAttributes.getJsonViewAnnotation(), openapi31)
411411
.ifPresent(apiResponse::headers);
412412
apiResponsesOp.addApiResponse(httpCode, apiResponse);
413413
}
@@ -622,8 +622,7 @@ else if (CollectionUtils.isEmpty(apiResponse.getContent()))
622622
setDescription(httpCode, apiResponse);
623623
}
624624
}
625-
if (apiResponse.getContent() != null && (methodAttributes.isUseReturnTypeSchema() ||
626-
((isGeneric || methodAttributes.isMethodOverloaded()) && methodAttributes.isNoApiResponseDoc()))) {
625+
if (apiResponse.getContent() != null && shouldCalculateContent(methodAttributes, isGeneric, httpCode)) {
627626
// Merge with existing schema
628627
Content existingContent = apiResponse.getContent();
629628
Type type = GenericTypeResolver.resolveType(methodParameter.getGenericParameterType(), methodParameter.getContainingClass());
@@ -642,6 +641,28 @@ else if (CollectionUtils.isEmpty(apiResponse.getContent()))
642641
apiResponsesOp.addApiResponse(httpCode, apiResponse);
643642
}
644643

644+
/**
645+
* Whether to consider calculating additional content.
646+
*
647+
* @param methodAttributes the method attributes
648+
* @param isGeneric the is generic
649+
* @param httpCode the http code
650+
*/
651+
private boolean shouldCalculateContent(MethodAttributes methodAttributes, boolean isGeneric, String httpCode) {
652+
return useReturnTypeSchema(methodAttributes, httpCode) ||
653+
((isGeneric || methodAttributes.isMethodOverloaded()) && methodAttributes.isNoApiResponseDoc());
654+
}
655+
656+
/**
657+
* Whether to use return type schema.
658+
*
659+
* @param methodAttributes the method attributes
660+
* @param httpCode the http code
661+
*/
662+
private boolean useReturnTypeSchema(MethodAttributes methodAttributes, String httpCode) {
663+
return methodAttributes.getUseReturnTypeSchema().getOrDefault(httpCode, false);
664+
}
665+
645666
/**
646667
* Evaluate response status string.
647668
*

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
}
Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,62 @@
11
{
2-
"openapi": "3.1.0",
3-
"info": {
4-
"title": "OpenAPI definition",
5-
"version": "v0"
2+
"openapi" : "3.1.0",
3+
"info" : {
4+
"title" : "OpenAPI definition",
5+
"version" : "v0"
66
},
7-
"servers": [
8-
{
9-
"url": "http://localhost",
10-
"description": "Generated server url"
11-
}
12-
],
13-
"paths": {
14-
"/testBoolean": {
15-
"post": {
16-
"tags": [
17-
"hello-controller"
18-
],
19-
"operationId": "HelloController",
20-
"responses": {
21-
"200": {
22-
"description": "OK",
23-
"content": {
24-
"*/*": {
25-
"schema": {
26-
"type": "object",
27-
"additionalProperties": {
28-
"type": "string"
7+
"servers" : [ {
8+
"url" : "http://localhost",
9+
"description" : "Generated server url"
10+
} ],
11+
"paths" : {
12+
"/testBoolean" : {
13+
"post" : {
14+
"tags" : [ "hello-controller" ],
15+
"operationId" : "HelloController",
16+
"responses" : {
17+
"200" : {
18+
"description" : "OK",
19+
"content" : {
20+
"*/*" : {
21+
"schema" : {
22+
"type" : "object",
23+
"additionalProperties" : {
24+
"type" : "string"
2925
}
3026
},
31-
"examples": {
32-
"success": {
33-
"description": "success",
34-
"value": "..."
27+
"examples" : {
28+
"success" : {
29+
"description" : "success",
30+
"value" : "..."
3531
}
3632
}
3733
}
3834
}
35+
},
36+
"400" : {
37+
"description" : "OK",
38+
"content" : {
39+
"*/*" : {
40+
"schema" : {
41+
"$ref" : "#/components/schemas/Error"
42+
}
43+
}
44+
}
3945
}
4046
}
4147
}
4248
}
4349
},
44-
"components": {}
45-
}
50+
"components" : {
51+
"schemas" : {
52+
"Error" : {
53+
"type" : "object",
54+
"properties" : {
55+
"message" : {
56+
"type" : "string"
57+
}
58+
}
59+
}
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)