Skip to content

Commit 9842652

Browse files
committed
- enum doc: add field doc with custom implementation
- add schema function to located schemas and/or schema properties - add `display` filter
1 parent 620c75d commit 9842652

File tree

10 files changed

+195
-66
lines changed

10 files changed

+195
-66
lines changed
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 io.jooby.internal.openapi;
7+
8+
import java.util.HashMap;
9+
import java.util.Map;
10+
11+
import com.fasterxml.jackson.annotation.JsonIgnore;
12+
import io.swagger.v3.oas.models.media.StringSchema;
13+
14+
public class EnumSchema extends StringSchema {
15+
@JsonIgnore private final Map<String, String> fields = new HashMap<>();
16+
17+
public EnumSchema() {}
18+
19+
public void setDescription(String name, String description) {
20+
fields.put(name, description);
21+
}
22+
23+
public String getDescription(String name) {
24+
return fields.get(name);
25+
}
26+
}

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

Lines changed: 51 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,7 @@
3030
import java.time.OffsetDateTime;
3131
import java.time.Period;
3232
import java.time.ZonedDateTime;
33-
import java.util.ArrayList;
34-
import java.util.Collection;
35-
import java.util.Collections;
36-
import java.util.Currency;
37-
import java.util.Date;
38-
import java.util.EnumSet;
39-
import java.util.HashMap;
40-
import java.util.HashSet;
41-
import java.util.List;
42-
import java.util.Locale;
43-
import java.util.Map;
44-
import java.util.Optional;
45-
import java.util.Set;
46-
import java.util.UUID;
33+
import java.util.*;
4734
import java.util.concurrent.ConcurrentHashMap;
4835
import java.util.concurrent.ConcurrentMap;
4936
import java.util.function.Function;
@@ -75,20 +62,7 @@
7562
import io.jooby.openapi.DebugOption;
7663
import io.swagger.v3.core.util.RefUtils;
7764
import io.swagger.v3.oas.models.SpecVersion;
78-
import io.swagger.v3.oas.models.media.ArraySchema;
79-
import io.swagger.v3.oas.models.media.BinarySchema;
80-
import io.swagger.v3.oas.models.media.BooleanSchema;
81-
import io.swagger.v3.oas.models.media.ByteArraySchema;
82-
import io.swagger.v3.oas.models.media.DateSchema;
83-
import io.swagger.v3.oas.models.media.DateTimeSchema;
84-
import io.swagger.v3.oas.models.media.FileSchema;
85-
import io.swagger.v3.oas.models.media.IntegerSchema;
86-
import io.swagger.v3.oas.models.media.MapSchema;
87-
import io.swagger.v3.oas.models.media.NumberSchema;
88-
import io.swagger.v3.oas.models.media.ObjectSchema;
89-
import io.swagger.v3.oas.models.media.Schema;
90-
import io.swagger.v3.oas.models.media.StringSchema;
91-
import io.swagger.v3.oas.models.media.UUIDSchema;
65+
import io.swagger.v3.oas.models.media.*;
9266
import jakarta.data.page.Page;
9367
import jakarta.data.page.PageRequest;
9468

