Skip to content

Commit 2e0e982

Browse files
committed
routes: add route summary
- routes/operations - add support for fn alias - fix error lookup to support expression like `error(400)`
1 parent 567e7b4 commit 2e0e982

File tree

6 files changed

+202
-18
lines changed

6 files changed

+202
-18
lines changed

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/asciidoc/AsciiDocContext.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ public Map<String, Object> getGlobalVariables() {
121121
openapiRoot.put("openapi", context.openapi);
122122
openapiRoot.put("now", context.now);
123123

124+
// Global/Default values:
124125
openapiRoot.put(
125126
"error",
126127
Map.of(
@@ -130,6 +131,14 @@ public Map<String, Object> getGlobalVariables() {
130131
"{{statusCode.reason}}",
131132
"message",
132133
"..."));
134+
// Routes
135+
var operations =
136+
context.openapi.getOperations().stream()
137+
.map(op -> new HttpRequest(context, op, Map.of()))
138+
.toList();
139+
// so we can print routes without calling function: routes() vs routes
140+
openapiRoot.put("routes", operations);
141+
openapiRoot.put("operations", operations);
133142

134143
// make in to work without literal
135144
openapiRoot.put("query", "query");
@@ -144,7 +153,8 @@ public Map<String, Object> getGlobalVariables() {
144153
@Override
145154
public Map<String, Function> getFunctions() {
146155
return Stream.of(Lookup.values())
147-
.collect(Collectors.toMap(Enum::name, it -> wrapFn(it)));
156+
.flatMap(it -> it.alias().stream().map(name -> Map.entry(name, it)))
157+
.collect(Collectors.toMap(Map.Entry::getKey, it -> wrapFn(it.getValue())));
148158
}
149159

150160
private static Function wrapFn(Lookup lookup) {

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/asciidoc/Display.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,8 @@ protected ToAsciiDoc toAsciidoc(AsciiDocContext context, Object input) {
183183
protected Object toJson(AsciiDocContext context, Object input) {
184184
return switch (input) {
185185
case Schema<?> schema -> context.schemaProperties(schema);
186-
case StatusCodeList codeList -> codeList.codes();
186+
case StatusCodeList codeList ->
187+
codeList.codes().size() == 1 ? codeList.codes().getFirst() : codeList.codes();
187188
default -> input;
188189
};
189190
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
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 io.jooby.internal.openapi.asciidoc;
7+
8+
import java.util.Iterator;
9+
import java.util.List;
10+
import java.util.Map;
11+
import java.util.Optional;
12+
13+
import com.fasterxml.jackson.annotation.JsonIncludeProperties;
14+
import edu.umd.cs.findbugs.annotations.NonNull;
15+
16+
@JsonIncludeProperties({"operations"})
17+
public record HttpRequestList(AsciiDocContext context, List<HttpRequest> operations)
18+
implements Iterable<HttpRequest>, ToAsciiDoc {
19+
@NonNull @Override
20+
public Iterator<HttpRequest> iterator() {
21+
return operations.iterator();
22+
}
23+
24+
@NonNull @Override
25+
public String toString() {
26+
return operations.toString();
27+
}
28+
29+
@Override
30+
public String list(Map<String, Object> options) {
31+
var sb = new StringBuilder();
32+
operations.forEach(
33+
op ->
34+
sb.append("* `+")
35+
.append(op)
36+
.append("+`")
37+
.append(
38+
Optional.ofNullable(op.getSummary()).map(summary -> ": " + summary).orElse(""))
39+
.append('\n'));
40+
if (!sb.isEmpty()) {
41+
sb.setLength(sb.length() - 1);
42+
}
43+
return sb.toString();
44+
}
45+
46+
@Override
47+
public String table(Map<String, Object> options) {
48+
var sb = new StringBuilder();
49+
sb.append("[cols=\"1,1,3a\", options=\"header\"]").append('\n');
50+
sb.append("|===").append('\n');
51+
sb.append("|").append("Method|Path|Summary").append("\n\n");
52+
operations.forEach(
53+
op ->
54+
sb.append("|`+")
55+
.append(op.getMethod())
56+
.append("+`\n")
57+
.append("|`+")
58+
.append(op.getPath())
59+
.append("+`\n")
60+
.append("|")
61+
.append(Optional.ofNullable(op.operation().getSummary()).orElse(""))
62+
.append("\n\n"));
63+
if (!sb.isEmpty()) {
64+
sb.append("\n");
65+
sb.setLength(sb.length() - 1);
66+
}
67+
sb.append("|===");
68+
return sb.toString();
69+
}
70+
}

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/asciidoc/Lookup.java

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,17 +114,10 @@ public Object execute(
114114
public List<String> getArgumentNames() {
115115
return List.of("path");
116116
}
117-
},
118-
model {
119-
@Override
120-
public Object execute(
121-
Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
122-
return schema.execute(args, self, context, lineNumber);
123-
}
124117

125118
@Override
126-
public List<String> getArgumentNames() {
127-
return schema.getArgumentNames();
119+
public List<String> alias() {
120+
return List.of("schema", "model");
128121
}
129122
},
130123
tag {
@@ -154,7 +147,35 @@ public Object execute(
154147

155148
@Override
156149
public List<String> getArgumentNames() {
157-
return List.of();
150+
return List.of("code");
151+
}
152+
},
153+
routes {
154+
@Override
155+
public Object execute(
156+
Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
157+
var asciidoc = AsciiDocContext.from(context);
158+
var operations = asciidoc.getOpenApi().getOperations();
159+
var list =
160+
operations.stream()
161+
.filter(
162+
it -> {
163+
var includes = (String) args.get("includes");
164+
return includes == null || it.getPath().matches(includes);
165+
})
166+
.map(it -> new HttpRequest(asciidoc, it, args))
167+
.toList();
168+
return new HttpRequestList(asciidoc, list);
169+
}
170+
171+
@Override
172+
public List<String> getArgumentNames() {
173+
return List.of("includes");
174+
}
175+
176+
@Override
177+
public List<String> alias() {
178+
return List.of("routes", "operations");
158179
}
159180
},
160181
statusCode {
@@ -176,7 +197,7 @@ public Object execute(
176197
return Stream.of(map);
177198
} else if (candidate instanceof Map<?, ?> codeMap) {
178199
var codes = new ArrayList<Map<String, Object>>();
179-
for (var entry : codeMap.entrySet()) {
200+
for (var entry : new TreeMap<>(codeMap).entrySet()) {
180201
var value = entry.getKey();
181202
if (value instanceof Number code) {
182203
Map<String, Object> map = new LinkedHashMap<>();
@@ -227,6 +248,10 @@ public List<String> getArgumentNames() {
227248
}
228249
};
229250

251+
public List<String> alias() {
252+
return List.of(name());
253+
}
254+
230255
protected Map<String, Object> appendMethod(Map<String, Object> args) {
231256
Map<String, Object> result = new LinkedHashMap<>(args);
232257
result.put("method", name());

modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/asciidoc/StatusCodeList.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
import java.util.List;
1010
import java.util.Map;
1111

12+
import com.fasterxml.jackson.annotation.JsonIncludeProperties;
1213
import edu.umd.cs.findbugs.annotations.NonNull;
1314
import io.jooby.internal.openapi.asciidoc.display.MapToAsciiDoc;
1415

16+
@JsonIncludeProperties({"codes"})
1517
public record StatusCodeList(List<Map<String, Object>> codes)
1618
implements Iterable<Map<String, Object>>, ToAsciiDoc {
1719
@NonNull @Override

modules/jooby-openapi/src/test/java/issues/i3820/PebbleSupportTest.java

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,82 @@
2121

2222
public class PebbleSupportTest {
2323

24+
@OpenAPITest(value = AppLib.class)
25+
public void routes(OpenAPIExt openapi) throws IOException {
26+
var templates = new PebbleTemplateSupport(CurrentDir.testClass(getClass(), "adoc"), openapi);
27+
// default error map
28+
templates
29+
.evaluateThat("{{ routes }}")
30+
.isEqualTo(
31+
"[GET /library/books/{isbn}, GET /library/search, GET /library/books, POST"
32+
+ " /library/books, POST /library/authors]");
33+
templates
34+
.evaluateThat("{{ routes(\"/library/books/?.*\") }}")
35+
.isEqualTo("[GET /library/books/{isbn}, GET /library/books, POST /library/books]");
36+
templates.evaluate("{{ routes | json(false) }}", output -> Json31.mapper().readTree(output));
37+
38+
templates
39+
.evaluateThat("{{ routes(\"/library/books/?.*\") | table }}")
40+
.isEqualToIgnoringNewLines(
41+
"""
42+
[cols="1,1,3a", options="header"]
43+
|===
44+
|Method|Path|Summary
45+
46+
|`+GET+`
47+
|`+/library/books/{isbn}+`
48+
|Get Specific Book Details
49+
50+
|`+GET+`
51+
|`+/library/books+`
52+
|Browse Books (Paginated)
53+
54+
|`+POST+`
55+
|`+/library/books+`
56+
|Add New Book
57+
58+
|===\
59+
""");
60+
61+
templates
62+
.evaluateThat("{{ routes(\"/library/books/?.*\") | list }}")
63+
.isEqualToIgnoringNewLines(
64+
"""
65+
* `+GET /library/books/{isbn}+`: Get Specific Book Details
66+
* `+GET /library/books+`: Browse Books (Paginated)
67+
* `+POST /library/books+`: Add New Book\
68+
""");
69+
}
70+
2471
@OpenAPITest(value = AppLib.class)
2572
public void statusCode(OpenAPIExt openapi) throws IOException {
2673
var templates = new PebbleTemplateSupport(CurrentDir.testClass(getClass(), "adoc"), openapi);
2774
// default error map
2875
templates.evaluateThat("{{ statusCode(200) }}").isEqualTo("[{code=200, reason=Success}]");
2976

77+
templates
78+
.evaluateThat("{{ statusCode(200) | json }}")
79+
.isEqualToIgnoringNewLines(
80+
"""
81+
[source, json]
82+
----
83+
{
84+
"code" : 200,
85+
"reason" : "Success"
86+
}
87+
----\
88+
""");
89+
3090
templates
3191
.evaluateThat("{{ statusCode(200) | list }}")
32-
.isEqualTo(
92+
.isEqualToIgnoringNewLines(
3393
"""
3494
* `+200+`: Success\
3595
""");
3696

3797
templates
3898
.evaluateThat("{{ statusCode(200) | table }}")
39-
.isEqualTo(
99+
.isEqualToIgnoringNewLines(
40100
"""
41101
|===
42102
|code|reason
@@ -49,7 +109,7 @@ public void statusCode(OpenAPIExt openapi) throws IOException {
49109

50110
templates
51111
.evaluateThat("{{ statusCode([200, 201]) | table }}")
52-
.isEqualTo(
112+
.isEqualToIgnoringNewLines(
53113
"""
54114
|===
55115
|code|reason
@@ -65,15 +125,15 @@ public void statusCode(OpenAPIExt openapi) throws IOException {
65125

66126
templates
67127
.evaluateThat("{{ statusCode([200, 201]) | list }}")
68-
.isEqualTo(
128+
.isEqualToIgnoringNewLines(
69129
"""
70130
* `+200+`: Success
71131
* `+201+`: Created\
72132
""");
73133

74134
templates
75135
.evaluateThat("{{ statusCode({200: \"OK\", 500: \"Internal Server Error\"}) | list }}")
76-
.isEqualTo(
136+
.isEqualToIgnoringNewLines(
77137
"""
78138
* `+200+`: OK
79139
* `+500+`: Internal Server Error\
@@ -142,6 +202,22 @@ public void shouldSupportJsonSchemaInV31(OpenAPIExt openapi) throws IOException
142202
@OpenAPITest(value = AppLib.class)
143203
public void errorMap(OpenAPIExt openapi) throws IOException {
144204
var templates = new PebbleTemplateSupport(CurrentDir.testClass(getClass(), "adoc"), openapi);
205+
templates
206+
.evaluateThat(
207+
"""
208+
{{ error(400) | json }}
209+
""")
210+
.isEqualToIgnoringNewLines(
211+
"""
212+
[source, json]
213+
----
214+
{
215+
"message" : "...",
216+
"reason" : "Bad Request",
217+
"statusCode" : 400
218+
}
219+
----\
220+
""");
145221
// default error map
146222
templates
147223
.evaluateThat(

0 commit comments

Comments
 (0)