I am using Swagger annotations over my controller parameters. So, I end up with annotations like @ApiParam(name="default name", value="this is a default value")
. I think this is quite verbose. I would like to change it to something like @Foo
. I want to know if there's a way to replace @Foo
with @ApiParam
during compile time. Also, since I am using Spring, I think I have to consider the annotation processing order in Spring, as well. I mean I shouldn't replace @ApiParam
with @Foo
after Swagger or Spring picks it up. Is there any way to do this?
In simpler words, I have the same annotation with the same parameters used 5 times. Basically, I want to replace them with some custom annotation.
I know I have to show what I have already tried, but I have no clue where to even start.
Also, the question is not related to Swagger, it is just an example. I want to replace one annotation with another during compile time, so that the one picked up by Spring won't be the one I have put on the source code, but the one I have replaced.
If I understand what you are asking for, this is possible without compile-time annotation processing. It's not pretty and it might be more complexity than it's worth, but here's one way to do it.
Here's a custom annotation I made that is used for my shorthand @ApiParam
.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface GameIdParam {
String name() default "My Game ID";
String value() default "The integer ID of a particular game";
}
You can define whatever properties in @ApiParam
that you wish to override. Then you can use Springfox's Extension Framework to implement a custom handler for the new annotation.
import com.google.common.base.Optional;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.Example;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.EnumTypeDeterminer;
import springfox.documentation.spi.service.contexts.ParameterContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.readers.parameter.ApiParamParameterBuilder;
import java.util.function.Predicate;
import static java.util.Optional.ofNullable;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER;
import static springfox.documentation.swagger.common.SwaggerPluginSupport.pluginDoesApply;
import static springfox.documentation.swagger.readers.parameter.Examples.examples;
@Component
public class ShorthandAnnotationPlugin extends ApiParamParameterBuilder {
private final DescriptionResolver descriptions;
private final EnumTypeDeterminer enumTypeDeterminer;
@Autowired
public ShorthandAnnotationPlugin(
DescriptionResolver descriptions,
EnumTypeDeterminer enumTypeDeterminer) {
super(descriptions, enumTypeDeterminer);
this.descriptions = descriptions;
this.enumTypeDeterminer = enumTypeDeterminer;
}
@Override
public void apply(ParameterContext context) {
Optional<GameIdParam> gameIdParam = context.resolvedMethodParameter().findAnnotation(GameIdParam.class);
if (gameIdParam.isPresent()) {
GameIdParam annotation = gameIdParam.get();
// Instantiate an ApiParam so we can take default values for attributes we didn't override.
ApiParam parentAnnotation = AnnotationUtils.synthesizeAnnotation(ApiParam.class);
context.parameterBuilder().name(ofNullable(annotation.name())
.filter(((Predicate<String>) String::isEmpty).negate()).orElse(null))
.description(ofNullable(descriptions.resolve(annotation.value()))
.filter(((Predicate<String>) String::isEmpty).negate()).orElse(null))
.parameterAccess(ofNullable(parentAnnotation.access())
.filter(((Predicate<String>) String::isEmpty).negate())
.orElse(null))
.defaultValue(ofNullable(parentAnnotation.defaultValue())
.filter(((Predicate<String>) String::isEmpty).negate())
.orElse(null))
.allowMultiple(parentAnnotation.allowMultiple())
.allowEmptyValue(parentAnnotation.allowEmptyValue())
.required(parentAnnotation.required())
.scalarExample(new Example(parentAnnotation.example()))
.complexExamples(examples(parentAnnotation.examples()))
.hidden(parentAnnotation.hidden())
.collectionFormat(parentAnnotation.collectionFormat())
.order(SWAGGER_PLUGIN_ORDER);
}
}
@Override
public boolean supports(DocumentationType documentationType) {
return pluginDoesApply(documentationType);
}
}
I used Springfox's ApiParamParameterBuilder as an example.
Now, I can use my @GameIdParam
@PostMapping("/{gameId}/info")
public String play(@GameIdParam @PathVariable int gameId) // ...
This pattern could be generalized to work with a series of custom shorthand annotations. It's not pretty and it introduces another level of indirection that people who know Springfox Swagger won't be familiar with.
Hope that helps! Good luck!