Skip to content

Commit 2fb853d

Browse files
committed
openapi: asciidoc output #3820
- add more snippets - code clean up
1 parent db3bc07 commit 2fb853d

30 files changed

+577
-166
lines changed

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

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public static String generate(OpenAPIExt openAPI, Path index) throws IOException
3838
var writer = new StringWriter();
3939
var context = new HashMap<String, Object>();
4040
template.evaluate(writer, context);
41-
return writer.toString();
41+
return writer.toString().trim();
4242
}
4343

4444
@SuppressWarnings("unchecked")
@@ -52,19 +52,10 @@ private static PebbleEngine newEngine(OpenAPIExt openapi, Path baseDir) {
5252
.map(Server::getUrl)
5353
.orElse("");
5454
var openapiRoot = json.convertValue(openapi, Map.class);
55+
openapiRoot.put("openapi", openapi);
5556
openapiRoot.put(
5657
"internal",
57-
Map.of(
58-
"openapi",
59-
openapi,
60-
"resolver",
61-
snippetResolver,
62-
"serverUrl",
63-
serverUrl,
64-
"json",
65-
json,
66-
"yaml",
67-
yaml));
58+
Map.of("resolver", snippetResolver, "serverUrl", serverUrl, "json", json, "yaml", yaml));
6859
var engine = newEngine(new OpenApiSupport(openapiRoot));
6960
snippetResolver.setEngine(engine);
7061
return engine;

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ public Object apply(
3232
throws PebbleException {
3333
try {
3434
var json = InternalContext.json(context);
35-
return json.writer().withDefaultPrettyPrinter().writeValueAsString(input);
35+
return "[source,json]\n----\n"
36+
+ json.writer().withDefaultPrettyPrinter().writeValueAsString(input)
37+
+ "\n----";
3638
} catch (JsonProcessingException e) {
3739
throw new PebbleException(
3840
e, "Could not convert to JSON: " + input, lineNumber, self.getName());
@@ -56,7 +58,9 @@ public Object apply(
5658
throws PebbleException {
5759
try {
5860
var yaml = InternalContext.yaml(context);
59-
return yaml.writer().withDefaultPrettyPrinter().writeValueAsString(input);
61+
return "[source,yaml]\n----\n"
62+
+ yaml.writer().withDefaultPrettyPrinter().writeValueAsString(input)
63+
+ "----";
6064
} catch (JsonProcessingException e) {
6165
throw new PebbleException(
6266
e, "Could not convert to YAML: " + input, lineNumber, self.getName());
@@ -70,7 +74,7 @@ public static Map<String, Filter> allFilters() {
7074
functions.put(value.name(), value);
7175
}
7276
for (var value : OperationFilters.values()) {
73-
functions.put(value.id(), value);
77+
functions.put(value.name(), value);
7478
}
7579
return functions;
7680
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public static <T> T variable(EvaluationContext context, String name) {
2424
}
2525

2626
public static OpenAPIExt openApi(EvaluationContext context) {
27-
return internal(context, "openapi");
27+
return (OpenAPIExt) context.getVariable("openapi");
2828
}
2929

3030
public static SnippetResolver resolver(EvaluationContext context) {

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

Lines changed: 182 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@
77

88
import java.util.*;
99
import java.util.concurrent.atomic.AtomicInteger;
10+
import java.util.function.Function;
1011
import java.util.stream.Collectors;
1112
import java.util.stream.Stream;
1213

14+
import com.google.common.base.CaseFormat;
15+
import com.google.common.base.Predicate;
1316
import com.google.common.base.Splitter;
1417
import com.google.common.collect.*;
1518
import edu.umd.cs.findbugs.annotations.NonNull;
19+
import edu.umd.cs.findbugs.annotations.Nullable;
1620
import io.jooby.MediaType;
1721
import io.jooby.StatusCode;
1822
import io.jooby.internal.openapi.OperationExt;
@@ -160,10 +164,26 @@ protected Object doApply(
160164
snippetContext.put("headers", snippetContext.get("requestHeaders"));
161165
return resolver.apply(id(), snippetContext);
162166
}
163-
167+
},
168+
requestFields {
164169
@Override
165-
protected String id() {
166-
return "http-request";
170+
protected Object doApply(
171+
SnippetResolver resolver,
172+
OperationExt operation,
173+
Map<String, Object> snippetContext,
174+
Map<String, Object> args,
175+
PebbleTemplate self,
176+
EvaluationContext context,
177+
int lineNumber)
178+
throws Exception {
179+
/* Body */
180+
List<Map<String, String>> fields = List.of();
181+
if (operation.getRequestBody() != null) {
182+
var schema = schema(context, operation.getRequestBody());
183+
fields = schemaToTable(schema);
184+
}
185+
snippetContext.put("fields", fields);
186+
return resolver.apply(id(), snippetContext);
167187
}
168188
},
169189
httpResponse {
@@ -180,14 +200,8 @@ protected Object doApply(
180200
/* Body */
181201
var requestBodyString = "";
182202
var statusCode = findStatusCode(args);
183-
ResponseExt response;
184-
if (statusCode != null) {
185-
response = (ResponseExt) operation.getResponses().get(Integer.toString(statusCode.value()));
186-
if (response == null) {
187-
throw new IllegalArgumentException("No response: " + statusCode.value());
188-
}
189-
} else {
190-
response = operation.getDefaultResponse();
203+
var response = responseByStatusCode(operation, statusCode);
204+
if (statusCode == null) {
191205
statusCode = StatusCode.valueOf(Integer.parseInt(response.getCode()));
192206
}
193207
var json = InternalContext.json(context);
@@ -201,10 +215,100 @@ protected Object doApply(
201215
snippetContext.put("headers", snippetContext.get("responseHeaders"));
202216
return resolver.apply(id(), snippetContext);
203217
}
204-
218+
},
219+
responseFields {
220+
@Override
221+
protected Object doApply(
222+
SnippetResolver resolver,
223+
OperationExt operation,
224+
Map<String, Object> snippetContext,
225+
Map<String, Object> args,
226+
PebbleTemplate self,
227+
EvaluationContext context,
228+
int lineNumber)
229+
throws Exception {
230+
/* Body */
231+
var statusCode = findStatusCode(args);
232+
var response = responseByStatusCode(operation, statusCode);
233+
var schema = schema(context, response);
234+
snippetContext.put("fields", schemaToTable(schema));
235+
return resolver.apply(id(), snippetContext);
236+
}
237+
},
238+
formParameters {
239+
@Override
240+
protected Object doApply(
241+
SnippetResolver resolver,
242+
OperationExt operation,
243+
Map<String, Object> snippetContext,
244+
Map<String, Object> args,
245+
PebbleTemplate self,
246+
EvaluationContext context,
247+
int lineNumber)
248+
throws Exception {
249+
snippetContext.put("parameters", parametersToTable(operation, p -> "form".equals(p.getIn())));
250+
return resolver.apply(id(), snippetContext);
251+
}
252+
},
253+
queryParameters {
254+
@Override
255+
protected Object doApply(
256+
SnippetResolver resolver,
257+
OperationExt operation,
258+
Map<String, Object> snippetContext,
259+
Map<String, Object> args,
260+
PebbleTemplate self,
261+
EvaluationContext context,
262+
int lineNumber)
263+
throws Exception {
264+
snippetContext.put(
265+
"parameters", parametersToTable(operation, p -> "query".equals(p.getIn())));
266+
return resolver.apply(id(), snippetContext);
267+
}
268+
},
269+
pathParameters {
205270
@Override
206-
protected String id() {
207-
return "http-response";
271+
protected Object doApply(
272+
SnippetResolver resolver,
273+
OperationExt operation,
274+
Map<String, Object> snippetContext,
275+
Map<String, Object> args,
276+
PebbleTemplate self,
277+
EvaluationContext context,
278+
int lineNumber)
279+
throws Exception {
280+
snippetContext.put("parameters", parametersToTable(operation, p -> "path".equals(p.getIn())));
281+
return resolver.apply(id(), snippetContext);
282+
}
283+
},
284+
cookieParameters {
285+
@Override
286+
protected Object doApply(
287+
SnippetResolver resolver,
288+
OperationExt operation,
289+
Map<String, Object> snippetContext,
290+
Map<String, Object> args,
291+
PebbleTemplate self,
292+
EvaluationContext context,
293+
int lineNumber)
294+
throws Exception {
295+
snippetContext.put("cookies", parametersToTable(operation, p -> "cookie".equals(p.getIn())));
296+
return resolver.apply(id(), snippetContext);
297+
}
298+
},
299+
requestParameters {
300+
@Override
301+
protected Object doApply(
302+
SnippetResolver resolver,
303+
OperationExt operation,
304+
Map<String, Object> snippetContext,
305+
Map<String, Object> args,
306+
PebbleTemplate self,
307+
EvaluationContext context,
308+
int lineNumber)
309+
throws Exception {
310+
snippetContext.put("parameters", parametersToTable(operation, p -> true));
311+
return resolver.apply(id(), snippetContext);
208312
}
209313
},
210314
schema {
@@ -256,7 +360,7 @@ protected Object doApply(
256360
int lineNumber)
257361
throws Exception {
258362
var statusCode = findStatusCode(args);
259-
var response = operation.getResponses().get(Integer.toString(statusCode.value()));
363+
var response = responseByStatusCode(operation, statusCode, null);
260364
if (response == null) {
261365
throw new IllegalArgumentException("No response for: " + statusCode);
262366
}
@@ -285,8 +389,8 @@ protected Object doApply(
285389
}
286390
};
287391

288-
protected String id() {
289-
return name();
392+
protected final String id() {
393+
return CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_HYPHEN, name());
290394
}
291395

292396
@Override
@@ -329,6 +433,25 @@ public Object apply(
329433
}
330434
}
331435

436+
protected ResponseExt responseByStatusCode(
437+
OperationExt operation, @Nullable StatusCode statusCode) {
438+
return responseByStatusCode(operation, statusCode, operation.getDefaultResponse());
439+
}
440+
441+
protected ResponseExt responseByStatusCode(
442+
OperationExt operation, @Nullable StatusCode statusCode, ResponseExt defaultResponse) {
443+
ResponseExt response;
444+
if (statusCode != null) {
445+
response = (ResponseExt) operation.getResponses().get(Integer.toString(statusCode.value()));
446+
if (response == null) {
447+
throw new IllegalArgumentException("No response: " + statusCode.value());
448+
}
449+
} else {
450+
response = defaultResponse;
451+
}
452+
return response;
453+
}
454+
332455
protected Map<String, Object> newSnippetContext(String serverUrl, OperationExt operation) {
333456
Map<String, Object> map = new HashMap<>();
334457
map.put("pattern", operation.getPattern());
@@ -353,8 +476,10 @@ protected Map<String, Object> newSnippetContext(String serverUrl, OperationExt o
353476
operation.getConsumes().forEach(value -> requestHeaders.put("Content-Type", value));
354477
}
355478

356-
map.put("requestHeaders", requestHeaders.entries());
357-
map.put("responseHeaders", responseHeaders.entries());
479+
Function<Map.Entry<String, String>, Map<String, String>> mapper =
480+
e -> Map.of("name", e.getKey(), "value", e.getValue());
481+
map.put("requestHeaders", requestHeaders.entries().stream().map(mapper).toList());
482+
map.put("responseHeaders", responseHeaders.entries().stream().map(mapper).toList());
358483
return map;
359484
}
360485

@@ -371,6 +496,44 @@ protected StatusCode findStatusCode(Map<String, Object> args) {
371496
return null;
372497
}
373498

499+
protected List<Map<String, String>> schemaToTable(Schema<?> schema) {
500+
List<Map<String, String>> fields = new ArrayList<>();
501+
SchemaData.from(schema)
502+
.forEach(
503+
(name, type) -> {
504+
var field = new LinkedHashMap<String, String>();
505+
field.put("name", name);
506+
field.put("type", type.toString());
507+
var property = schema.getProperties().get(name);
508+
if (property != null) {
509+
field.put("description", property.getDescription());
510+
}
511+
fields.add(field);
512+
});
513+
return fields;
514+
}
515+
516+
protected List<Map<String, String>> parametersToTable(
517+
OperationExt operation, Predicate<Parameter> predicate) {
518+
List<Map<String, String>> fields = new ArrayList<>();
519+
var parameters =
520+
Optional.ofNullable(operation.getParameters()).orElse(List.of()).stream()
521+
.filter(predicate)
522+
.sorted(Comparator.comparing(Parameter::getName))
523+
.toList();
524+
parameters.forEach(
525+
it -> {
526+
var schema = it.getSchema();
527+
var field = new LinkedHashMap<String, String>();
528+
field.put("name", it.getName());
529+
field.put("type", SchemaData.shemaType(schema));
530+
field.put("description", it.getDescription());
531+
field.put("in", it.getIn());
532+
fields.add(field);
533+
});
534+
return fields;
535+
}
536+
374537
protected Schema<?> schema(EvaluationContext context, Object input) {
375538
var schema =
376539
switch (input) {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
|===
2+
|Parameter|Description
3+
{% for cookie in cookies %}
4+
|`+${cookie.name}+`
5+
|${cookie.description}
6+
{% endfor %}
7+
|===
Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
|===
2-
|Parameter|Description
3-
4-
{{#parameters}}
5-
|{{#tableCellContent}}`+{{name}}+`{{/tableCellContent}}
6-
|{{#tableCellContent}}{{description}}{{/tableCellContent}}
7-
8-
{{/parameters}}
9-
|===
2+
|Parameter|Type|Description
3+
{% for parameter in parameters %}
4+
|`+${parameter.name}+`
5+
|`+${parameter.type}+`
6+
|${parameter.description}
7+
{% endfor %}
8+
|===

modules/jooby-openapi/src/main/resources/io/jooby/openapi/templates/asciidoc/default-http-request.snippet

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
----
33
${method} ${path} HTTP/1.1
44
{% for h in headers -%}
5-
${h.key}: ${h.value}
5+
${h.name}: ${h.value}
66
{% endfor -%}
77
${requestBody -}
88
----

modules/jooby-openapi/src/main/resources/io/jooby/openapi/templates/asciidoc/default-http-response.snippet

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
----
33
HTTP/1.1 ${statusCode} ${statusReason}
44
{% for h in headers -%}
5-
${h.key}: ${h.value}
5+
${h.name}: ${h.value}
66
{% endfor -%}
77
${responseBody -}
88
----

modules/jooby-openapi/src/main/resources/io/jooby/openapi/templates/asciidoc/default-httpie-request.snippet

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)