Skip to content
This repository was archived by the owner on Mar 3, 2026. It is now read-only.

Commit 038dbbe

Browse files
committed
apitool: remove empty org.jooby.Result type present in swagger.json and api.raml fix jooby-project#944
1 parent 4f3c249 commit 038dbbe

File tree

6 files changed

+125
-11
lines changed

6 files changed

+125
-11
lines changed

modules/jooby-apitool/src/main/java/org/jooby/apitool/ApiParser.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ private RouteMethod complement(Route.Definition route, final RouteMethod method)
372372

373373
types.accept(route.consumes(), method::consumes);
374374
types.accept(route.produces(), method::produces);
375+
375376
return method;
376377
}
377378
}

modules/jooby-apitool/src/main/java/org/jooby/apitool/RouteResponse.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@
207207
import com.google.common.collect.ImmutableMap;
208208

209209
import java.lang.reflect.Type;
210+
import java.util.LinkedHashMap;
210211
import java.util.Map;
211212
import java.util.Objects;
212213
import java.util.Optional;
@@ -304,7 +305,12 @@ public int statusCode() {
304305
* @return This response.
305306
*/
306307
public RouteResponse status(final Map<Integer, String> status) {
307-
this.status = status;
308+
if (status != null) {
309+
if (this.status == null) {
310+
this.status = new LinkedHashMap<>();
311+
this.status.putAll(status);
312+
}
313+
}
308314
return this;
309315
}
310316

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

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@
207207
import com.fasterxml.jackson.annotation.JsonInclude;
208208
import com.fasterxml.jackson.databind.ObjectMapper;
209209
import com.fasterxml.jackson.databind.module.SimpleModule;
210+
import com.google.common.collect.ImmutableMap;
210211
import com.google.common.collect.ImmutableSet;
211212
import com.google.common.io.ByteStreams;
212213
import com.google.common.primitives.Primitives;
@@ -218,8 +219,10 @@
218219
import org.jooby.Jooby;
219220
import org.jooby.Request;
220221
import org.jooby.Response;
222+
import org.jooby.Result;
221223
import org.jooby.Route;
222224
import org.jooby.Session;
225+
import org.jooby.Status;
223226
import org.jooby.Upload;
224227
import org.jooby.apitool.RouteMethod;
225228
import org.jooby.apitool.RouteParameter;
@@ -409,9 +412,22 @@ public List<RouteMethod> parse(String classname) throws Exception {
409412
returnType = TypeDescriptorParser.parse(loader,
410413
Optional.ofNullable(method.signature).orElse(method.desc));
411414
}
415+
Integer status;
416+
if (returnType instanceof TypeWithStatus) {
417+
status = ((TypeWithStatus) returnType).status;
418+
returnType = ((TypeWithStatus) returnType).type();
419+
} else {
420+
status = null;
421+
}
412422
List<RouteParameter> parameters = params(loader, owner, lambda.pattern, method);
423+
RouteResponse routeResponse = new RouteResponse(simplifyType(returnType));
424+
if (status != null) {
425+
routeResponse.status(ImmutableMap.of(status,
426+
Try.apply(() -> Status.valueOf(status.intValue()).reason())
427+
.orElse(status.toString())));
428+
}
413429
RouteMethod route = new RouteMethod(lambda.name, lambda.pattern,
414-
new RouteResponse(simplifyType(returnType))).parameters(parameters);
430+
routeResponse).parameters(parameters);
415431
javadoc(route, javadoc.pop(lambda.declaringClass, lambda.name, lambda.pattern));
416432
methods.add(route);
417433
} else {
@@ -984,12 +1000,16 @@ private java.lang.reflect.Type returnType(final ClassLoader loader,
9841000
.map(previous -> {
9851001
/** return 1; return true; return new Foo(); */
9861002
if (previous instanceof MethodInsnNode) {
987-
MethodInsnNode methodNode = ((MethodInsnNode) previous);
988-
if (methodNode.name.equals("<init>")) {
989-
return loadType(loader, methodNode.owner);
1003+
MethodInsnNode minnsn = ((MethodInsnNode) previous);
1004+
if (minnsn.name.equals("<init>")) {
1005+
return loadType(loader, minnsn.owner);
9901006
}
991-
String desc = methodNode.desc;
992-
return TypeDescriptorParser.parse(loader, desc);
1007+
String desc = minnsn.desc;
1008+
java.lang.reflect.Type type = TypeDescriptorParser.parse(loader, desc);
1009+
if (type.getTypeName().equals(Result.class.getName())) {
1010+
return new TypeWithStatus(type, statusCodeFor(minnsn));
1011+
}
1012+
return type;
9931013
}
9941014
/** return "String" | int | double */
9951015
if (previous instanceof LdcInsnNode) {
@@ -1009,6 +1029,28 @@ private java.lang.reflect.Type returnType(final ClassLoader loader,
10091029
}).orElse(Object.class);
10101030
}
10111031

1032+
private Integer statusCodeFor(MethodInsnNode node) {
1033+
if (node.name.equals("noContent")) {
1034+
return 204;
1035+
}
1036+
if (node.name.equals("with")) {
1037+
AbstractInsnNode previous = node.getPrevious();
1038+
if (previous instanceof IntInsnNode) {
1039+
return ((IntInsnNode) previous).operand;
1040+
}
1041+
if (previous instanceof FieldInsnNode) {
1042+
String owner = ((FieldInsnNode) previous).owner.replace("/", ".");
1043+
if (owner.equals(Status.class.getName())) {
1044+
String statusName = ((FieldInsnNode) previous).name;
1045+
return Try.apply(() -> Status.class.getDeclaredField(statusName).get(null))
1046+
.map(it -> ((Status) it).value())
1047+
.orElse(null);
1048+
}
1049+
}
1050+
}
1051+
return null;
1052+
}
1053+
10121054
@SuppressWarnings("unchecked")
10131055
static java.lang.reflect.Type localVariable(final ClassLoader loader, final MethodNode m,
10141056
final VarInsnNode varInsn) {
@@ -1058,7 +1100,7 @@ public void write(final char[] cbuf, final int off, final int len) throws IOExce
10581100

10591101
@Override
10601102
public void flush() throws IOException {
1061-
log.debug("{}:\n{}", owner, buff);
1103+
log.info("{}:\n{}", owner, buff);
10621104
}
10631105

10641106
@Override
@@ -1148,4 +1190,25 @@ private static RouteParameter toRouteParameter(String pattern, final Parameter p
11481190
return new RouteParameter(pname, kind.get(), p.getParameterizedType(), null);
11491191
}
11501192

1193+
private static class TypeWithStatus implements java.lang.reflect.Type {
1194+
private final java.lang.reflect.Type forwarding;
1195+
final Integer status;
1196+
1197+
TypeWithStatus(java.lang.reflect.Type forwarding, Integer status) {
1198+
this.forwarding = forwarding;
1199+
this.status = status;
1200+
}
1201+
1202+
public java.lang.reflect.Type type() {
1203+
if (status != null && status == 204 && forwarding.getTypeName().equals(Result.class.getName())) {
1204+
return void.class;
1205+
}
1206+
return forwarding;
1207+
}
1208+
1209+
@Override public String getTypeName() {
1210+
return forwarding.getTypeName();
1211+
}
1212+
}
1213+
11511214
}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,11 @@ public Swagger build(Swagger base, final List<RouteMethod> routes) throws Except
383383
Response response = new Response();
384384
String doc = returns.description().orElse(status.get(statusCode));
385385
response.description(doc);
386-
// make sure type definition gets in
387-
modelFactory.apply(returns.type());
388-
response.schema(converter.readAsProperty(returns.type()));
386+
if (!"void".equals(returns.type().getTypeName())) {
387+
// make sure type definition gets in
388+
modelFactory.apply(returns.type());
389+
response.schema(converter.readAsProperty(returns.type()));
390+
}
389391
op.addResponse(statusCode.toString(), response);
390392
status.entrySet().stream()
391393
.filter(it -> !statusCode.equals(it.getKey()))
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package apps;
2+
3+
import org.jooby.Jooby;
4+
import org.jooby.Results;
5+
6+
public class App944 extends Jooby {
7+
{
8+
delete("/", () -> {
9+
return Results.noContent();
10+
});
11+
}
12+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.jooby.apitool;
2+
3+
import apps.App944;
4+
import org.junit.Test;
5+
6+
import java.nio.file.Path;
7+
import java.nio.file.Paths;
8+
9+
public class Issue944 {
10+
11+
@Test
12+
public void shouldSkipEmptyResultType() throws Exception {
13+
new RouteMethodAssert(new ApiParser(dir()).parseFully(new App944()))
14+
.next(r -> {
15+
r.returnType(void.class);
16+
r.pattern("/");
17+
r.description(null);
18+
r.summary(null);
19+
r.status(204, "No Content");
20+
}).done();
21+
}
22+
23+
private Path dir() {
24+
Path userdir = Paths.get(System.getProperty("user.dir"));
25+
if (!userdir.toString().endsWith("jooby-apitool")) {
26+
userdir = userdir.resolve("modules").resolve("jooby-apitool");
27+
}
28+
return userdir;
29+
}
30+
}

0 commit comments

Comments
 (0)