Skip to content

Commit b303207

Browse files
Exclude Spring methods using Swagger annotations (#721)
Support Swagger V3 annotations
1 parent 64caab5 commit b303207

File tree

9 files changed

+430
-53
lines changed

9 files changed

+430
-53
lines changed

typescript-generator-core/pom.xml

+7-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,13 @@
157157
<dependency>
158158
<groupId>io.swagger</groupId>
159159
<artifactId>swagger-annotations</artifactId>
160-
<version>1.6.2</version>
160+
<version>1.6.3</version>
161+
<scope>test</scope>
162+
</dependency>
163+
<dependency>
164+
<groupId>io.swagger.core.v3</groupId>
165+
<artifactId>swagger-annotations</artifactId>
166+
<version>2.1.11</version>
161167
<scope>test</scope>
162168
</dependency>
163169
<dependency>

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/Swagger.java

+155-47
Original file line numberDiff line numberDiff line change
@@ -10,56 +10,115 @@
1010
import java.util.Arrays;
1111
import java.util.List;
1212
import java.util.Map;
13+
import java.util.Objects;
1314
import java.util.Set;
15+
import java.util.function.Supplier;
1416

1517

1618
public class Swagger {
1719

18-
static SwaggerOperation parseSwaggerAnnotations(Method method) {
20+
public static SwaggerOperation parseSwaggerAnnotations(Method method) {
21+
return firstResult(
22+
() -> parseSwaggerAnnotations3(method),
23+
() -> parseSwaggerAnnotations1(method),
24+
new SwaggerOperation());
25+
}
26+
27+
private static SwaggerOperation parseSwaggerAnnotations1(Method method) {
28+
final Annotation apiOperation = Utils.getAnnotation(method, "io.swagger.annotations.ApiOperation");
29+
final Annotation[] apiResponses = Utils.getAnnotationElementValue(method, "io.swagger.annotations.ApiResponses", "value", Annotation[].class);
30+
if (apiOperation == null && apiResponses == null) {
31+
return null;
32+
}
1933
final SwaggerOperation swaggerOperation = new SwaggerOperation();
2034
// @ApiOperation
21-
{
22-
final Annotation apiOperation = Utils.getAnnotation(method, "io.swagger.annotations.ApiOperation");
23-
if (apiOperation != null) {
24-
final Class<?> response = Utils.getAnnotationElementValue(apiOperation, "response", Class.class);
25-
final String responseContainer = Utils.getAnnotationElementValue(apiOperation, "responseContainer", String.class);
26-
if (responseContainer == null || responseContainer.isEmpty()) {
27-
swaggerOperation.responseType = response;
28-
} else {
29-
switch (responseContainer) {
30-
case "List":
31-
swaggerOperation.responseType = Utils.createParameterizedType(List.class, response);
32-
break;
33-
case "Set":
34-
swaggerOperation.responseType = Utils.createParameterizedType(Set.class, response);
35-
break;
36-
case "Map":
37-
swaggerOperation.responseType = Utils.createParameterizedType(Map.class, String.class, response);
38-
break;
39-
}
35+
if (apiOperation != null) {
36+
final Class<?> response = Utils.getAnnotationElementValue(apiOperation, "response", Class.class);
37+
final String responseContainer = Utils.getAnnotationElementValue(apiOperation, "responseContainer", String.class);
38+
if (responseContainer == null || responseContainer.isEmpty()) {
39+
swaggerOperation.responseType = response;
40+
} else {
41+
switch (responseContainer) {
42+
case "List":
43+
swaggerOperation.responseType = Utils.createParameterizedType(List.class, response);
44+
break;
45+
case "Set":
46+
swaggerOperation.responseType = Utils.createParameterizedType(Set.class, response);
47+
break;
48+
case "Map":
49+
swaggerOperation.responseType = Utils.createParameterizedType(Map.class, String.class, response);
50+
break;
4051
}
41-
swaggerOperation.hidden = Utils.getAnnotationElementValue(apiOperation, "hidden", Boolean.class);
42-
swaggerOperation.comment = Utils.getAnnotationElementValue(apiOperation, "value", String.class);
43-
swaggerOperation.comment = swaggerOperation.comment.isEmpty() ? null : swaggerOperation.comment;
4452
}
53+
swaggerOperation.hidden = Utils.getAnnotationElementValue(apiOperation, "hidden", Boolean.class);
54+
swaggerOperation.comment = Utils.getAnnotationElementValue(apiOperation, "value", String.class);
55+
swaggerOperation.comment = swaggerOperation.comment.isEmpty() ? null : swaggerOperation.comment;
4556
}
4657
// @ApiResponses
47-
{
48-
final Annotation[] apiResponses = Utils.getAnnotationElementValue(method, "io.swagger.annotations.ApiResponses", "value", Annotation[].class);
49-
if (apiResponses != null) {
50-
swaggerOperation.possibleResponses = new ArrayList<>();
51-
for (Annotation apiResponse : apiResponses) {
52-
final SwaggerResponse response = new SwaggerResponse();
53-
response.code = Utils.getAnnotationElementValue(apiResponse, "code", Integer.class);
54-
response.comment = Utils.getAnnotationElementValue(apiResponse, "message", String.class);
55-
response.responseType = Utils.getAnnotationElementValue(apiResponse, "response", Class.class);
58+
if (apiResponses != null) {
59+
swaggerOperation.possibleResponses = new ArrayList<>();
60+
for (Annotation apiResponse : apiResponses) {
61+
final SwaggerResponse response = new SwaggerResponse();
62+
response.code = String.valueOf(Utils.getAnnotationElementValue(apiResponse, "code", Integer.class));
63+
response.comment = Utils.getAnnotationElementValue(apiResponse, "message", String.class);
64+
response.responseType = Utils.getAnnotationElementValue(apiResponse, "response", Class.class);
65+
swaggerOperation.possibleResponses.add(response);
66+
}
67+
}
68+
return swaggerOperation;
69+
}
70+
71+
private static SwaggerOperation parseSwaggerAnnotations3(Method method) {
72+
final Annotation operationAnnotation = Utils.getAnnotation(method, "io.swagger.v3.oas.annotations.Operation");
73+
final Annotation apiResponseAnnotation = Utils.getAnnotation(method, "io.swagger.v3.oas.annotations.responses.ApiResponse");
74+
final Annotation apiResponsesAnnotation = Utils.getAnnotation(method, "io.swagger.v3.oas.annotations.responses.ApiResponses");
75+
if (operationAnnotation == null && apiResponseAnnotation == null && apiResponsesAnnotation == null) {
76+
return null;
77+
}
78+
final SwaggerOperation swaggerOperation = new SwaggerOperation();
79+
// @Operation
80+
if (operationAnnotation != null) {
81+
swaggerOperation.hidden = Utils.getAnnotationElementValue(operationAnnotation, "hidden", Boolean.class);
82+
swaggerOperation.comment = Utils.getAnnotationElementValue(operationAnnotation, "description", String.class);
83+
swaggerOperation.comment = swaggerOperation.comment.isEmpty() ? null : swaggerOperation.comment;
84+
}
85+
// @ApiResponses
86+
final List<Annotation> responses = firstResult(
87+
() -> emptyToNull(Utils.getRepeatableAnnotation(apiResponseAnnotation, apiResponsesAnnotation)),
88+
() -> emptyToNull(Arrays.asList(Utils.getAnnotationElementValue(operationAnnotation, "responses", Annotation[].class)))
89+
);
90+
if (responses != null) {
91+
swaggerOperation.possibleResponses = new ArrayList<>();
92+
for (Annotation apiResponse : responses) {
93+
final SwaggerResponse response = new SwaggerResponse();
94+
final String code = Utils.getAnnotationElementValue(apiResponse, "responseCode", String.class);
95+
response.code = Objects.equals(code, "default") ? null : code;
96+
response.comment = Utils.getAnnotationElementValue(apiResponse, "description", String.class);
97+
final Annotation[] content = Utils.getAnnotationElementValue(apiResponse, "content", Annotation[].class);
98+
if (content.length > 0) {
99+
final Annotation schema = Utils.getAnnotationElementValue(content[0], "schema", Annotation.class);
100+
final Class<?> implementation = Utils.getAnnotationElementValue(schema, "implementation", Class.class);
101+
if (!Objects.equals(implementation, Void.class)) {
102+
response.responseType = implementation;
103+
if (swaggerOperation.responseType == null) {
104+
if (response.code == null || isSuccessCode(response.code)) {
105+
swaggerOperation.responseType = implementation;
106+
}
107+
}
108+
}
109+
}
110+
if (response.code != null) {
56111
swaggerOperation.possibleResponses.add(response);
57112
}
58113
}
59114
}
60115
return swaggerOperation;
61116
}
62117

118+
private static boolean isSuccessCode(String code) {
119+
return code.startsWith("2");
120+
}
121+
63122
static List<String> getOperationComments(SwaggerOperation operation) {
64123
final List<String> comments = new ArrayList<>();
65124
if (operation.comment != null) {
@@ -88,31 +147,80 @@ private static BeanModel enrichBean(BeanModel bean) {
88147
final PropertyModel enrichedProperty = enrichProperty(property);
89148
enrichedProperties.add(enrichedProperty);
90149
}
91-
final String comment = Utils.getAnnotationElementValue(bean.getOrigin(), "io.swagger.annotations.ApiModel", "description", String.class);
150+
final String comment = firstResult(
151+
() -> Utils.getAnnotationElementValue(bean.getOrigin(), "io.swagger.v3.oas.annotations.media.Schema", "description", String.class),
152+
() -> Utils.getAnnotationElementValue(bean.getOrigin(), "io.swagger.annotations.ApiModel", "description", String.class));
92153
final List<String> comments = comment != null && !comment.isEmpty() ? Arrays.asList(comment) : null;
93154
return bean.withProperties(enrichedProperties).withComments(Utils.concat(comments, bean.getComments()));
94155
}
95156

96157
private static PropertyModel enrichProperty(PropertyModel property) {
97158
if (property.getOriginalMember() instanceof AnnotatedElement) {
98159
final AnnotatedElement annotatedElement = (AnnotatedElement) property.getOriginalMember();
99-
final String comment = Utils.getAnnotationElementValue(annotatedElement, "io.swagger.annotations.ApiModelProperty", "value", String.class);
100-
final List<String> comments = comment != null && !comment.isEmpty() ? Arrays.asList(comment) : null;
101-
final PropertyModel propertyModel = property.withComments(Utils.concat(comments, property.getComments()));
102-
final String dataTypeString = Utils.getAnnotationElementValue(annotatedElement, "io.swagger.annotations.ApiModelProperty", "dataType", String.class);
103-
if (dataTypeString == null || dataTypeString.isEmpty()) {
104-
return propertyModel;
105-
}
106-
try {
107-
final Type type = Class.forName(dataTypeString);
108-
final boolean required = Utils.getAnnotationElementValue(annotatedElement, "io.swagger.annotations.ApiModelProperty", "required", Boolean.class);
109-
return propertyModel.withType(type).withOptional(!required);
110-
} catch (ClassNotFoundException | ClassCastException e) {
111-
return propertyModel;
112-
}
160+
return firstResult(
161+
() -> enrichProperty3(property, annotatedElement),
162+
() -> enrichProperty1(property, annotatedElement),
163+
property);
113164
} else {
114165
return property;
115166
}
116167
}
117168

169+
private static PropertyModel enrichProperty1(PropertyModel property, AnnotatedElement annotatedElement) {
170+
final Annotation apiModelProperty = Utils.getAnnotation(annotatedElement, "io.swagger.annotations.ApiModelProperty");
171+
if (apiModelProperty == null) {
172+
return null;
173+
}
174+
final String comment = Utils.getAnnotationElementValue(apiModelProperty, "value", String.class);
175+
final List<String> comments = comment != null && !comment.isEmpty() ? Arrays.asList(comment) : null;
176+
final PropertyModel propertyModel = property.withComments(Utils.concat(comments, property.getComments()));
177+
final String dataTypeString = Utils.getAnnotationElementValue(apiModelProperty, "dataType", String.class);
178+
if (dataTypeString == null || dataTypeString.isEmpty()) {
179+
return propertyModel;
180+
}
181+
try {
182+
final Type type = Class.forName(dataTypeString);
183+
final boolean required = Utils.getAnnotationElementValue(apiModelProperty, "required", Boolean.class);
184+
return propertyModel.withType(type).withOptional(!required);
185+
} catch (ClassNotFoundException | ClassCastException e) {
186+
return propertyModel;
187+
}
188+
}
189+
190+
private static PropertyModel enrichProperty3(PropertyModel property, AnnotatedElement annotatedElement) {
191+
final Annotation schema = Utils.getAnnotation(annotatedElement, "io.swagger.v3.oas.annotations.media.Schema");
192+
if (schema == null) {
193+
return null;
194+
}
195+
final String comment = Utils.getAnnotationElementValue(schema, "description", String.class);
196+
final List<String> comments = comment != null && !comment.isEmpty() ? Arrays.asList(comment) : null;
197+
final PropertyModel propertyModel = property.withComments(Utils.concat(comments, property.getComments()));
198+
final Class<?> implementation = Utils.getAnnotationElementValue(schema, "implementation", Class.class);
199+
if (implementation == null || Objects.equals(implementation, Void.class)) {
200+
return propertyModel;
201+
}
202+
final boolean required = Utils.getAnnotationElementValue(schema, "required", Boolean.class);
203+
return propertyModel.withType(implementation).withOptional(!required);
204+
}
205+
206+
private static <T> T firstResult(Supplier<? extends T> supplier1, Supplier<? extends T> supplier2) {
207+
return firstResult(supplier1, supplier2, null);
208+
}
209+
210+
private static <T> T firstResult(Supplier<? extends T> supplier1, Supplier<? extends T> supplier2, T defaultResult) {
211+
final T result1 = supplier1.get();
212+
if (result1 != null) {
213+
return result1;
214+
}
215+
final T result2 = supplier2.get();
216+
if (result2 != null) {
217+
return result2;
218+
}
219+
return defaultResult;
220+
}
221+
222+
private static <T> List<T> emptyToNull(List<T> list) {
223+
return list != null && !list.isEmpty() ? list : null;
224+
}
225+
118226
}

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/parser/SwaggerResponse.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66

77
public class SwaggerResponse {
8-
public int code;
8+
public String code;
99
public String comment;
1010
public Type responseType;
1111
}

typescript-generator-core/src/main/java/cz/habarta/typescript/generator/util/Utils.java

+12
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,18 @@ public static <T> T getAnnotationElementValue(Annotation annotation, String anno
212212
}
213213
}
214214

215+
public static List<Annotation> getRepeatableAnnotation(Annotation directAnnotation, Annotation containerAnnotation) {
216+
final List<Annotation> repeatableAnnotations = new ArrayList<>();
217+
if (directAnnotation != null) {
218+
repeatableAnnotations.add(directAnnotation);
219+
}
220+
if (containerAnnotation != null) {
221+
final Annotation[] annotations = Utils.getAnnotationElementValue(containerAnnotation, "value", Annotation[].class);
222+
Stream.of(annotations).forEach(repeatableAnnotations::add);
223+
}
224+
return repeatableAnnotations;
225+
}
226+
215227
public static Type replaceRawClassInType(Type type, Class<?> newClass) {
216228
if (type instanceof ParameterizedType) {
217229
final ParameterizedType parameterizedType = (ParameterizedType) type;

0 commit comments

Comments
 (0)