swagger multiple versions in path - java

I have the following spring get mapping (org.springframework.web.bind.annotation.GetMapping) in a controller:
#GetMapping("/v{version:[1-2]}/something/{id}")
I want to be able to access the two versions of the api in swagger. This is my swagger config:
#Bean
public Docket v1(SwaggerProperties swaggerProperties) {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("V1")
.select()
.paths(regex("/v1/.*")).build()
}
#Bean
public Docket v2(SwaggerProperties swaggerProperties) {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("V2")
.select()
.paths(regex("/v2/.*")).build()
}
This does not work, the only thing I can see in swagger when I remove the paths selector is:
/v{version}/something/{id}
And I would like to see:
/v1/something/{id}
When selecting the V1 group in the swagger group selector:
And this when selecting V2:
/v2/something/{id}

Actually, you might need to implement custom PathProvider to unwrap the mapping path "/v{version:[1-2]}/something/{id}" into particular one in Docket like that:
//in Docket
.pathProvider(new ParticularVersionPathProvider("v1"))
...
class ParticularVersionPathProvider extends AbstractPathProvider {
...
private String version;
BasePathAwareRelativePathProvider(String version){
this.version = version;
}
#Override
public String getOperationPath(String operationPath) {
//very schematically
return operationPath.replace("v{version}",version);
}
}
see this complete example

Related

How to force a DTO class to be in Swagger models

I have a SpringBoot application with Swagger 3.17.1.
I have one abstract class AbstractDtoClass and one DTO class DtoClass that extends the first one.
I have several REST request definitions, and all returns a AbstractDtoClass object, none returns DtoClass.
As a result, the Swagger models, i.e. the DTO models I can find in "definitions" from /v2/api-docs, contains AbstractDtoClass but not DtoClass.
I would like DtoClass to be in the Swagger models too. How can I do that?
I have tried to put #SwaggerDefinition on the DtoClass definition.
I have tried to put #ApiModel(parent = AbstractDtoClass.class) on the DtoClass definition.
I have tried to put #ApiModel(subTypes = {DeclarationDto.class}, discriminator = "DeclarationDto") on the AbstractDtoClass definition even though I am not sure I am using discriminator properly.
Nothing have worked.
Can anyone help me please?
You can make use of following method to add additional models that are not part of any annotation or are perhaps implicit.
springfox.documentation.spring.web.plugins.Docket#additionalModels(ResolvedType first, ResolvedType... remaining)
Below is the sample DocketConfig,
#Configuration
#EnableSwagger2
public class SwaggerConfig {
private final TypeResolver typeResolver;
public SwaggerConfig(final TypeResolver typeResolver) {
this.typeResolver = typeResolver;
}
#Bean
public Docket docketApi() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.rmurugaian.service.pricing.server"))
.build()
.additionalModels(typeResolver.resolve(DummyDTO.class));
}

How to document additional models in Swagger for an inaccessible Controller?

I have a controller that I have to keep generic by having it accept a String as #RequestBody and return a String, i.e., String processRequest(#RequestBody String json) {...}
I don't have control over the source code of that controller, but I can get to it programmatically.
The real objects that are going to be passed in and returned are defined elsewhere in a series of request messages: RequestMessage1.java, RequestMessage2.java, etc. Responses are likewise: Response1.java, Response2.java1.
The controller also hands off the processing of these requests to a Processor that looks something like this (Request1Processor.java): Response1 process(RequestMessage1 message).
My question is this.
Is there a way to configure swagger such that it exposes the REST controller class's endpoint, i.e, processRequest, but shows all these Processor classes with their inputs and outputs as the documentation for that controller?
I saw as part of documentation the ability to add models that are not "reachable". I tried the method that's in documentation like this:
#Autowired
private TypeResolver typeResolver;
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.additionalModels(typeResolver.resolve(Date.class));
}
However, the additional Date model did not show up in swagger-ui.html.
What am I doing wrong here?
Also, is there a way to somehow show that RequestMessage1 type will have a response with Response1?
Date class was an unfavourable example to test because it is treated as a string.
Data Types
...
string (this includes dates and files)
Try it again with real models you want to document additionally:
#Bean
public Docket api(TypeResolver typeResolver) {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.additionalModels(typeResolver.resolve(RequestMessage1.class, Response1.class)));
}
An alternative solution is to extend the already existing docket object instead of creating a new one.
Here is a solution:
import com.fasterxml.classmate.TypeResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.spring.web.plugins.Docket;
#Configuration
public class SwaggerConfiguration {
private final TypeResolver typeResolver;
#Autowired
public SwaggerConfiguration(TypeResolver typeResolver) {
this.typeResolver = typeResolver;
}
#Autowireda
public void createAdditionalModelDocumentation(Docket docket) {
docket.additionalModels(typeResolver.resolve(RequestMessage1.class),
typeResolver.resolve(Response1.class));
}
}

