Companion module for "springdoc-openapi with Spring Boot 4.1: Generating, Customising and Versioning Your API Spec". Reuses the ApiVersionConfigurer setup from the versioning-mechanics companion project and tests what springdoc-openapi 3.1.1 actually generates for a path with multiple version-scoped handlers: a default oneOf-merged operation with an arbitrary operationId, a working GroupedOpenApi + OpenApiCustomizer fix that collapses it to one clean schema per version, and a check of the officially-versioning-supported functional-endpoint path (springdoc v3.0.2's "Add support for Spring Framework API Versioning with Functional Endpoints"), which turns out to document only one of two registered versions rather than either version separately. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01M1uZDXWkY1MnTfQgt4HMeQ
2.3 KiB
Why the version→schema map in the fix is hand-maintained
VersionedOpenApiConfig
keeps a small, explicit Map<String, VersionSpec> from version string to schema name /
operationId / summary, rather than deriving that mapping automatically from the running
application. This is a deliberate limitation, not an oversight, and it's worth being honest about
why: an OpenApiCustomizer (org.springdoc.core.customizers.OpenApiCustomizer) receives exactly
one argument, the fully-built io.swagger.v3.oas.models.OpenAPI document — see
javap org/springdoc/core/customizers/OpenApiCustomizer.class:
public interface org.springdoc.core.customizers.OpenApiCustomizer {
public abstract void customise(io.swagger.v3.oas.models.OpenAPI);
}
By the time a customizer runs, springdoc has already folded the three HandlerMethods into one
merged Operation (see the previous chapter) — the association between "this specific handler
method" and "this specific version string" has already been discarded. An OperationCustomizer
(org.springdoc.core.customizers.OperationCustomizer) does get a HandlerMethod argument per
call, which looks more promising, but it still runs once per merged operation, not once per
original handler — it cannot see the three original mappings separately either, because springdoc
folds first and customizes second. There is no supported extension point in 3.1.1 that intercepts
before the fold.
Reflectively pulling the version condition back out of Spring's own RequestMappingInfo (via
RequestMappingHandlerMapping.getHandlerMethods()) would work at the Spring MVC layer — Framework
7 exposes it there in principle — but wiring that into springdoc's generation pipeline means
matching springdoc's internal notion of "this operation" back to Spring's RequestMappingInfo by
path and method, which is exactly the kind of implementation-detail-coupled reflection that breaks
silently on the next springdoc minor version. A hand-maintained map keyed by the version strings
you already wrote in your own @GetMapping(version = ...) annotations is three lines of
boilerplate per version and it does not break when springdoc changes how it folds operations
internally.