Skip to content

Commit daf6090

Browse files
committed
link: add link to schema filter
- bug fixing - make jakarata Page fully embedded
1 parent bbcf676 commit daf6090

File tree

13 files changed

+353
-87
lines changed

13 files changed

+353
-87
lines changed
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 io.jooby.internal.openapi;
7+
8+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
9+
import io.swagger.v3.oas.models.media.Schema;
10+
11+
@JsonIgnoreProperties({"items"})
12+
public class ArrayLikeSchema<T> extends Schema<T> {
13+
14+
public static ArrayLikeSchema<?> create(Schema<?> schema, Schema<?> items) {
15+
var arrayLikeSchema = new ArrayLikeSchema<>();
16+
arrayLikeSchema.setItems(items);
17+
arrayLikeSchema.setProperties(schema.getProperties());
18+
arrayLikeSchema.setType(schema.getType());
19+
arrayLikeSchema.setTypes(schema.getTypes());
20+
arrayLikeSchema.setName(schema.getName());
21+
return arrayLikeSchema;
22+
}
23+
}

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -450,15 +450,23 @@ private Schema schema(JavaType type) {
450450
// must be embedded it mimics a List<T>. This is bc it might have a different item type
451451
// per operation.
452452
var pageSchema = converters.read(type.getRawClass()).get("Page");
453-
// force loading of PageRequest
454-
schema(PageRequest.class);
453+
454+
var pageRequestSchema = converters.read(PageRequest.class).get("PageRequest");
455+
pageSchema.getProperties().put("pageRequest", pageRequestSchema);
456+
pageSchema.getProperties().put("nextPageRequest", pageRequestSchema);
457+
pageSchema.getProperties().put("previousPageRequest", pageRequestSchema);
455458

456459
var params = type.getBindings().getTypeParameters();
460+
Schema<?> element;
457461
if (params != null && !params.isEmpty()) {
462+
element = schema(params.getFirst());
458463
Schema<?> contentSchema = (Schema<?>) pageSchema.getProperties().get("content");
459-
contentSchema.setItems(schema(params.getFirst()));
464+
contentSchema.setItems(element);
465+
} else {
466+
element = new Schema<>();
467+
element.setType("object");
460468
}
461-
return pageSchema;
469+
return ArrayLikeSchema.create(pageSchema, element);
462470
}
463471
return schema(type.getRawClass());
464472
}

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

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

8+
import static io.swagger.v3.oas.models.Components.COMPONENTS_SCHEMAS_REF;
89
import static java.util.Optional.ofNullable;
910

