Commits
Tarun Sukhani authored 7d9bcd6cfe4
feat: generate OpenAPI 3 spec from routes file, expose at /@api/openapi.{json,yaml} (PF-12)
Adds an OpenAPI 3 generator that reads the in-memory route table
(Router.routes) and reflects on controller method signatures to build a
spec object, plus a plugin that serves it at /@api/openapi.json,
/@api/openapi.yaml, and renders Swagger UI at /@api/docs.
Design split: OpenApiGenerator is a stateless library (no Play
internals beyond the public Router.Route fields) so it is unit-testable
without a full Play boot. OpenApiPlugin is a thin shell that handles
config (openapi.enabled, openapi.basePath), short-circuits the request
in rawInvocation, and delegates serialization to swagger-core's
Json.pretty / Yaml.pretty.
Swagger UI is bundled as a single static HTML loader that pulls
swagger-ui-dist from unpkg.com — keeps the framework jar lean (no ~3 MB
of JS/CSS) at the cost of needing internet. /@api/docs is restricted to
DEV mode (404 in PROD) since the on-the-wire spec endpoint is the
production-grade contract.
What's deliberately out of scope: annotation-driven schema enrichment
(@Operation, @Schema, etc.) is a deep rabbit hole that swagger-jaxrs2
solves for JAX-RS apps but would need a Play-specific port. The
generator emits ObjectSchema for any non-primitive bean type — adequate
for codegen tools to produce client stubs against, but documentation
authors who want richer metadata will need a follow-up. Static-asset
routes (staticDir:, staticFile:, 404) are skipped; WS routes are
skipped (no useful HTTP equivalent).
Reflection notes: Play's controllers are typically static methods on
classes in the controllers.* package. The generator looks them up via
Class.forName + case-insensitive method match (Play's own resolution
convention). Without -parameters at compile time, javac doesn't store
real parameter names, so the generator falls back to positional
assignment for path parameters when isNamePresent() is false.
Annotation-driven naming (@PathParam etc.) would make this more
precise; deferred with the rest of the annotation work.
Dependencies: swagger-models 2.2.39 (the OpenAPI object graph),
swagger-core 2.2.39 (Json/Yaml utility classes), swagger-annotations
(compile-time peer of swagger-models), jackson-dataformat-yaml +
jackson-datatype-jsr310 (runtime peers of swagger-core). Pre-seeded
into framework/lib/ to work around the resolver pre-pinning declared
modules (see PF-9); ant resolve confirms cleanly thereafter.
Tests: OpenApiGeneratorTest (8 cases) exercises the generator directly
against hand-built Route objects — simple GET, path parameters, full
HTTP method matrix, void returns, unresolvable actions, static-route
skipping, and JSON/YAML round-trips through swagger-core's pretty
printers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>