Skip to content

Commit dcdddd1

Browse files
committed
API-Tool doesn`t generate correct swagger documentation for non-functional routing fix jooby-project#1261
1 parent 0eed398 commit dcdddd1

File tree

4 files changed

+102
-31
lines changed

4 files changed

+102
-31
lines changed

modules/jooby-apitool/src/main/java/org/jooby/internal/apitool/BytecodeRouteParser.java

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,13 @@
230230
import org.jooby.apitool.RouteParameter;
231231
import org.jooby.apitool.RouteResponse;
232232
import org.jooby.funzy.Try;
233+
233234
import static org.jooby.funzy.When.when;
235+
234236
import org.jooby.internal.RouteMetadata;
237+
235238
import static org.jooby.internal.apitool.Filters.access;
236239
import static org.jooby.internal.apitool.Filters.and;
237-
import static org.jooby.internal.apitool.Filters.call;
238240
import static org.jooby.internal.apitool.Filters.file;
239241
import static org.jooby.internal.apitool.Filters.getOrCreateKotlinClass;
240242
import static org.jooby.internal.apitool.Filters.is;
@@ -249,7 +251,9 @@
249251
import static org.jooby.internal.apitool.Filters.param;
250252
import static org.jooby.internal.apitool.Filters.path;
251253
import static org.jooby.internal.apitool.Filters.scriptRoute;
254+
import static org.jooby.internal.apitool.Filters.sendObject;
252255
import static org.jooby.internal.apitool.Filters.use;
256+
253257
import org.jooby.internal.mvc.MvcRoutes;
254258
import org.jooby.mvc.Body;
255259
import org.jooby.mvc.Flash;
@@ -259,8 +263,10 @@
259263
import org.objectweb.asm.ClassReader;
260264
import org.objectweb.asm.Handle;
261265
import org.objectweb.asm.Opcodes;
266+
262267
import static org.objectweb.asm.Opcodes.GETSTATIC;
263268
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
269+
264270
import org.objectweb.asm.Type;
265271
import org.objectweb.asm.tree.AbstractInsnNode;
266272
import org.objectweb.asm.tree.ClassNode;
@@ -408,7 +414,7 @@ public List<RouteMethod> parse(String classname) throws Exception {
408414

409415
java.lang.reflect.Type returnType;
410416
if (method.desc.endsWith("V")) {
411-
returnType = void.class;
417+
returnType = sendReturnType(loader, method);
412418
} else if (method.desc.endsWith(RETURN_OBJ)) {
413419
returnType = returnType(loader, method);
414420
} else {
@@ -1055,36 +1061,53 @@ private java.lang.reflect.Type returnType(final ClassLoader loader,
10551061
.filter(and(is(InsnNode.class), opcode(Opcodes.ARETURN)))
10561062
.findFirst()
10571063
.map(AbstractInsnNode::getPrevious)
1058-
.map(previous -> {
1059-
/** return 1; return true; return new Foo(); */
1060-
if (previous instanceof MethodInsnNode) {
1061-
MethodInsnNode minnsn = ((MethodInsnNode) previous);
1062-
if (minnsn.name.equals("<init>")) {
1063-
return loadType(loader, minnsn.owner);
1064-
}
1065-
String desc = minnsn.desc;
1066-
java.lang.reflect.Type type = TypeDescriptorParser.parse(loader, desc);
1067-
if (type.getTypeName().equals(Result.class.getName())) {
1068-
return new TypeWithStatus(type, statusCodeFor(minnsn));
1069-
}
1070-
return type;
1071-
}
1072-
/** return "String" | int | double */
1073-
if (previous instanceof LdcInsnNode) {
1074-
Object cst = ((LdcInsnNode) previous).cst;
1075-
if (cst instanceof Type) {
1076-
return TypeDescriptorParser.parse(loader, ((Type) cst).getDescriptor());
1077-
}
1078-
return cst.getClass();
1079-
}
1080-
/** return variable */
1081-
if (previous instanceof VarInsnNode) {
1082-
VarInsnNode varInsn = (VarInsnNode) previous;
1083-
return localVariable(loader, m, varInsn);
1084-
}
1064+
.map(previous -> handleReturnType(loader, m, previous))
1065+
.orElseGet(() -> sendReturnType(loader, m));
1066+
}
10851067

1086-
return Object.class;
1087-
}).orElse(Object.class);
1068+
private java.lang.reflect.Type handleReturnType(ClassLoader loader, MethodNode m,
1069+
AbstractInsnNode previous) {
1070+
/** return 1; return true; return new Foo(); */
1071+
if (previous instanceof MethodInsnNode) {
1072+
MethodInsnNode minnsn = ((MethodInsnNode) previous);
1073+
if (minnsn.name.equals("<init>")) {
1074+
return loadType(loader, minnsn.owner);
1075+
}
1076+
String desc = minnsn.desc;
1077+
java.lang.reflect.Type type = TypeDescriptorParser.parse(loader, desc);
1078+
if (type.getTypeName().equals(Result.class.getName())) {
1079+
return new TypeWithStatus(type, statusCodeFor(minnsn));
1080+
}
1081+
return type;
1082+
}
1083+
/** return "String" | int | double */
1084+
if (previous instanceof LdcInsnNode) {
1085+
Object cst = ((LdcInsnNode) previous).cst;
1086+
if (cst instanceof Type) {
1087+
return TypeDescriptorParser.parse(loader, ((Type) cst).getDescriptor());
1088+
}
1089+
return cst.getClass();
1090+
}
1091+
/** return variable */
1092+
if (previous instanceof VarInsnNode) {
1093+
VarInsnNode varInsn = (VarInsnNode) previous;
1094+
return localVariable(loader, m, varInsn);
1095+
}
1096+
1097+
return Object.class;
1098+
}
1099+
1100+
private java.lang.reflect.Type sendReturnType(ClassLoader loader, MethodNode m) {
1101+
return new Insns(m)
1102+
.last()
1103+
.prev()
1104+
.filter(MethodInsnNode.class::isInstance)
1105+
.map(MethodInsnNode.class::cast)
1106+
.filter(sendObject())
1107+
.findFirst()
1108+
.map(AbstractInsnNode::getPrevious)
1109+
.map(node -> handleReturnType(loader, m, node))
1110+
.orElse(void.class);
10881111
}
10891112

10901113
private Integer statusCodeFor(MethodInsnNode node) {

modules/jooby-apitool/src/main/java/org/jooby/internal/apitool/Filters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@
206206
import org.jooby.Jooby;
207207
import org.jooby.Mutant;
208208
import org.jooby.Request;
209+
import org.jooby.Response;
209210
import org.jooby.Route;
210211
import org.jooby.Router;
211212
import org.objectweb.asm.Opcodes;
@@ -303,6 +304,10 @@ public static Predicate<MethodNode> methodName(final String name) {
303304
return is(MethodNode.class).and(m -> m.name.equals(name));
304305
}
305306

307+
public static Predicate<MethodInsnNode> sendObject() {
308+
return call(Response.class, "send", Object.class.getName());
309+
}
310+
306311
@SuppressWarnings("rawtypes")
307312
public static Predicate<MethodNode> method(final String name, final Object... args) {
308313
return is(MethodNode.class).and(m -> {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package issues;
2+
3+
import kt.App1261;
4+
import org.jooby.apitool.ApiParser;
5+
import org.jooby.apitool.ApiToolFeature;
6+
import org.jooby.apitool.RouteMethod;
7+
import org.jooby.apitool.RouteMethodAssert;
8+
import org.junit.Test;
9+
10+
import java.util.List;
11+
12+
public class Issue1261 extends ApiToolFeature {
13+
14+
@Test
15+
public void shouldContainsSwaggerResponseDescription() throws Exception {
16+
List<RouteMethod> routes = new ApiParser(dir()).parseFully(new App1261());
17+
new RouteMethodAssert(routes)
18+
.next(r -> {
19+
r.returnType("kt.ResultData");
20+
r.method("GET");
21+
r.pattern("/");
22+
r.description(null);
23+
r.summary(null);
24+
r.returns(null);
25+
})
26+
.done();
27+
}
28+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package kt
2+
3+
import org.jooby.*
4+
5+
data class ResultData(
6+
val text: String,
7+
val number: Int
8+
)
9+
10+
class App1261 : Kooby({
11+
get("/") {req, rsp ->
12+
val response = ResultData("Test", 123)
13+
rsp.send(response)
14+
}
15+
})

0 commit comments

Comments
 (0)