Reputation: 36229
I've created the following alias:
@Retention(RetentionPolicy.RUNTIME)
@Parameter(in = ParameterIn.PATH,
name = "FieldId",
required = true,
extensions = { @Extension(properties = @ExtensionProperty(name = "custom-type",
value = "FieldId")) })
@AnnotationCollector
public @interface MyAnnotator {
}
When generating the OpenAPI definition, if I use the @Parameter
directly in my resource, it works, but if I use @MyAnnotator
, it is ignored. So for example:
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{FieldId}")
void create(@Parameter(in = ParameterIn.PATH,
name = "FieldId",
required = true,
extensions = { @Extension(properties = @ExtensionProperty(name = "custom-type",
value = "FieldId")) },
schema = @Schema(type = "string")) final FieldId fieldId)
would generate
post:
parameters:
- name: FieldId
in: path
required: true
schema:
type: string
x-custom-type: FieldId
but doing
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{FieldId}")
void create(@MyAnnotator FieldId fieldId)
does not. How come?
Upvotes: 1
Views: 2255
Reputation: 3306
Unfortunately the swagger-core doesn't support this feature. @Parameter
annotation must be placed on parameter directly.
At first JaxRS Reader implementation tries to find and resolve @Parameter
annotation like this.
io.swagger.v3.oas.annotations.Parameter paramAnnotation = AnnotationsUtils.getAnnotation(io.swagger.v3.oas.annotations.Parameter.class, paramAnnotations[i]);
Type paramType = ParameterProcessor.getParameterType(paramAnnotation, true);
if (paramType == null) {
paramType = type;
} else {
if (!(paramType instanceof Class)) {
paramType = type;
}
}
ResolvedParameter resolvedParameter = getParameters(paramType, Arrays.asList(paramAnnotations[i]), operation, classConsumes, methodConsumes, jsonViewAnnotation);
AnnotationUtils doesn't go deeper
public static <T> T getAnnotation(Class<T> cls, Annotation... annotations) {
if (annotations == null) {
return null;
}
for (Annotation annotation : annotations) {
if (cls.isAssignableFrom(annotation.getClass())) {
return (T)annotation;
}
}
return null;
}
Finally getParameters(...)
method is stricter. It checks is the annotation's type exactly same as the desired one.
for (Annotation annotation : annotations) {
if (annotation instanceof QueryParam) {
QueryParam param = (QueryParam) annotation;
// ...
} else if (annotation instanceof PathParam) {
PathParam param = (PathParam) annotation;
// ...
} else if (annotation instanceof MatrixParam) {
MatrixParam param = (MatrixParam) annotation;
// ...
}
// ... and so on
}
But there are some light at the end of the tunnel. You can create a custom ParameterExtension service implementing OpenAPIExtension
and you can handle those custom annotations.
As I mentioned before creating a custom ParameterExtension service is allowed. I made a minimal parameter resolver extension which extends the DefaultParameterExtension
.
All Parameter template needs to be created as a custom annotation like this:
@Retention(RetentionPolicy.RUNTIME)
@Parameter(in = ParameterIn.PATH,
name = "FieldId",
required = true,
extensions = {@Extension(properties = @ExtensionProperty(name = "custom-type",
value = "FieldId"))})
public @interface MyAnnotator {
}
The extended ParameterExtension reads @ParameterAlias
annotation which has a mandatory attribute.
@Path("/v1")
@Tags(@Tag(name = "test", description = ""))
public class FooResource {
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Path("/{FieldId}")
@Operation(operationId = "modifyFoo", summary = "Modifies a Foo entity")
public void modify(@ParameterAlias(MyAnnotator.class) final FieldId fieldId) {
}
}
Finally the extended extension ParameterAliasExtension
handles the @ParameterAliasExtension
public class ParameterAliasExtension extends DefaultParameterExtension {
@Override
public ResolvedParameter extractParameters(List<Annotation> annotations,
Type type,
Set<Type> typesToSkip,
Components components,
javax.ws.rs.Consumes classConsumes,
javax.ws.rs.Consumes methodConsumes,
boolean includeRequestBody,
JsonView jsonViewAnnotation,
Iterator<OpenAPIExtension> chain) {
List<Annotation> extendedAnnotations = null;
if (null != annotations) {
extendedAnnotations = new ArrayList<>(annotations);
ParameterAlias alias = AnnotationsUtils.getAnnotation(ParameterAlias.class, annotations.toArray(new Annotation[0]));
if (null != alias) {
Parameter aliasParameter = AnnotationsUtils.getAnnotation(Parameter.class, alias.value().getDeclaredAnnotations());
if (null != aliasParameter) {
extendedAnnotations.add(aliasParameter);
}
}
}
return super.extractParameters(extendedAnnotations == null ? annotations : extendedAnnotations,
type,
typesToSkip,
components,
classConsumes,
methodConsumes,
includeRequestBody,
jsonViewAnnotation,
chain);
}
}
This example is avaliable on my GitHub repo: https://github.com/zforgo/stackoverflow/tree/master/openapi-alias
Upvotes: 4