Skip to content

Commit b6eee1f

Browse files
author
bnasslahsen
committed
Operation having method for each accept header generate does not always generate the same api-docs. Fixes #822
1 parent 73207b9 commit b6eee1f

File tree

5 files changed

+275
-3
lines changed

5 files changed

+275
-3
lines changed

springdoc-openapi-common/src/main/java/org/springdoc/core/GenericResponseBuilder.java

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.lang.reflect.Type;
2727
import java.util.ArrayList;
2828
import java.util.Arrays;
29+
import java.util.HashSet;
2930
import java.util.LinkedHashMap;
3031
import java.util.List;
3132
import java.util.Map;
@@ -290,13 +291,15 @@ private void buildGenericApiResponses(Components components, MethodParameter met
290291
private void buildApiResponses(Components components, MethodParameter methodParameter, ApiResponses apiResponsesOp,
291292
MethodAttributes methodAttributes) {
292293
Map<String, ApiResponse> genericMapResponse = methodAttributes.getGenericMapResponse();
293-
if (!CollectionUtils.isEmpty(apiResponsesOp) && apiResponsesOp.size() > genericMapResponse.size()) {
294+
if (!CollectionUtils.isEmpty(apiResponsesOp) && apiResponsesOp.size() > genericMapResponse.size()) {
294295
// API Responses at operation and @ApiResponse annotation
295296
for (Map.Entry<String, ApiResponse> entry : apiResponsesOp.entrySet()) {
296297
String httpCode = entry.getKey();
297298
if (!genericMapResponse.containsKey(httpCode)) {
298-
ApiResponse apiResponse = entry.getValue();
299-
buildApiResponses(components, methodParameter, apiResponsesOp, methodAttributes, httpCode, apiResponse, false);
299+
if (!methodAttributes.isMethodOverloaded() || (methodAttributes.isMethodOverloaded() && isValidHttpCode(httpCode, methodParameter))) {
300+
ApiResponse apiResponse = entry.getValue();
301+
buildApiResponses(components, methodParameter, apiResponsesOp, methodAttributes, httpCode, apiResponse, false);
302+
}
300303
}
301304
}
302305
}
@@ -533,4 +536,26 @@ private Map<String, ApiResponse> getGenericMapResponse(Class<?> beanType) {
533536
.map(ControllerAdviceInfo::getApiResponseMap)
534537
.collect(LinkedHashMap::new, Map::putAll, Map::putAll);
535538
}
539+
540+
private boolean isValidHttpCode(String httpCode, MethodParameter methodParameter) {
541+
Set<io.swagger.v3.oas.annotations.responses.ApiResponse> responseSet = getApiResponses(methodParameter.getMethod());
542+
if (isHttpCodePresent(httpCode, responseSet))
543+
return true;
544+
io.swagger.v3.oas.annotations.Operation apiOperation = AnnotatedElementUtils.findMergedAnnotation(methodParameter.getMethod(),
545+
io.swagger.v3.oas.annotations.Operation.class);
546+
if (apiOperation != null) {
547+
responseSet = new HashSet<>(Arrays.asList(apiOperation.responses()));
548+
if (isHttpCodePresent(httpCode, responseSet))
549+
return true;
550+
}
551+
String httpCode1 = evaluateResponseStatus(methodParameter.getMethod(), methodParameter.getMethod().getClass(), false);
552+
if (httpCode.equals(httpCode1))
553+
return true;
554+
return false;
555+
}
556+
557+
private boolean isHttpCodePresent(String httpCode, Set<io.swagger.v3.oas.annotations.responses.ApiResponse> responseSet) {
558+
return !responseSet.isEmpty() && responseSet.stream().anyMatch(apiResponseAnnotations -> httpCode.equals(apiResponseAnnotations.responseCode()));
559+
}
560+
536561
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.api.app12;
20+
21+
import com.fasterxml.jackson.annotation.JsonProperty;
22+
import io.swagger.v3.oas.annotations.Operation;
23+
import io.swagger.v3.oas.annotations.media.Content;
24+
import io.swagger.v3.oas.annotations.media.Schema;
25+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
26+
27+
import org.springframework.hateoas.EntityModel;
28+
import org.springframework.hateoas.MediaTypes;
29+
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
30+
import org.springframework.http.MediaType;
31+
import org.springframework.http.ResponseEntity;
32+
import org.springframework.web.bind.annotation.GetMapping;
33+
import org.springframework.web.bind.annotation.RequestMapping;
34+
import org.springframework.web.bind.annotation.RestController;
35+
36+
37+
@RequestMapping("/demo")
38+
@RestController
39+
public class HelloController {
40+
41+
@Operation(summary = "GetMyData", operationId = "gettt",
42+
responses = @ApiResponse(responseCode = "204",
43+
content = @Content(mediaType = "application/vnd.something")))
44+
@GetMapping(produces = "application/vnd.something")
45+
public ResponseEntity<Void> getSomethingElse() {
46+
return ResponseEntity.noContent().build();
47+
}
48+
49+
@GetMapping(produces = MediaType.TEXT_PLAIN_VALUE)
50+
public String get() {
51+
return "some text";
52+
}
53+
54+
@GetMapping(produces = MediaTypes.HAL_JSON_VALUE)
55+
public EntityModel<JsonResponse> getHal() {
56+
return EntityModel.of(new JsonResponse(),
57+
WebMvcLinkBuilder.linkTo(HelloController.class).slash("somelink").withSelfRel()
58+
);
59+
}
60+
61+
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
62+
public JsonResponse getJson() {
63+
return new JsonResponse();
64+
}
65+
66+
@GetMapping(produces = MediaType.APPLICATION_XML_VALUE)
67+
@ApiResponse(responseCode = "202",
68+
content = @Content(mediaType = MediaType.APPLICATION_XML_VALUE, schema = @Schema(implementation = JsonResponse.class)))
69+
public JsonResponse getXML() {
70+
return new JsonResponse();
71+
}
72+
73+
public class JsonResponse {
74+
@JsonProperty
75+
private String field;
76+
}
77+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * * Copyright 2019-2020 the original author or authors.
6+
* * * *
7+
* * * * Licensed under the Apache License, Version 2.0 (the "License");
8+
* * * * you may not use this file except in compliance with the License.
9+
* * * * You may obtain a copy of the License at
10+
* * * *
11+
* * * * https://www.apache.org/licenses/LICENSE-2.0
12+
* * * *
13+
* * * * Unless required by applicable law or agreed to in writing, software
14+
* * * * distributed under the License is distributed on an "AS IS" BASIS,
15+
* * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* * * * See the License for the specific language governing permissions and
17+
* * * * limitations under the License.
18+
* * *
19+
* *
20+
*
21+
*
22+
*/
23+
24+
package test.org.springdoc.api.app12;
25+
26+
import io.swagger.v3.oas.models.Operation;
27+
import org.springdoc.core.customizers.OperationCustomizer;
28+
import test.org.springdoc.api.AbstractSpringDocTest;
29+
30+
import org.springframework.boot.autoconfigure.SpringBootApplication;
31+
import org.springframework.context.annotation.Bean;
32+
import org.springframework.web.method.HandlerMethod;
33+
34+
public class SpringDocApp12Test extends AbstractSpringDocTest {
35+
36+
@SpringBootApplication
37+
static class SpringDocTestApp {
38+
@Bean
39+
OperationCustomizer operationCustomizer() {
40+
return (Operation operation, HandlerMethod handlerMethod) -> {
41+
if (operation.getOperationId().startsWith("gettt"))
42+
operation.setOperationId("gettt");
43+
return operation;
44+
};
45+
}
46+
}
47+
48+
49+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
{
2+
"openapi": "3.0.1",
3+
"info": {
4+
"title": "OpenAPI definition",
5+
"version": "v0"
6+
},
7+
"servers": [
8+
{
9+
"url": "http://localhost",
10+
"description": "Generated server url"
11+
}
12+
],
13+
"paths": {
14+
"/demo": {
15+
"get": {
16+
"tags": [
17+
"hello-controller"
18+
],
19+
"summary": "GetMyData",
20+
"operationId": "gettt",
21+
"responses": {
22+
"200": {
23+
"description": "OK",
24+
"content": {
25+
"text/plain": {
26+
"schema": {
27+
"type": "string"
28+
}
29+
},
30+
"application/json": {
31+
"schema": {
32+
"$ref": "#/components/schemas/JsonResponse"
33+
}
34+
},
35+
"application/hal+json": {
36+
"schema": {
37+
"$ref": "#/components/schemas/EntityModelJsonResponse"
38+
}
39+
}
40+
}
41+
},
42+
"204": {
43+
"description": "No Content",
44+
"content": {
45+
"application/vnd.something": {}
46+
}
47+
},
48+
"202": {
49+
"description": "Accepted",
50+
"content": {
51+
"application/xml": {
52+
"schema": {
53+
"$ref": "#/components/schemas/JsonResponse"
54+
}
55+
}
56+
}
57+
}
58+
}
59+
}
60+
}
61+
},
62+
"components": {
63+
"schemas": {
64+
"JsonResponse": {
65+
"type": "object",
66+
"properties": {
67+
"field": {
68+
"type": "string"
69+
}
70+
}
71+
},
72+
"EntityModelJsonResponse": {
73+
"type": "object",
74+
"properties": {
75+
"field": {
76+
"type": "string"
77+
},
78+
"_links": {
79+
"$ref": "#/components/schemas/Links"
80+
}
81+
}
82+
},
83+
"Links": {
84+
"type": "object",
85+
"additionalProperties": {
86+
"$ref": "#/components/schemas/Link"
87+
}
88+
},
89+
"Link": {
90+
"type": "object",
91+
"properties": {
92+
"href": {
93+
"type": "string"
94+
},
95+
"hreflang": {
96+
"type": "string"
97+
},
98+
"title": {
99+
"type": "string"
100+
},
101+
"type": {
102+
"type": "string"
103+
},
104+
"deprecation": {
105+
"type": "string"
106+
},
107+
"profile": {
108+
"type": "string"
109+
},
110+
"name": {
111+
"type": "string"
112+
},
113+
"templated": {
114+
"type": "boolean"
115+
}
116+
}
117+
}
118+
}
119+
}
120+
}

springdoc-openapi-ui/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<dependency>
3131
<groupId>org.springframework.boot</groupId>
3232
<artifactId>spring-boot-starter-security</artifactId>
33+
<scope>test</scope>
3334
</dependency>
3435
</dependencies>
3536
</project>

0 commit comments

Comments
 (0)