how to do friendly base url for swagger 2.8.0

I'm trying to change base access url for API documentation. The url is "http://localhost:8080/swagger-ui.html". I want to get something like "http://localhost:8080/myapi/swagger-ui.html".
I use Springfox 2.8.0 Swagger, Java 8, Spring Boot 2.0
The swagger configuration is:
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {
#Bean
public Docket api(ServletContext servletContext) {
return new Docket(DocumentationType.SWAGGER_2)
.pathProvider(new RelativePathProvider(servletContext) {
#Override
public String getApplicationBasePath() {
return "/myapi";
}
})
.select()
.apis(RequestHandlerSelectors.any())
.paths(Predicates.not(PathSelectors.regex("/error")))
.build()
.useDefaultResponseMessages(false);
}
}
Custom path provider had to help, but I still get access to api documentation by using url "http://localhost:8080/swagger-ui.html". If I use url "http://localhost:8080/myapi/swagger-ui.html", I get 404 error. Look at the screenshot below.
UPD: Springfox is abandoned
Springfox Swagger had always been kinda dirty solution with a lot of unclearness and bugs, but by now (2021 Q4) it hadn't been updated for more than a year.
The final straw was the fact that Springfox Swagger 3.0 doesn't work anymore with Spring Boot 2.6.x.
So, if you reading this, please, consider switching over to https://springdoc.org/ instead.
It's a pretty straightforward conversion and they do a great job of
documenting it. https://springdoc.org/#migrating-from-springfox.
For those who use Springfox Swagger 3.0.0
Here's the working configuration for changing base url for docs:
springfox:
documentation:
swaggerUi:
baseUrl: /documentation
openApi:
v3:
path: /documentation/v3/api-docs
swagger:
v2:
path: /documentation/v2/api-docs
You can edit your SwaggerConfiguration like that:
Take care to replace the package (which need to be the one
containing your REST controllers), the host, and the PATH you need
#Configuration
#EnableSwagger2
public class SwaggerConfiguration implements WebMvcConfigurer {
public static final String PATH = "/myapi";
#Bean
public Docket api() {
final var package = "com.julia.rest";
final var host = "localhost:8080";
return new Docket(DocumentationType.SWAGGER_2)
.host(host)
.select()
.apis(RequestHandlerSelectors.basePackage(package))
.paths(PathSelectors.any())
.build();
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
final var apiDocs = "/v2/api-docs";
final var configUi = "/swagger-resources/configuration/ui";
final var configSecurity = "/swagger-resources/configuration/security";
final var resources = "/swagger-resources";
registry.addRedirectViewController(PATH + apiDocs, apiDocs).setKeepQueryParams(true);
registry.addRedirectViewController(PATH + resources, resources);
registry.addRedirectViewController(PATH + configUi, configUi);
registry.addRedirectViewController(PATH + configSecurity, configSecurity);
registry.addRedirectViewController(PATH, "/");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(PATH + "/**").addResourceLocations("classpath:/META-INF/resources/");
}
}
Another solution is by changing the spring-boot URL context-path:
Edit pour application.properties file:
server.servlet.context-path=/myapi
Or if you have an application.yml file:
server:
servlet:
context-path: /myapi
Warning: It will change the base path of all your web services, not only Swagger
I also have faced this problem and tried many possible resolutions, and nothings didn't help really.
In my case, I can't use any resource redirect as swagger must be accessible as locally as on google cloud by match path /api-docs/**. and on google cloud any resource redirection will be denied in my case. All resources must be loading also from this path
here is my solution:
springfox-swagger2 and springfox-swagger-ui of version 2.9.2
#EnableSwagger2
#Configuration
public class SwaggerCommonConfig implements WebMvcConfigurer {
public static final String PATH = "/api-docs";
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController(PATH, "/");
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(PATH + "/**").addResourceLocations("classpath:/META-INF/resources/");
}
}
and as springfox don't have any possibilities to do it by another way, in my case, we just will create simple controller that will be translating resource requests from our custom path to standard springfox. (it's not very elegant part but as it is :))
#RestController
#RequestMapping(SwaggerGatewayCommonConfig.PATH)
#RequiredArgsConstructor
public class SwaggerController {
private final RestTemplate restTemplate;
private final static String V2_API_DOCS = "/v2/api-docs";
private final static String SWAGGER_RESOURCES_CONFIGURATION_UI = "/swagger-resources/configuration/ui";
private final static String SWAGGER_RESOURCES_CONFIGURATION_SECURITY = "/swagger-resources/configuration/security";
private final static String SWAGGER_RESOURCES = "/swagger-resources";
private final static Pattern pattern = Pattern.compile("http[s]*://([^/]+)", Pattern.CASE_INSENSITIVE);
#Value("${server.port}")
private String port;
#GetMapping(V2_API_DOCS)
#SuppressWarnings("unchecked")
public Map<String, Object> getV2ApiDocs(HttpServletRequest request) {
Matcher matcher = pattern.matcher(request.getRequestURL().toString());
matcher.find();
Map<String, Object> resp = (Map<String, Object>) restTemplate.getForObject(toLocalSwaggerUrl(V2_API_DOCS), Map.class);
//we have to replace standard host, to requested host. as swagger UI make api requests from this host
resp.put("host", matcher.group(1));
return resp;
}
#GetMapping(SWAGGER_RESOURCES_CONFIGURATION_UI)
public Object getSwaggerResourcesConfigurationUi() {
return restTemplate.getForObject(toLocalSwaggerUrl(SWAGGER_RESOURCES_CONFIGURATION_UI), Object.class);
}
#GetMapping(SWAGGER_RESOURCES_CONFIGURATION_SECURITY)
public Object getSwaggerResourcesConfigurationSecurity() {
return restTemplate.getForObject(toLocalSwaggerUrl(SWAGGER_RESOURCES_CONFIGURATION_SECURITY), Object.class);
}
#GetMapping(SWAGGER_RESOURCES)
public Object getSwaggerResources() {
return restTemplate.getForObject(toLocalSwaggerUrl(SWAGGER_RESOURCES), Object.class);
}
private String toLocalSwaggerUrl(String path) {
return "http://localhost:" + port + path;
}
}
I hope it will save time to somebody faced it also =)
Good luck
Swagger base access url is constructed from your base application path.So if you change your base application path , you will get the desired behavior.But also all your apis will be changed to that path. You can find how to change it here How to set base url for rest in spring boot? .
What you did was too change how swagger call other apis from your application, not to change his base url. There are some tricks to change the swagger base url without changing application base path (moving manually all swagger resources), but i do not recommend that.

