Reputation: 367
I am trying to get Swagger to read the API documentation from a properties file swagger.properties
but can't. In @ApiOperation
annotation there is an error saying: Attribute value must be constant
. Any suggestions about how to solve this and be able to read the documentation from a properties file?
Here is the Controller code:
package com.demo.student.demo.controller;
import com.demo.student.demo.entity.Student;
import com.demo.student.demo.service.StudentService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping(value = "/v1/students")
@Api(description = "Set of endpoints for Creating, Retrieving, Updating and Deleting of Students.")
public class StudentController {
private final String message;
public StudentController(@Value("${test.swagger.message}") String message){
this.message=message;
}
@Autowired
private StudentService studentService;
@GetMapping
@ApiOperation(message)
public List<Student> findAll(){
return studentService.findAl();
}
}
And also, how can I inject a value at the class level in the @API(description)?
Upvotes: 2
Views: 5266
Reputation: 2152
There is a workaround. But you need an additional dependency for that - springfox
.
You can write a plugin which will inject a text from an external file into you the @ApiOperation
description
field. I am using it in my project for injecting markdown files. It comes very handy since Swagger supports markdown and having a separate file for each endpoint provides you an opportunity to write extensive API descriptions (also within a markdown editor if you are using IntelliJ IDEA or similar).
Here is the code you need for it:
Custom annotation (@ApiDescription
) for each endpoint you want to provide a description. The value of the annotation will be the file path to your markdown or properties file. Later the plugin will look for the file at the provided filepath and set the description to the content of the file.
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiDescription {
String value() default "";
}
Plugin itself. It is an extensibility point. In this case we want to swap or set the value of description of @ApiOperation
annotation later on. Check out the Springfox Plugins.
...
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import springfox.documentation.spring.web.DescriptionResolver;
...
@Component
public class ApiDescriptionPlugin implements OperationBuilderPlugin {
private final DescriptionResolver resolver;
@Autowired
public ApiDescriptionPlugin(DescriptionResolver resolver) {
this.resolver = resolver;
}
@Override
public void apply(OperationContext context) {
Optional<ApiDescription> descOptional = context.findAnnotation(ApiDescription.class);
boolean hasText = descOptional.isPresent() && StringUtils.hasText(descOptional.get().value());
if(!hasText) {
return;
}
final String file = descOptional.get().value();
final URL url = Resources.getResource(file);
String description;
try {
description = Resources.toString(url, StandardCharsets.UTF_8);
} catch(IOException e) {
log.error("Error while reading markdown description file {}", file, e);
description = String.format("Markdown file %s not loaded", file);
}
context.operationBuilder().notes(resolver.resolve(description));
}
@Override
public boolean supports(DocumentationType type) {
return true;
}
}
Simply annotate the endpoints with @ApiDescription("/notes/auth/login.md")
(the file must be in resources
folder)
You can adapt this example to work with properties file(s) (I don't know how your structure looks like and how you separate different API descriptions). This workaround with markdown files is useful for writing extensive descriptions and keeping them away from the actual code.
It works with Swagger 2.0.
Give it a try.
Upvotes: 5
Reputation: 2701
As the error message says the attribute values must be constants and Spring can't inject values to static final fields. Similarly, it is also impossible to inject values outside the class level (ie. the description for the @Api
annotation)
A workaround would be creating a class with only constants which are all final static Strings
like this
public final class Constants {
public static final String API_DESCRIPTION = "description";
public static final String FIND_ALL_MESSAGE= "message";
}
and using it in the controller
@Api(description = Constants.API_DESCRIPTION)
public class StudentController {
@ApiOperation(Constants.FIND_ALL_MESSAGE)
public List<Student> findAll(){...}
}
for Swagger however, for some of the fields it is possible using ${key}
syntax. According to the documentation at https://springfox.github.io/springfox/docs/current/#property-file-lookup since version 2.7 it is possible for the following fields:
@ApiParam#value()
@ApiImplicitParam#value()
@ApiModelProperty#value()
@ApiOperation#value()
@ApiOperation#notes()
@RequestParam#defaultValue()
@RequestHeader#defaultValue()
Upvotes: 2