1011
import java.io.IOException;
@@ -33,6 +34,7 @@
3334
import io.pebbletemplates.pebble.extension.AbstractExtension;
3435
import io.pebbletemplates.pebble.extension.Filter;
3536
import io.pebbletemplates.pebble.extension.Function;
37+
import io.pebbletemplates.pebble.lexer.Syntax;
3638
import io.pebbletemplates.pebble.loader.ClasspathLoader;
3739
import io.pebbletemplates.pebble.loader.DelegatingLoader;
3840
import io.pebbletemplates.pebble.loader.FileLoader;
@@ -56,7 +58,7 @@ public class AsciiDocContext {
5658

5759
private final AutoDataFakerMapper faker = new AutoDataFakerMapper();
5860

59-
private final Map<Object, Map<String, Object>> examples = new HashMap<>();
61+
private final Map<Schema<?>, Map<String, Object>> examples = new HashMap<>();
6062

6163
private final Instant now = Instant.now();
6264

@@ -111,6 +113,7 @@ private static PebbleEngine createEngine(
111113
return new PebbleEngine.Builder()
112114
.autoEscaping(false)
113115
.loader(new DelegatingLoader(loaders))
116+
.syntax(new Syntax.Builder().setEnableNewLineTrimming(false).build())
114117
.extension(
115118
new AbstractExtension() {
116119
@Override
@@ -131,9 +134,11 @@ public Map<String, Object> getGlobalVariables() {
131134
"..."));
132135
// Routes
133136
var operations =
134-
Optional.of(context.openapi.getOperations()).orElse(List.of()).stream()
135-
.map(op -> new HttpRequest(context, op, Map.of()))
136-
.toList();
137+
new HttpRequestList(
138+
context,
139+
Optional.of(context.openapi.getOperations()).orElse(List.of()).stream()
140+
.map(op -> new HttpRequest(context, op, Map.of()))
141+
.toList());
137142
// so we can print routes without calling function: routes() vs routes
138143
openapiRoot.put("routes", operations);
139144
openapiRoot.put("operations", operations);
@@ -150,6 +155,12 @@ public Map<String, Object> getGlobalVariables() {
150155
.toList()))
151156
.toList();
152157
openapiRoot.put("tags", tags);
158+
// Schemas
159+
var components = context.openapi.getComponents();
160+
if (components != null && components.getSchemas() != null) {
161+
var schemas = components.getSchemas();
162+
openapiRoot.put("schemas", schemas);
163+
}
153164

154165
// make in to work without literal
155166
openapiRoot.put("query", "query");
@@ -286,7 +297,7 @@ public String schemaType(Schema<?> schema) {
286297
return Optional.ofNullable(resolved.getFormat()).orElse(resolveType(resolved));
287298
}
288299

289-
private String resolveType(Schema<?> schema) {
300+
public String resolveType(Schema<?> schema) {
290301
var resolved = resolveSchema(schema);
291302
if (resolved.getType() == null) {
292303
return resolved.getTypes().iterator().next();
@@ -302,7 +313,12 @@ public Schema<?> resolveSchema(Schema<?> schema) {
302313
return schema;
303314
}
304315

305-
public Map<String, Object> schemaProperties(Schema<?> schema) {
316+
public Object schemaProperties(Schema<?> schema) {
317+
var resolved = resolveSchema(schema);
318+
var resolvedType = resolveType(resolved);
319+
if ("array".equals(resolvedType)) {
320+
return List.of(traverse(resolved.getItems(), NOOP));
321+
}
306322
return traverse(schema, NOOP);
307323
}
308324

@@ -338,49 +354,48 @@ public Schema<?> emptySchema(Schema<?> schema) {
338354
return empty;
339355
}
340356

341-
public Map<String, Object> schemaExample(Schema<?> schema) {
342-
return examples.computeIfAbsent(
343-
schema,
344-
s ->
345-
traverse(
346-
new HashSet<>(),
347-
schema,
348-
(parent, property) -> {
349-
var enumItems = property.getEnum();
350-
if (enumItems == null || enumItems.isEmpty()) {
351-
var type = schemaType(property);
352-
var gen = faker.getGenerator(parent.getName(), property.getName(), type, type);
353-
return gen.get();
354-
} else {
355-
return enumItems.get(new Random().nextInt(enumItems.size())).toString();
356-
}
357-
},
358-
NOOP,
359-
NOOP));
357+
public Object schemaExample(Schema<?> schema) {
358+
var resolved = resolveSchema(schema);
359+
var resolvedType = resolveType(resolved);
360+
var target = resolved;
361+
if ("array".equals(resolvedType)) {
362+
target = resolveSchema(resolved.getItems());
363+
}
364+
var result =
365+
examples.computeIfAbsent(
366+
target,
367+
key ->
368+
traverse(
369+
new HashSet<>(),
370+
key,
371+
(parent, property) -> {
372+
var enumItems = property.getEnum();
373+
if (enumItems == null || enumItems.isEmpty()) {
374+
var type = schemaType(property);
375+
var gen =
376+
faker.getGenerator(parent.getName(), property.getName(), type, type);
377+
return gen.get();
378+
} else {
379+
return enumItems.get(new Random().nextInt(enumItems.size())).toString();
380+
}
381+
},
382+
NOOP));
383+
return "array".equals(resolvedType) ? List.of(result) : result;
360384
}
361385

362386
public void traverseSchema(Schema<?> schema, BiConsumer<String, Schema<?>> consumer) {
363387
traverse(schema, consumer);
364388
}
365389

366390
private Map<String, Object> traverse(Schema<?> schema, BiConsumer<String, Schema<?>> consumer) {
367-
return traverse(schema, consumer, NOOP);
368-
}
369-
370-
private Map<String, Object> traverse(
371-
Schema<?> schema,
372-
BiConsumer<String, Schema<?>> consumer,
373-
BiConsumer<String, Schema<?>> inner) {
374-
return traverse(
375-
new HashSet<>(), schema, (parent, property) -> schemaType(property), consumer, inner);
391+
return traverse(new HashSet<>(), schema, (parent, property) -> schemaType(property), consumer);
376392
}
377393

378394
private Map<String, Object> traverse(
379395
Set<Object> visited,
380396
Schema<?> schema,
381397
SneakyThrows.Function2<Schema<?>, Schema<?>, String> valueMapper,
382-
BiConsumer<String, Schema<?>> consumer,
383-
BiConsumer<String, Schema<?>> inner) {
398+
BiConsumer<String, Schema<?>> consumer) {
384399
if (schema == null) {
385400
return Map.of();
386401
}
@@ -395,13 +410,11 @@ private Map<String, Object> traverse(
395410
var valueType = resolveType(resolvedValue);
396411
consumer.accept(name, resolvedValue);
397412
if ("object".equals(valueType)) {
398-
result.put(name, traverse(visited, resolvedValue, valueMapper, inner, inner));
413+
result.put(name, traverse(visited, resolvedValue, valueMapper, NOOP));
399414
} else if ("array".equals(valueType)) {
400415
var array =
401416
ofNullable(resolvedValue.getItems())
402-
.map(
403-
items ->
404-
traverse(visited, resolveSchema(items), valueMapper, inner, inner))
417+
.map(items -> traverse(visited, resolveSchema(items), valueMapper, NOOP))
405418
.map(List::of)
406419
.orElse(List.of());
407420
result.put(name, array);
@@ -442,8 +455,8 @@ private Optional<Schema<?>> resolveSchemaInternal(String name) {
442455
if (components == null || components.getSchemas() == null) {
443456
throw new NoSuchElementException("No schema found");
444457
}
445-
if (name.startsWith("#/components/schemas/")) {
446-
name = name.substring("#/components/schemas/".length());
458+
if (name.startsWith(COMPONENTS_SCHEMAS_REF)) {
459+
name = name.substring(COMPONENTS_SCHEMAS_REF.length());
447460
}
448461
return Optional.ofNullable((Schema<?>) components.getSchemas().get(name));
449462
}

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@
55
*/
66
package io.jooby.internal.openapi.asciidoc;
77

8-
import java.util.HashMap;
9-
import java.util.List;
10-
import java.util.Map;
11-
import java.util.TreeMap;
8+
import java.util.*;
129

1310
import io.jooby.internal.openapi.OperationExt;
1411
import io.jooby.internal.openapi.asciidoc.display.*;
@@ -91,6 +88,38 @@ public Object apply(
9188
return new SafeString(toAsciidoc(asciidoc, input).list(new TreeMap<>(args)));
9289
}
9390
},
91+
link {
92+
@Override
93+
public Object apply(
94+
Object input,
95+
Map<String, Object> args,
96+
PebbleTemplate self,
97+
EvaluationContext context,
98+
int lineNumber)
99+
throws PebbleException {
100+
var schema =
101+
switch (input) {
102+
case Schema<?> s -> s;
103+
case HttpMessage msg -> msg.getBody();
104+
default -> throw new IllegalArgumentException("Can't render: " + input);
105+
};
106+
var asciidoc = AsciiDocContext.from(context);
107+
var resolved = asciidoc.resolveSchema(schema);
108+
var target = resolved;
109+
var prefix = "";
110+
var suffix = "";
111+
if (resolved.getItems() != null) {
112+
target = asciidoc.resolveSchema(resolved.getItems());
113+
prefix = Optional.ofNullable(resolved.getName()).orElse("") + "[";
114+
suffix = "]";
115+
}
116+
if ("object".equals(asciidoc.resolveType(target))) {
117+
return new SafeString(prefix + "<<" + target.getName() + ">>" + suffix);
118+
}
119+
// no link for basic types
120+
return prefix + target.getName() + suffix;
121+
}
122+
},
94123
curl {
95124
@Override
96125
public Object apply(

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import io.jooby.internal.openapi.ParameterExt;
2222
import io.swagger.v3.oas.models.media.Schema;
2323
import io.swagger.v3.oas.models.parameters.Parameter;
24+
import io.swagger.v3.oas.models.security.SecurityRequirement;
2425

2526
@JsonIncludeProperties({"path", "method"})
2627
public record HttpRequest(
@@ -222,6 +223,14 @@ private void encode(
222223
}
223224
}
224225

226+
public boolean isDeprecated() {
227+
return operation.getDeprecated() == Boolean.TRUE;
228+
}
229+
230+
public List<SecurityRequirement> getSecurity() {
231+
return operation.getSecurity();
232+
}
233+
225234
@Override
226235
public Schema<?> getBody() {
227236
return getBody(List.of());

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ public Object apply(
2929
if (input instanceof Schema<?> schema) {
3030
var asciidoc = AsciiDocContext.from(context);
3131
return asciidoc.schemaExample(schema);
32-
} else if (input instanceof Map mapLike) {
33-
3432
}
3533
return input;
3634
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ public String render(Map<String, Object> options) {
2929
}
3030
var schema = response.getBody();
3131
if (schema != null) {
32-
sb.append(context.getJson().writeValueAsString(context.schemaProperties(schema)))
33-
.append('\n');
32+
sb.append(context.toJson(context.schemaProperties(schema), false)).append('\n');
3433
}
3534
return sb.append("----").toString();
3635
} catch (Exception x) {

modules/jooby-openapi/src/test/java/io/jooby/internal/openapi/asciidoc/PebbleTemplateSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@ public String evaluate(String input) throws IOException {
4242
var template = context.getEngine().getLiteralTemplate(input);
4343
var writer = new StringWriter();
4444
template.evaluate(writer);
45-
return writer.toString();
45+
return writer.toString().trim();
4646
}
4747
}

0 commit comments

Comments
 (0)