I'm writing some API and I want to integrate Documentation while writing code, so I found Swagger as a good way to to this.
I used Swagger core v3 notations, so my classes are something like:
#RestController
#RequestMapping("/api/v1/bundles")
#OpenAPIDefinition(
info = #Info(
title = "Lifecycle Management RESTful API.",
version = "1",
description = "TODO",
license = #License(name = "Apache 2.0", url = "xxx"),
contact = #Contact(url = "xxx", name = "xx", email = "xxx#xxx.fr")
))
public class RestBundle {
#GetMapping(value = "/{nodeId}",
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.OK)
#ResponseBody
#Operation(summary = "Get all bundles",
description = "Get all available bundles status from a specific node")
public Something(..) {
//Something ...
}
}
And I create a configuration class:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("ANF Orchestrator")
.description("REST API for ANF Orchestrator")
.license("Apache 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.termsOfServiceUrl("")
.version("1.0.0")
.contact(new Contact("Amine","xxx", "aalaouie#laas.fr"))
.build();
}
}
ANd I want to enable the UI of Swagger to get the documentation, but when I enter to:
.../swagger-ui.html
I get:
Unable to render this definition
The provided definition does not specify a valid version field.
Please indicate a valid Swagger or OpenAPI version field. Supported version fields are swagger: "2.0" and those that match openapi: 3.0.n(for example, openapi: 3.0.0).
Try to extend your SwaggerConfig class with WebMvcConfigurationSupport and override its method called addResourceHandlers with implementation like this:
#Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
On top of class, can you try removing this annotation, as the SwaggerConfig already contains the Docket info.
#OpenAPIDefinition(
info = #Info(
title = "Lifecycle Management RESTful API.",
version = "1",
description = "TODO",
license = #License(name = "Apache 2.0", url = "xxx"),
contact = #Contact(url = "xxx", name = "xx", email = "xxx#xxx.fr")
))
Related
I'm using SpringBoot with the following dependency
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.12</version>
</dependency>
The controller class (#RestController) has one entry point (#GetMapping), and this entry point should return a List of the object : MyClass.java. I added Swagger annotations above the method in order to create API documentation via a swagger UI page.
The swagger documentation should indicate that the return object is of type
List< MyClass>
But how should I do that ? If I do
"#Schema(implementation = List< MyClass >.class)"
there is a compile error.
Swagger annotations:
#Operation(....)
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "successful operation",
content = { #Content(mediaType = "application/json",
schema = #Schema(implementation = ????)) }),
#ApiResponse(...),
#ApiResponse(...)
#GetMapping(value = "/aaa", produces = MediaType.APPLICATION_JSON_VALUE)
public List<MyClass> getAaa(...)
{
return ...
}
You need to use an ArraySchema annotation for this and assign it to the array attribute instead of the schema attribute of the #Content annotation.
You don't need to specify List.class only its type parameter MyClass.class.
#Operation(
summary = "Get a list of users",
description = "Get a list of users registered in the system",
responses = {#ApiResponse(
responseCode = "200",
description = "The response for the user request",
content = {
#Content(
mediaType = "application/json",
array = #ArraySchema(schema = #Schema(implementation = User.class))
)
})
}
)
#GET
#SecurityRequirement(name = "JWT")
#Path("/user")
public List<User> getUsers() {
return null;
}
I am trying to set Additional Properties element into the Open API Schema 3.X but unfortunatel I was not able to find anything in the documentation that help me on it.
I have a Application in Spring boot and it is using Spring doc OAS that relies on Swagger OAS as transitive dependency.
Let me pick some code snippet here:
#GetMapping("/{accountId}")
#Operation(summary = "Get account by account id", tags = TAG)
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "Return a specific account queried by path",
content = { #Content(mediaType = "application/json",
schema = #Schema(implementation = AccountDetailsDTO.class)) }),
#ApiResponse(responseCode = "404", description = "No accounts found",
content = #Content) })
public ResponseEntity<AccountDetailsDTO> getAccountDetailsByClientId(#PathVariable("accountId") Integer accountId) { }
This attribute is default to true and What I would like to see is as false like that below:
If you want explicitly set the attribute to false you can a TransformationFilter (annoted #Component for Spring) to set additionalProperties to false for each component of you specification if you are using Springfox.
If you are using Springdoc, you can add a OpenApiCustomiser bean, see examples
Example with Springdoc OpenAPI
#Bean
public OpenApiCustomiser openApiCustomiser() {
return openApi -> openApi.getComponents().getSchemas().values().forEach( s -> s.setAdditionalProperties(false));
}
Example with Springfox framework
#Component
#Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class OpenApiTransformationFilter implements WebMvcOpenApiTransformationFilter
{
public boolean supports(#NotNull DocumentationType delimiter)
{
return SwaggerPluginSupport.pluginDoesApply(delimiter);
}
#Override
public OpenAPI transform(OpenApiTransformationContext<HttpServletRequest> context)
{
OpenAPI openApi = context.getSpecification();
openApi.getComponents().getSchemas().values().forEach(schema -> schema.setAdditionalProperties(false));
return openApi;
}
}
One workaround might be define a dummy class that contains the type information, then use that as the #Schema#implementation class in your #APIResponse.
static class YourTypeMap extends java.util.HashMap<String, YourType> {};
Then:
#APIResponse(
responseCode = "200",
content = #Content(
mediaType = "application/json",
schema = #Schema(implementation = YourTypeMap.class)))
Credits: MikeEdgar
I have integrated Swagger (OpenAPI) with Spring Webflux as mentioned here: https://springdoc.org/#spring-weblfuxwebmvcfn-with-functional-endpoints using RouterOperation. The integration works fine and is accessible at /swagger-ui.html
However, for POST APIs, I am not seeing the "Request" sample when I click on "Try it out" button. My Post API accepts a Json as Request Body.
How do I configure this ? Can that be done via Annotations along with RouterOperation or something else ?
Edit: Below is my Router class code:
#Configuration
public class MyRouter {
#RouterOperations({
#RouterOperation(path = "/data", beanClass = MyHandler.class, beanMethod = "getData"),
#RouterOperation(path = "/allData", beanClass = MyHandler.class, beanMethod = "getAllData") })
#Bean
public RouterFunction<ServerResponse> route(MyHandler MyHandler) {
return RouterFunctions
.route(RequestPredicates.POST("/data").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), MyHandler::getData)
.andRoute(RequestPredicates.GET("/allData").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), MyHandler::getAllData);
}
}
Upon adding RouterOperations annotation I can see the swagger-ui showing both the GET and POST APIs correctly but not the request schema sample.
I also came across yaml / json file to describe this. But I am not getting where to put this file in my application so that swagger-ui uses it.
Finally found it
Using #Operation and #Schema, can define the class that is required as input in request body. This will be shown as sample json structure in Swagger-ui. No other configuration required.
#RouterOperations({
#RouterOperation(
path = "/data", beanClass = MyHandler.class, beanMethod = "getData",
operation = #Operation(
operationId = "opGetData",
requestBody = #RequestBody(required = true, description = "Enter Request body as Json Object",
content = #Content(
schema = #Schema(implementation = ApiRequestBody.class))))),
#RouterOperation(path = "/allData", beanClass = MyHandler.class, beanMethod = "getAllData")})
#Bean
public RouterFunction<ServerResponse> route(MyHandler myHandler) {
return RouterFunctions
.route(RequestPredicates.POST("/data").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), myHandler::getData)
.andRoute(RequestPredicates.GET("/allData").and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), myHandler::getAllData);
}
I am using swagger 3.0.0-Snapshot to create documentation for my Spring Boot application.
My maven dependencies are
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-spring-webmvc</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
My swagger config class is as simple as possible:
#Configuration
#EnableSwagger2WebMvc
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.select()
.apis(RequestHandlerSelectors.basePackage("com.mycompany.cs"))
.paths(PathSelectors.any())
.build()
.pathMapping("/")
.useDefaultResponseMessages(false);
}
And my controller method has the following annotation:
#ApiOperation(value = "Hello world", httpMethod = "POST")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK",
examples = #Example(value = #ExampleProperty(mediaType = "application/json",
value = exampleValue)))
})
It is working and shows in Swagger UI "Example Value" field value that has constant string exampleValue that is private static String.
The question is how to pass the content of json file that is in resources folder to #ExampleProperty value?
I tried to read file content in static block and pass it to initialize final String with it, but then the compiler says that "Attribute value has to be constant".
The content of json file must be shown in example field in Swagger UI.
Good news is that Swagger is using Spring and it is possible to use the power of DI.
For instance, you want to add new functionality to ServiceModelToSwagger2MapperImpl. Create your own component that extends it and mark it primary. Spring will autowire your implementation of ServiceModelToSwagger2Mapper abstract class.
#Component
#Primary
#Slf4j
public class ServiceModelToSwagger2MapperExtensionImpl extends ServiceModelToSwagger2MapperImpl {
For instance, you want it to read the content of the file and put it to the example field:
#Override
protected Map<String, Response> mapResponseMessages(Set<ResponseMessage> from) {
Map<String, Response> responses = super.mapResponseMessages(from);
responses.forEach((key, response)-> {
Map<String, Object> examples = response.getExamples();
examples.entrySet().forEach(example -> {
Object exampleObject = example.getValue();
if (exampleObject instanceof String) {
String exampleValue = (String) exampleObject;
if (exampleValue.startsWith("file:")) {
String fileContent = readFileContent(exampleValue);
example.setValue(fileContent);
}
}});
});
return responses;
}
private String readFileContent(String example) {
String fileContent = "";
try {
String fileName = example.replace("file:", "");
File resource = new ClassPathResource(fileName).getFile();
if(resource.exists()) {
fileContent
= new String(Files.readAllBytes(resource.toPath()));
}
} catch (
IOException e) {
log.error("Cannot read swagger documentation from file {}", example);
}
return fileContent;
}
And here is an example of usage in your controller:
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK",
examples = #Example(value = #ExampleProperty(mediaType = "application/vnd.siren+json",
value = "file:/data/controller-responses/reponse.json")))
})
Swagger basic authorization not working with #Api annotation .But when used with #Apioperation it is working fine . I want to apply basic authorization at controller level rather than at method level .
used like this :
#RestController
#Slf4j
#Api(value="API related ",authorizations = {#Authorization(value="basicAuth")})
#RequestMapping(value="invoices",produces =MediaType.APPLICATION_JSON_UTF8_VALUE)
#SuppressWarnings("rawtypes")
public class InvoiceController {
#SuppressWarnings("unchecked")
#GetMapping
#ApiOperation(value = "${InvoiceController.getAll.notes}", notes="${InvoiceController.getAll.notes}",response = Invoice.class)
#ApiResponses(value = {#ApiResponse(code = 200, message = "Successfully retrieved list of invoices")})
public #ResponseBody ResponseEntity<Response> getAll(#Valid PaginationDto pagination,#Valid InvoiceFilterCriteriaDto filter)
throws GenericServiceException{
}
}
in main class , created Docket like below by mentioning the basic auth :
List<SecurityScheme> schemeList = new ArrayList<>();
schemeList.add(new BasicAuth("basicAuth"));
return new Docket(DocumentationType.SWAGGER_2)
.forCodeGeneration(true)
.produces(new HashSet<>(Arrays.asList( new String[] { MediaType.APPLICATION_JSON_UTF8_VALUE.toString()})))
.apiInfo(apiInfo())
.securitySchemes(schemeList)
Fixed by adding securityContexts to Docket config.
Please refer to https://www.baeldung.com/spring-boot-swagger-jwt