Skip to content

Commit 459578c

Browse files
committed
Support for CompletionStage as endpoint return type. Fixes #370.
1 parent ae79660 commit 459578c

File tree

5 files changed

+239
-3
lines changed

5 files changed

+239
-3
lines changed

springdoc-openapi-webmvc-core/src/main/java/org/springdoc/core/ResponseBuilder.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@
2020

2121
import java.lang.reflect.ParameterizedType;
2222
import java.lang.reflect.Type;
23+
import java.util.Arrays;
2324
import java.util.List;
2425
import java.util.concurrent.Callable;
26+
import java.util.concurrent.CompletionStage;
2527

2628
import com.fasterxml.jackson.annotation.JsonView;
2729
import io.swagger.v3.oas.models.Components;
@@ -33,18 +35,21 @@
3335
@SuppressWarnings("rawtypes")
3436
public class ResponseBuilder extends AbstractResponseBuilder {
3537

38+
private final List<Class<?>> resultWrappers = Arrays.asList(Callable.class, CompletionStage.class);
39+
3640
public ResponseBuilder(OperationBuilder operationBuilder, List<ReturnTypeParser> returnTypeParsers) {
3741
super(operationBuilder, returnTypeParsers);
3842
}
3943

4044
@Override
4145
protected Schema calculateSchemaFromParameterizedType(Components components, ParameterizedType parameterizedType,
42-
JsonView jsonView) {
46+
JsonView jsonView) {
4347
Schema<?> schemaN = null;
44-
if (ResponseEntity.class.getName().contentEquals(parameterizedType.getRawType().getTypeName()) || HttpEntity.class.getName().contentEquals(parameterizedType.getRawType().getTypeName())) {
48+
String parameterizedTypeName = parameterizedType.getRawType().getTypeName();
49+
if (ResponseEntity.class.getName().contentEquals(parameterizedTypeName) || HttpEntity.class.getName().contentEquals(parameterizedTypeName)) {
4550
schemaN = calculateSchemaParameterizedType(components, parameterizedType, jsonView);
4651
}
47-
else if (Callable.class.getName().contentEquals(parameterizedType.getRawType().getTypeName())) {
52+
else if(resultWrappers.stream().anyMatch(clazz -> isParameterizedTypeWrapper(clazz, parameterizedType))) {
4853
Type type = parameterizedType.getActualTypeArguments()[0];
4954
if (type instanceof ParameterizedType) {
5055
schemaN = this.calculateSchemaFromParameterizedType(components, (ParameterizedType) type, jsonView);
@@ -56,4 +61,12 @@ else if (Callable.class.getName().contentEquals(parameterizedType.getRawType().g
5661
return schemaN;
5762
}
5863

64+
private boolean isParameterizedTypeWrapper(Class<?> clazz, ParameterizedType type) {
65+
try {
66+
Class<?> parameterType = Class.forName(type.getRawType().getTypeName());
67+
return clazz.isAssignableFrom(parameterType);
68+
} catch (ClassNotFoundException e) {
69+
return false;
70+
}
71+
}
5972
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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.app76;
20+
21+
import org.springframework.http.ResponseEntity;
22+
import org.springframework.web.bind.annotation.RequestMapping;
23+
import org.springframework.web.bind.annotation.RequestMethod;
24+
import org.springframework.web.bind.annotation.RestController;
25+
26+
import java.util.concurrent.CompletableFuture;
27+
import java.util.concurrent.CompletionStage;
28+
29+
@RestController
30+
public class HelloController {
31+
32+
@RequestMapping(value = "/person1", method = RequestMethod.GET)
33+
private CompletionStage<ResponseEntity<PersonDTO>> getPerson1(String str) {
34+
return null;
35+
}
36+
37+
@RequestMapping(value = "/person2", method = RequestMethod.GET)
38+
private CompletableFuture<PersonDTO> getPerson2(String str) {
39+
return null;
40+
}
41+
42+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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.app76;
20+
21+
public class PersonDTO {
22+
private String email;
23+
24+
private String firstName;
25+
26+
private String lastName;
27+
28+
public PersonDTO() {
29+
}
30+
31+
public PersonDTO(final String email, final String firstName, final String lastName) {
32+
this.email = email;
33+
this.firstName = firstName;
34+
this.lastName = lastName;
35+
}
36+
37+
public String getEmail() {
38+
return email;
39+
}
40+
41+
public void setEmail(final String email) {
42+
this.email = email;
43+
}
44+
45+
public String getFirstName() {
46+
return firstName;
47+
}
48+
49+
public void setFirstName(final String firstName) {
50+
this.firstName = firstName;
51+
}
52+
53+
public String getLastName() {
54+
return lastName;
55+
}
56+
57+
public void setLastName(final String lastName) {
58+
this.lastName = lastName;
59+
}
60+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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.app76;
20+
21+
import org.springframework.boot.autoconfigure.SpringBootApplication;
22+
import test.org.springdoc.api.AbstractSpringDocTest;
23+
24+
public class SpringDocApp76Test extends AbstractSpringDocTest {
25+
26+
@SpringBootApplication
27+
static class SpringDocTestApp {}
28+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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+
"/person1": {
15+
"get": {
16+
"tags": [
17+
"hello-controller"
18+
],
19+
"operationId": "getPerson1",
20+
"parameters": [
21+
{
22+
"name": "str",
23+
"in": "query",
24+
"required": true,
25+
"schema": {
26+
"type": "string"
27+
}
28+
}
29+
],
30+
"responses": {
31+
"200": {
32+
"description": "default response",
33+
"content": {
34+
"*/*": {
35+
"schema": {
36+
"$ref": "#/components/schemas/PersonDTO"
37+
}
38+
}
39+
}
40+
}
41+
}
42+
}
43+
},
44+
"/person2": {
45+
"get": {
46+
"tags": [
47+
"hello-controller"
48+
],
49+
"operationId": "getPerson2",
50+
"parameters": [
51+
{
52+
"name": "str",
53+
"in": "query",
54+
"required": true,
55+
"schema": {
56+
"type": "string"
57+
}
58+
}
59+
],
60+
"responses": {
61+
"200": {
62+
"description": "default response",
63+
"content": {
64+
"*/*": {
65+
"schema": {
66+
"$ref": "#/components/schemas/PersonDTO"
67+
}
68+
}
69+
}
70+
}
71+
}
72+
}
73+
}
74+
},
75+
"components": {
76+
"schemas": {
77+
"PersonDTO": {
78+
"type": "object",
79+
"properties": {
80+
"email": {
81+
"type": "string"
82+
},
83+
"firstName": {
84+
"type": "string"
85+
},
86+
"lastName": {
87+
"type": "string"
88+
}
89+
}
90+
}
91+
}
92+
}
93+
}

0 commit comments

Comments
 (0)