Skip to content

Commit 3638d0c

Browse files
committed
open-api: traverse mount/install instruction on lambda/script routes and documente all them
1 parent 946286c commit 3638d0c

7 files changed

Lines changed: 213 additions & 21 deletions

File tree

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/OperationExt.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ public void setController(ClassNode controller) {
211211
this.controller = controller;
212212
}
213213

214+
@Override
215+
public void setOperationId(String operationId) {
216+
super.setOperationId(operationId);
217+
}
218+
214219
@JsonIgnore
215220
public List<AnnotationNode> getAllAnnotations() {
216221
return Stream.of(

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/RouteParser.java

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,9 @@ public List<OperationExt> parse(ParserContext ctx, OpenAPIExt openapi) {
6565
Optional.ofNullable(ctx.getMainClass()).orElse(ctx.getRouter().getClassName());
6666
ClassNode application = ctx.classNode(Type.getObjectType(applicationName.replace(".", "/")));
6767

68-
// javadoc
69-
var javaDoc = ctx.javadoc().parse(ctx.getRouter().getClassName());
70-
for (OperationExt operation : operations) {
71-
// Script/lambda
72-
if (operation.getController() == null) {
73-
javaDoc
74-
.flatMap(doc -> doc.getScript(operation.getMethod(), operation.getPattern()))
75-
.ifPresent(
76-
scriptDoc -> {
77-
if (scriptDoc.getPath() != null) {
78-
JavaDocSetter.setPath(operation, scriptDoc.getPath());
79-
}
80-
JavaDocSetter.set(operation, scriptDoc);
81-
});
82-
}
83-
}
68+
// JavaDoc
69+
addJavaDoc(ctx, ctx.getRouter().getClassName(), "", operations);
70+
8471
// swagger/openapi:
8572
for (OperationExt operation : operations) {
8673
operation.setApplication(application);
@@ -116,6 +103,29 @@ public List<OperationExt> parse(ParserContext ctx, OpenAPIExt openapi) {
116103
return result;
117104
}
118105

106+
private static void addJavaDoc(
107+
ParserContext ctx, String className, String prefix, List<OperationExt> operations) {
108+
// javadoc
109+
var offset = prefix == null || prefix.isEmpty() ? 0 : prefix.length();
110+
var javaDoc = ctx.javadoc().parse(className);
111+
for (OperationExt operation : operations) {
112+
// Script/lambda
113+
if (operation.getController() == null) {
114+
javaDoc
115+
.flatMap(
116+
doc ->
117+
doc.getScript(operation.getMethod(), operation.getPattern().substring(offset)))
118+
.ifPresent(
119+
scriptDoc -> {
120+
if (scriptDoc.getPath() != null) {
121+
JavaDocSetter.setPath(operation, scriptDoc.getPath());
122+
}
123+
JavaDocSetter.set(operation, scriptDoc);
124+
});
125+
}
126+
}
127+
}
128+
119129
private void checkResponses(ParserContext ctx, OperationExt operation) {
120130
// checkResponse(ctx, operation, 200, operation.getDefaultResponse());
121131
for (Map.Entry<String, ApiResponse> entry : operation.getResponses().entrySet()) {
@@ -607,7 +617,9 @@ private List<OperationExt> mountRouter(
607617
throw new UnsupportedOperationException(InsnSupport.toString(node));
608618
}
609619
ClassNode classNode = ctx.classNode(router);
610-
return parse(ctx.newContext(router), prefix, classNode);
620+
var operations = parse(ctx.newContext(router), prefix, classNode);
621+
addJavaDoc(ctx, router.getClassName(), prefix, operations);
622+
return operations;
611623
}
612624

613625
private List<OperationExt> installApp(
@@ -632,7 +644,9 @@ private List<OperationExt> installApp(
632644
throw new UnsupportedOperationException(InsnSupport.toString(node));
633645
}
634646
ClassNode classNode = ctx.classNode(router);
635-
return parse(ctx.newContext(router), prefix, classNode);
647+
var operations = parse(ctx.newContext(router), prefix, classNode);
648+
addJavaDoc(ctx, router.getClassName(), prefix, operations);
649+
return operations;
636650
}
637651

638652
private Type kotlinSupplier(ParserContext ctx, MethodInsnNode node, AbstractInsnNode ins) {
@@ -896,7 +910,8 @@ private OperationExt newRouteDescriptor(
896910
node.name.equals("apply")
897911
|| node.name.equals("invoke")
898912
|| node.name.startsWith("invoke$")
899-
|| node.name.contains("$lambda");
913+
|| node.name.contains("$lambda")
914+
|| node.name.startsWith("fake$");
900915
if (notSynthetic && !lambda) {
901916
operation.setOperationId(node.name);
902917
}

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/javadoc/JavaDocParser.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public Map<String, ClassDoc> traverse(DetailAST tree) {
7979
}
8080
});
8181
// Script routes
82-
scripts(scope, classDoc, null, null, new HashSet<>());
82+
counter.addAndGet(scripts(scope, classDoc, null, null, new HashSet<>()));
8383

8484
if (counter.get() > 0) {
8585
classes.put(classDoc.getName(), classDoc);
@@ -88,8 +88,9 @@ public Map<String, ClassDoc> traverse(DetailAST tree) {
8888
return classes;
8989
}
9090

91-
private void scripts(
91+
private int scripts(
9292
DetailAST scope, ClassDoc classDoc, PathDoc pathDoc, String prefix, Set<DetailAST> visited) {
93+
var counter = new AtomicInteger(0);
9394
for (var script : tree(scope).filter(tokens(TokenTypes.METHOD_CALL)).toList()) {
9495
if (visited.add(script)) {
9596
// Test for HTTP method name
@@ -106,11 +107,17 @@ private void scripts(
106107
.filter(tokens(TokenTypes.BLOCK_COMMENT_BEGIN))
107108
.findFirst()
108109
.orElse(JavaDocNode.EMPTY_AST);
110+
if (scriptComment != JavaDocNode.EMPTY_AST) {
111+
counter.incrementAndGet();
112+
}
109113
if (Router.METHODS.contains(callName.toUpperCase())) {
110114
pathLiteral(script)
111115
.ifPresent(
112116
pattern -> {
113117
var resolvedComment = resolveScriptComment(classDoc, script, scriptComment);
118+
if (scriptComment != resolvedComment.comment) {
119+
counter.incrementAndGet();
120+
}
114121
var scriptDoc =
115122
new ScriptDoc(
116123
this,
@@ -138,6 +145,7 @@ private void scripts(
138145
}
139146
}
140147
}
148+
return counter.get();
141149
}
142150

143151
/**
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package issues.i3729.api;
7+
8+
import io.jooby.Context;
9+
import io.jooby.Jooby;
10+
11+
public class InstalledRouter extends Jooby {
12+
13+
{
14+
/*
15+
* Installed operation.
16+
*
17+
* @operationId installedOp
18+
*/
19+
get("/installed", Context::getRequestPath);
20+
21+
mount(new MountedRouter());
22+
}
23+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package issues.i3729.api;
7+
8+
import io.jooby.Context;
9+
import io.jooby.Jooby;
10+
11+
public class MountedApp extends Jooby {
12+
{
13+
/*
14+
* This is the main router.
15+
*/
16+
get("/main", Context::getRequestPath);
17+
18+
mount(new MountedRouter());
19+
20+
mount("/mount-point", new MountedRouter());
21+
22+
install(InstalledRouter::new);
23+
24+
install("/install-point", InstalledRouter::new);
25+
}
26+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package issues.i3729.api;
7+
8+
import static org.junit.jupiter.api.Assertions.assertEquals;
9+
10+
import io.jooby.openapi.OpenAPIResult;
11+
import io.jooby.openapi.OpenAPITest;
12+
13+
public class MountedDocTest {
14+
15+
@OpenAPITest(value = MountedApp.class)
16+
public void shouldGenerateDocFromMountedApp(OpenAPIResult result) {
17+
assertEquals(
18+
"openapi: 3.0.1\n"
19+
+ "info:\n"
20+
+ " title: Mounted API\n"
21+
+ " description: Mounted API description\n"
22+
+ " version: \"1.0\"\n"
23+
+ "paths:\n"
24+
+ " /main:\n"
25+
+ " get:\n"
26+
+ " summary: This is the main router.\n"
27+
+ " operationId: getMain\n"
28+
+ " responses:\n"
29+
+ " \"200\":\n"
30+
+ " description: Success\n"
31+
+ " content:\n"
32+
+ " application/json:\n"
33+
+ " schema:\n"
34+
+ " type: string\n"
35+
+ " /mounted:\n"
36+
+ " get:\n"
37+
+ " summary: Mounted operation.\n"
38+
+ " operationId: mountedOp3\n"
39+
+ " responses:\n"
40+
+ " \"200\":\n"
41+
+ " description: Success\n"
42+
+ " content:\n"
43+
+ " application/json:\n"
44+
+ " schema:\n"
45+
+ " type: string\n"
46+
+ " /mount-point/mounted:\n"
47+
+ " get:\n"
48+
+ " summary: Mounted operation.\n"
49+
+ " operationId: mountedOp2\n"
50+
+ " responses:\n"
51+
+ " \"200\":\n"
52+
+ " description: Success\n"
53+
+ " content:\n"
54+
+ " application/json:\n"
55+
+ " schema:\n"
56+
+ " type: string\n"
57+
+ " /installed:\n"
58+
+ " get:\n"
59+
+ " summary: Installed operation.\n"
60+
+ " operationId: installedOp\n"
61+
+ " responses:\n"
62+
+ " \"200\":\n"
63+
+ " description: Success\n"
64+
+ " content:\n"
65+
+ " application/json:\n"
66+
+ " schema:\n"
67+
+ " type: string\n"
68+
+ " /install-point/installed:\n"
69+
+ " get:\n"
70+
+ " summary: Installed operation.\n"
71+
+ " operationId: installedOp2\n"
72+
+ " responses:\n"
73+
+ " \"200\":\n"
74+
+ " description: Success\n"
75+
+ " content:\n"
76+
+ " application/json:\n"
77+
+ " schema:\n"
78+
+ " type: string\n"
79+
+ " /install-point/mounted:\n"
80+
+ " get:\n"
81+
+ " summary: Mounted operation.\n"
82+
+ " operationId: mountedOp4\n"
83+
+ " responses:\n"
84+
+ " \"200\":\n"
85+
+ " description: Success\n"
86+
+ " content:\n"
87+
+ " application/json:\n"
88+
+ " schema:\n"
89+
+ " type: string\n",
90+
result.toYaml());
91+
}
92+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Jooby https://jooby.io
3+
* Apache License Version 2.0 https://jooby.io/LICENSE.txt
4+
* Copyright 2014 Edgar Espina
5+
*/
6+
package issues.i3729.api;
7+
8+
import io.jooby.Context;
9+
import io.jooby.Jooby;
10+
11+
public class MountedRouter extends Jooby {
12+
13+
{
14+
get("/mounted", this::mountedOp);
15+
}
16+
17+
/*
18+
* Mounted operation.
19+
*/
20+
private String mountedOp(Context context) {
21+
return "";
22+
}
23+
}

0 commit comments

Comments
 (0)