How to hide repository-controller from Swagger UI

I am using spring-boot-starter-parent 1.3.3.RELEASE. I am unable to disable repository controller in Swagger UI
I have disabled the other unwanted endpoints using this link.
How to disable repository-controller from swagger UI?? kindly provide your inputs
If use the follow, it returns only the search repository which written by me but I need the entity endpoints also. Those entities endpoints will return by spring defaults.
.apis(RequestHandlerSelectors.basePackage("bla.blablah.bla"))
Kindly refer the image:
When initializing your Docket in your Application class, you can filter package names easily, therefore it will create your api for only given base package:
#Bean
public Docket swaggerSpringMvcPlugin(){
return new Docket( DocumentationType.SWAGGER_2 )//
.select().apis( RequestHandlerSelectors.basePackage( "com.blabla.bla" ) )//
.build();
}
Another solution:
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(Predicates.not(RequestHandlerSelectors.basePackage("org.springframework.boot")))
.build();
Another solution that negates a specific package:
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
.apis(RequestHandlerSelectors.basePackage("org.springframework.boot").negate())
.build();
I solved the same issue by creating my own Predicate and adding them to the Docket configuration. You can get the Group name (repository-controller) from RequestHandler Object.
My code:
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(GroupNameFilter())
.build();
}
private Predicate<RequestHandler> GroupNameFilter(){
return new Predicate<RequestHandler>() {
#Override
public boolean apply(RequestHandler input) {
return !input.groupName().equals("repository-controller");
}
};
}

