Open anma opened 3 years ago
To second this, we have almost exactly the same use case, but have hit a different aspect of the same problem. We want to define a meta-annotation, but then allow specific methods to override particular responses. So for example, if a meta-annotation defining 200, 403, and 404 codes is on an endpoint and we add an @ApiResponse
with a code of 200, the resulting schema should have the 403 and 404 from the meta-annotation and the 200 from the direct annotation.
That is not what happens now.
However, our experience doesn't quite match that of @anma .
An example from our [test] codebase:
@RestController
public class FakeController
{
@GetMapping("/something")
@ApiResponse(responseCode = "403",
content = @Content(schema = @Schema(oneOf = String.class)))
@DefaultStatusCodes
public void test()
{ /* do something here */ }
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Forbidden
// @NotFound // etc. etc.
public @interface DefaultStatusCodes {}
@Retention(RetentionPolicy.RUNTIME)
@ApiResponse(responseCode = "403", description = "Access Denied",
content = @Content(schema = @Schema(accessMode = AccessMode.READ_ONLY, oneOf = Problem.class)))
public @interface Forbidden {}
This produces a spec which appears to ignore the directly-present annotation and use the one from the meta-annotation.
Swap the @ApiResponse
and @DefaultStatusCodes
lines, however, and the spec will reflect the meta-annotation and not the directly-present one.
:confounded: :angry:
As for fixing this, ideally I'd like to see:
I'm even more confused now. Recompiling the test classes in question seems to change the behavior depending on what's compiled when.
So the overall bug "merging overlapping annotations is broken" still persists... but it's not clear that there's just one root cause of the behavior that we and @anma are encountering.
As far as fixing this, it seems like our sources of response information are:
When reconciling overlapping statuses defined in the above sources, I would propose the following order of precedence:
@ApiResponse
used in a meta-annotation takes precedence over one used in another annotation that is in turn used in the meta annotation.)That first one is a bit of a problem in the case of multiple overlapping annotations. Annotation order is preserved in the bytecode, but AFAIK it's not firmly specified that reflection will return them in that order (and I haven't tested that it does.) Not sure what the right thing is here if order isn't reliable.
Meta-annotations can be used to group annotations like
@ApiResponse
. However, when you mix direct and meta annotations, only the direct annotations are considered and the meta-annotations are ignored.For example, with the following code the generated documentation contains only the responses for code 200. The responses from the
@DefaultApiResponses
meta-annotations are ignored. If you remove the two direct@ApiResponse
annotations (marked withXXX
), then the documentation contains the responses for 400, 401, and 403 as expected. But you cannot have both.The cause of the problem is the implementation of
io.swagger.v3.core.util.ReflectionUtils.getRepeatableAnnotations(Method, Class<A>)
, which considers meta-annotations only if the given method has no direct annotations. Mergin the direct annotations and meta-annotations would make meta-annotations much more useful here.