@@ -283,7 +257,7 @@ public Schema schema(Class type) {
283257
return new ObjectSchema();
284258
}
285259
if (type.isEnum()) {
286-
StringSchema schema = new StringSchema();
260+
var schema = new EnumSchema();
287261
EnumSet.allOf(type).forEach(e -> schema.addEnumItem(((Enum) e).name()));
288262
return schema;
289263
}
@@ -333,36 +307,56 @@ private void document(Class typeName, Schema schema, ResolvedSchemaExt resolvedS
333307
.ifPresent(
334308
javadoc -> {
335309
Optional.ofNullable(javadoc.getText()).ifPresent(schema::setDescription);
310+
// make a copy
336311
Map<String, Schema> properties = schema.getProperties();
337312
if (properties != null) {
338-
properties.forEach(
339-
(key, value) -> {
340-
var text = javadoc.getPropertyDoc(key);
341-
var propertyType = getPropertyType(typeName, key);
342-
var isEnum =
343-
propertyType != null
344-
&& propertyType.isEnum()
345-
&& resolvedSchema.referencedSchemasByType.keySet().stream()
346-
.map(this::toClass)
347-
.anyMatch(it -> !it.equals(propertyType));
348-
if (isEnum) {
349-
javadocParser
350-
.parse(propertyType.getName())
351-
.ifPresent(
352-
enumDoc -> {
353-
var enumDesc = enumDoc.getEnumDescription(text);
354-
if (enumDesc != null) {
355-
value.setDescription(enumDesc);
356-
}
357-
});
358-
} else {
359-
value.setDescription(text);
360-
var example = javadoc.getPropertyExample(key);
361-
if (example != null) {
362-
value.setExample(example);
363-
}
364-
}
365-
});
313+
new LinkedHashMap<>(properties)
314+
.forEach(
315+
(key, value) -> {
316+
var text = javadoc.getPropertyDoc(key);
317+
var propertyType = getPropertyType(typeName, key);
318+
var isEnum =
319+
propertyType != null
320+
&& propertyType.isEnum()
321+
&& resolvedSchema.referencedSchemasByType.keySet().stream()
322+
.map(this::toClass)
323+
.anyMatch(it -> !it.equals(propertyType));
324+
if (isEnum) {
325+
javadocParser
326+
.parse(propertyType.getName())
327+
.ifPresent(
328+
enumDoc -> {
329+
var enumDesc = enumDoc.getEnumDescription(text);
330+
if (enumDesc != null) {
331+
EnumSchema enumSchema;
332+
if (!(value instanceof EnumSchema)) {
333+
enumSchema = new EnumSchema();
334+
value.getEnum().stream()
335+
.forEach(
336+
enumValue ->
337+
enumSchema.addEnumItemObject(
338+
enumValue.toString()));
339+
properties.put(key, enumSchema);
340+
} else {
341+
enumSchema = (EnumSchema) value;
342+
}
343+
for (var field : enumSchema.getEnum()) {
344+
var enumItemDesc = enumDoc.getEnumItemDescription(field);
345+
if (enumItemDesc != null) {
346+
enumSchema.setDescription(field, enumItemDesc);
347+
}
348+
}
349+
enumSchema.setDescription(enumDesc);
350+
}
351+
});
352+
} else {
353+
value.setDescription(text);
354+
var example = javadoc.getPropertyExample(key);
355+
if (example != null) {
356+
value.setExample(example);
357+
}
358+
}
359+
});
366360
}
367361
});
368362
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ public List<OperationExt> parse(ParserContext ctx, OpenAPIExt openapi) {
6363
String applicationName =
6464
Optional.ofNullable(ctx.getMainClass()).orElse(ctx.getRouter().getClassName());
6565
ClassNode application = ctx.classNode(Type.getObjectType(applicationName.replace(".", "/")));
66-
6766
// JavaDoc
6867
addJavaDoc(ctx, ctx.getRouter().getClassName(), "", operations);
6968

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,60 @@
1010
import java.util.Map;
1111

1212
import com.fasterxml.jackson.core.JsonProcessingException;
13+
import io.jooby.internal.openapi.EnumSchema;
1314
import io.pebbletemplates.pebble.error.PebbleException;
1415
import io.pebbletemplates.pebble.extension.Filter;
1516
import io.pebbletemplates.pebble.template.EvaluationContext;
1617
import io.pebbletemplates.pebble.template.PebbleTemplate;
18+
import io.swagger.v3.oas.models.media.Schema;
1719

1820
public enum Filters implements Filter {
21+
display {
22+
@Override
23+
public List<String> getArgumentNames() {
24+
return null;
25+
}
26+
27+
@Override
28+
public Object apply(
29+
Object input,
30+
Map<String, Object> args,
31+
PebbleTemplate self,
32+
EvaluationContext context,
33+
int lineNumber)
34+
throws PebbleException {
35+
if (input instanceof Schema<?> schema) {
36+
return displaySchema(schema);
37+
} else {
38+
throw new IllegalArgumentException("Unsupported input type: " + input.getClass());
39+
}
40+
}
41+
42+
private Object displaySchema(Schema<?> schema) {
43+
if (schema instanceof EnumSchema enumSchema) {
44+
var sb = new StringBuilder();
45+
sb.append(
46+
"""
47+
[cols="1,3"]
48+
|===
49+
| Type | Description
50+
51+
""");
52+
for (var name : enumSchema.getEnum()) {
53+
sb.append("\n")
54+
.append("| *")
55+
.append(name)
56+
.append("*\n")
57+
.append("| ")
58+
.append(enumSchema.getDescription(name))
59+
.append("\n");
60+
}
61+
return sb.append(" |===").toString();
62+
}
63+
return null;
64+
}
65+
},
66+
1967
json {
2068
@Override
2169
public List<String> getArgumentNames() {

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
import java.util.HashMap;
99
import java.util.List;
1010
import java.util.Map;
11+
import java.util.stream.Collectors;
12+
import java.util.stream.Stream;
1113

1214
import io.pebbletemplates.pebble.error.PebbleException;
1315
import io.pebbletemplates.pebble.extension.Function;
1416
import io.pebbletemplates.pebble.template.EvaluationContext;
1517
import io.pebbletemplates.pebble.template.PebbleTemplate;
18+
import io.swagger.v3.oas.models.media.Schema;
1619

1720
public enum Functions implements Function {
1821
GET {
@@ -80,6 +83,51 @@ public Object execute(
8083
return operation.execute(args, self, context, lineNumber);
8184
}
8285
},
86+
tag {
87+
@Override
88+
public List<String> getArgumentNames() {
89+
return List.of("name");
90+
}
91+
92+
@Override
93+
public Object execute(
94+
Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
95+
var openApi = InternalContext.openApi(context);
96+
var name = args.get("name");
97+
return openApi.getTags().stream()
98+
.filter(tag -> tag.getName().equals(name))
99+
.findFirst()
100+
.orElseThrow(() -> new IllegalArgumentException("Tag not found: " + name));
101+
}
102+
},
103+
schema {
104+
@Override
105+
public List<String> getArgumentNames() {
106+
return List.of("name");
107+
}
108+
109+
@Override
110+
public Object execute(
111+
Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
112+
var openApi = InternalContext.openApi(context);
113+
var name = args.get("name").toString();
114+
var path = name.split("\\.");
115+
var schema = openApi.getComponents().getSchemas().get(path[0]);
116+
if (schema == null) {
117+
throw new IllegalArgumentException("Schema not found: " + name);
118+
}
119+
for (int i = 1; i < path.length; i++) {
120+
Schema<?> inner = (Schema<?>) schema.getProperties().get(path[i]);
121+
if (inner == null) {
122+
throw new IllegalArgumentException(
123+
"Property not found: " + Stream.of(path).limit(i).collect(Collectors.joining(".")));
124+
}
125+
schema = inner;
126+
}
127+
128+
return schema;
129+
}
130+
},
83131
operation {
84132
@Override
85133
public List<String> getArgumentNames() {

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ protected String doApply(
6565
.getConsumes()
6666
.forEach(value -> options.put("-H", "'Content-Type: " + value + "'"));
6767
}
68+
var parameters = Optional.ofNullable(operation.getParameters()).orElse(List.of());
6869
/* Body */
6970
if (operation.getRequestBody() != null) {
7071
var requestBody = operation.getRequestBody();
@@ -84,8 +85,7 @@ protected String doApply(
8485
}
8586
} else {
8687
// can be form
87-
var form =
88-
operation.getParameters().stream().filter(it -> "form".equals(it.getIn())).toList();
88+
var form = parameters.stream().filter(it -> "form".equals(it.getIn())).toList();
8989
encodeUrlParameter(form)
9090
.forEach(
9191
it ->
@@ -97,8 +97,7 @@ protected String doApply(
9797
}
9898
/* Method */
9999
var url = snippetContext.get("url").toString();
100-
var query =
101-
operation.getParameters().stream().filter(it -> "query".equals(it.getIn())).toList();
100+
var query = parameters.stream().filter(it -> "query".equals(it.getIn())).toList();
102101
// query parameters
103102
if (!query.isEmpty()) {
104103
url +=
@@ -520,10 +519,9 @@ protected Map<String, Object> newSnippetContext(String serverUrl, OperationExt o
520519
map.put("url", serverUrl + operation.getPath());
521520
var requestHeaders = ArrayListMultimap.<String, String>create();
522521
var responseHeaders = ArrayListMultimap.<String, String>create();
522+
var parameters = Optional.ofNullable(operation.getParameters()).orElse(List.of());
523523
var headerParams =
524-
operation.getParameters().stream()
525-
.filter(it -> "header".equalsIgnoreCase(it.getIn()))
526-
.toList();
524+
parameters.stream().filter(it -> "header".equalsIgnoreCase(it.getIn())).toList();
527525
operation
528526
.getProduces()
529527
.forEach(

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@ public String getEnumDescription(String text) {
8888
return text;
8989
}
9090

91+
public String getEnumItemDescription(String name) {
92+
if (isEnum()) {
93+
var field = fields.get(name);
94+
if (field != null) {
95+
return field.getText();
96+
}
97+
}
98+
return null;
99+
}
100+
91101
private void defaultRecordMembers() {
92102
JavaDocTag.javaDocTag(
93103
javadoc,

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ public FieldDoc(JavaDocParser ctx, DetailAST node, DetailAST javadoc) {
1717
super(ctx, node, javadoc);
1818
}
1919

20+
@Override
21+
public String getText() {
22+
var text = super.getText();
23+
return text == null ? null : text.replace("<p>", "").replace("</p>", "").trim();
24+
}
25+
2026
public String getName() {
2127
return JavaDocSupport.getSimpleName(node);
2228
}

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
*/
66
package io.jooby.internal.openapi.javadoc;
77

8-
import static io.jooby.internal.openapi.javadoc.JavaDocStream.*;
98
import static io.jooby.internal.openapi.javadoc.JavaDocStream.javadocToken;
109

1110
import java.util.*;

modules/jooby-openapi/src/main/java/io/jooby/openapi/OpenAPIGenerator.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ public OpenAPIGenerator() {}
241241
doc.getServers().forEach(openapi::addServersItem);
242242
doc.getContact().forEach(info::setContact);
243243
doc.getLicense().forEach(info::setLicense);
244+
doc.getTags().forEach(openapi::addTagsItem);
244245
});
245246
}
246247

0 commit comments

Comments
 (0)