Api annotation's description is deprecated

In Swagger, the #Api annotation's description element is deprecated.
Deprecated.
Not used in 1.5.X, kept for legacy support.
Is there a newer way of providing the description?
I found two solutions for Spring Boot application:
1. Swagger 2 based:
Firstly, use the tags method for specify the tags definitions in your Docket bean:
#Configuration
#EnableSwagger2
public class Swagger2Config {
public static final String TAG_1 = "tag1";
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors.basePackage("my.package")).build()
.tags(new Tag(TAG_1, "Tag 1 description."))
// Other tags here...
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("My API").version("1.0.0").build();
}
}
After, in RestController just add the #Api annotation with one (or more) of the your tags:
#Api(tags = { SwaggerConfig.TAG_1 })
#RestController
#RequestMapping("tag1-domain")
public class Tag1RestController { ... }
2. Swagger 3 based (OpenAPI):
Similarly, use the addTagsItem method for specify the tags definitions in your OpenAPI bean:
#Configuration
public class OpenApiConfig {
public static final String TAG_1 = "tag1";
#Bean
public OpenAPI customOpenAPI() {
final Info info = new Info()
.title("My API")
.description("My API description.")
.version("1.0.0");
return new OpenAPI().components(new Components())
.addTagsItem(createTag(TAG_1, "Tag 1 description."))
// Other tags here...
.info(info);
}
private Tag createTag(String name, String description) {
final Tag tag = new Tag();
tag.setName(name);
tag.setDescription(description);
return tag;
}
}
Finally, in RestController just add the #Tag annotation:
#Tag(name = OpenApiConfig.TAG_1)
#RestController
#RequestMapping("tag1-domain")
public class Tag1RestController { ... }
This is the correct way to add description to your Swagger API documentation for Swagger v1.5:
#Api(tags = {"Swagger Resource"})
#SwaggerDefinition(tags = {
#Tag(name = "Swagger Resource", description = "Write description here")
})
public class ... {
}
The reason why it's deprecated is that previous Swagger versions (1.x) used the #Api description annotation to group operations.
In the Swagger 2.0 specification, the notion of tags was created and made a more flexible grouping mechanism. To be API compliant, the description field was retained so upgrades would be easy, but the correct way to add a description is though the tags attribute, which should reference a #Tag annotation. The #Tag allows you to provide a description and also external links, etc.
I tried above solutions but they didn't work for me.
To add a title and description to the documentation you create ApiInfo and Contact objects like in example below.
Then you simply add apiInfo object to your Swagger Docket.
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
#EnableSwagger2
#Configuration
public class SwaggerConfig {
private Contact contact = new Contact("", "", "");
private ApiInfo apiInfo = new ApiInfo(
"Backoffice API documentation",
"This page documents Backoffice RESTful Web Service Endpoints",
"1.0",
"",
contact,
"",
"");
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
.apis(RequestHandlerSelectors.basePackage(
PaymentsController.class.getPackage().getName()
))
.paths(PathSelectors.ant("/api/v1/payments" + "/**"))
.build()
.useDefaultResponseMessages(false)
.globalOperationParameters(
newArrayList(new ParameterBuilder()
.name("x-authorization")
.description("X-Authorization")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(false)
.build()));
}
}
Above code produces a description like in a screenshot below.
I too wondered what to do about uses of the deprecated description (showing up as warnings in my IDE).
Well, on closer inspection it turned out that description is not used anywhere in Swagger UI. After that the solution (in our case*) became clear: simply remove those descriptions.
(*In our codebase, with clean class and method names etc, there was certainly no need for such "API descriptions" for the reader of the code. I would have tolerated having these bits of Swagger-related noise in the codebase if they added some value in Swagger UI, but since they didn't, the only sensible thing was to throw them away.)
I found that the following works by combining both the #Api and #Tag annotations building off of this answer.
The value within the tags field of the #Api annotation needs to match the value within the name field of the #Tag annotation.
#Api(tags = "Controller API")
#Tag(name = "Controller API", description = "This controller performs API operations")
public class ReportStatusConsumerController {
}
An old question but may help using swagger 3
#Configuration
#EnableSwagger2
public class SwaggerConfig {
// Swagger configuration
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo( this.apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("API Reference").version("1.0.0")
.description("something")
.license("Apache 2.0")
.build();
}
public void addResouceHandler(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}

Categories