();
+ errorContext.put("code", statusCode);
+ errorContext.put("message", rsp.getDescription());
+ return context().error(evaluationContext, errorContext);
+ }
+
+ private ResponseExt response() {
+ if (statusCode == null) {
+ return operation.getDefaultResponse();
+ } else {
+ var rsp = operation.getResponses().get(Integer.toString(statusCode));
+ if (rsp == null) {
+ if (statusCode >= 200 && statusCode <= 299) {
+ // override default response
+ return operation.getDefaultResponse();
+ }
+ }
+ return (ResponseExt) rsp;
+ }
+ }
+
+ public StatusCode getStatusCode() {
+ if (statusCode == null) {
+ return Optional.ofNullable(response())
+ .map(it -> StatusCode.valueOf(Integer.parseInt(it.getCode())))
+ .orElse(StatusCode.OK);
+ }
+ return StatusCode.valueOf(statusCode);
+ }
+
+ private Schema> getBody(ResponseExt response) {
+ return Optional.ofNullable(response)
+ .map(it -> toSchema(it.getContent(), List.of()))
+ .map(context()::resolveSchema)
+ .orElse(null);
+ }
+
+ @NonNull @Override
+ public String toString() {
+ return operation.getMethod() + " " + operation.getPath();
+ }
+}
diff --git a/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/asciidoc/Lookup.java b/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/asciidoc/Lookup.java
new file mode 100644
index 0000000000..46feb364f6
--- /dev/null
+++ b/modules/jooby-openapi/src/main/java/io/jooby/internal/openapi/asciidoc/Lookup.java
@@ -0,0 +1,267 @@
+/*
+ * Jooby https://jooby.io
+ * Apache License Version 2.0 https://jooby.io/LICENSE.txt
+ * Copyright 2014 Edgar Espina
+ */
+package io.jooby.internal.openapi.asciidoc;
+
+import java.util.*;
+import java.util.stream.Stream;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import io.jooby.StatusCode;
+import io.pebbletemplates.pebble.extension.Function;
+import io.pebbletemplates.pebble.template.EvaluationContext;
+import io.pebbletemplates.pebble.template.PebbleTemplate;
+
+/**
+ * GET("path") | table GET("path") | parameters | table
+ *
+ * schema("Book") | json schema("Book.type") | yaml
+ *
+ *
GET("path") | response | json
+ *
+ *
GET("path") | response(200) | json
+ *
+ *
GET("path") | request | json
+ *
+ *
GET("path") | request | http
+ *
+ *
GET("path") | request | body | http
+ */
+public enum Lookup implements Function {
+ operation {
+ @Override
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ var method = args.get("method").toString();
+ var path = args.get("path").toString();
+ var asciidoc = AsciiDocContext.from(context);
+ return asciidoc.getOpenApi().findOperation(method, path);
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("method", "path");
+ }
+ },
+ GET {
+ @Override
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ return operation.execute(appendMethod(args), self, context, lineNumber);
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("path");
+ }
+ },
+ POST {
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ return operation.execute(appendMethod(args), self, context, lineNumber);
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("path");
+ }
+ },
+ PUT {
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ return operation.execute(appendMethod(args), self, context, lineNumber);
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("path");
+ }
+ },
+ PATCH {
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ return operation.execute(appendMethod(args), self, context, lineNumber);
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("path");
+ }
+ },
+ DELETE {
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ return operation.execute(appendMethod(args), self, context, lineNumber);
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("path");
+ }
+ },
+ schema {
+ @Override
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ var path = args.get("path").toString();
+ var asciidoc = AsciiDocContext.from(context);
+ return asciidoc.resolveSchema(path);
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("path");
+ }
+
+ @Override
+ public List alias() {
+ return List.of("schema", "model");
+ }
+ },
+ tag {
+ @Override
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ var asciidoc = AsciiDocContext.from(context);
+ var name = args.get("name").toString();
+ return asciidoc.getOpenApi().getTags().stream()
+ .filter(tag -> tag.getName().equalsIgnoreCase(name))
+ .findFirst()
+ .map(
+ it ->
+ new TagExt(
+ it,
+ asciidoc.getOpenApi().findOperationByTag(it.getName()).stream()
+ .map(op -> new HttpRequest(asciidoc, op, Map.of()))
+ .toList()))
+ .orElseThrow(() -> new NoSuchElementException("Tag not found: " + name));
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("name");
+ }
+ },
+ error {
+ @Override
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ var asciidoc = AsciiDocContext.from(context);
+ return asciidoc.error(context, args);
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("code");
+ }
+ },
+ routes {
+ @Override
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ var asciidoc = AsciiDocContext.from(context);
+ var operations = asciidoc.getOpenApi().getOperations();
+ var list =
+ operations.stream()
+ .filter(
+ it -> {
+ var includes = (String) args.get("includes");
+ return includes == null || it.getPath().matches(includes);
+ })
+ .map(it -> new HttpRequest(asciidoc, it, args))
+ .toList();
+ return new HttpRequestList(asciidoc, list);
+ }
+
+ @Override
+ public List getArgumentNames() {
+ return List.of("includes");
+ }
+
+ @Override
+ public List alias() {
+ return List.of("routes", "operations");
+ }
+ },
+ statusCode {
+ @Override
+ public Object execute(
+ Map args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+ var code = args.get("code");
+ if (code instanceof List> codes) {
+ return new StatusCodeList(codes.stream().flatMap(this::toMap).toList());
+ }
+ return new StatusCodeList(toMap(code).toList());
+ }
+
+ @NonNull private Stream