How to hide #Deprecated marked APIs/Controllers when generating Swagger via Docket? - spring

I am looking for a way to hide the APIs marked as #Deprecated so they are not visible on the swagger UI.
One way is to use the hidden = true in the #Operation annotation but thats too manual and not easy to toggle back if I decide that I want to view the Deprecated endpoints again.
Is there another way that we can achieve this via Docket config?
#Operation(
tags = "Deprecated",
deprecated = true
)
#GetMapping("/api/customer/order")
public String viewOrders() {
//
}
#Bean
public Docket orderApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("order")
.apiInfo(metadata())
.select()
.paths(PathSelectors.ant("/order/**"))
.build();
}

Related

Remove Controller Name in Swagger UI

Is there any way to hide the controller name in the swagger-ui.
My class is like this. I do not want my controller name on the ui.
#Api(tags = {"group"})
public class MyControllerName {}
I did check some existing answers. for ex: How to remove controller list from Swagger UI did not help at all.
Create Docket bean and assign to it custom ApiInfo object, in this way:
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.controller"))
.paths(PathSelectors.ant("/foos/*"))
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfo(
"My REST API",
"Some custom description of API.",
"API TOS",
"Terms of service",
new Contact("John Doe", "www.example.com", "myeaddress#company.com"),
"License of API", "API license URL", Collections.emptyList());
}
You can exclude any controller:
import { Controller, Get } from '#nestjs/common';
import { ApiExcludeController } from '#nestjs/swagger';
import { AppService } from './app.service';
#ApiExcludeController()
#Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
#Get()
getHello(): string {
return this.appService.getHello();
}
}
This sould be good feature request to springfox team. If you need to customize the swagger-UI you should be doing it on your own.
Maybe the below steps are useful for someone.
Go to https://github.com/swagger-api/swagger-ui
Download the latest code
customize whatever you want to customize
package either as a web jar or as resources in your application
create resource handler mapping if required
Refer - https://github.com/springfox/springfox/issues/1108

OpenApi: Unable to override content type using OperationBuilderPlugin

I'm wasting a lot of time trying to figure out how to set a default MediaType for my Spring Controllers in the output of the api-docs generated by SpringFox (Docket openapi v3.0). Lastly I've found an almost undocumented interface OperationBuilderPlugin that theorically allows me to set some properties in the OperationContext but unfortunately though the property I'm looking for seems to be ignored when the operation is built:
#Override
public void apply(OperationContext context) {
context.operationBuilder()
.produces(new LinkedHashSet<>(
Collections.singletonList(MediaType.APPLICATION_JSON_VALUE)));
}
Also tried to set the produces directly into Docket but still no luck
#Bean
public Docket api() {
....
return new Docket(DocumentationType.OAS_30)
.securityContexts(Collections.singletonList(securityContext()))
.securitySchemes(Collections.singletonList(authenticationScheme))
.useDefaultResponseMessages(true)
.consumes(new HashSet<>(Arrays.asList(MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE)))
.produces(new LinkedHashSet<>(Arrays.asList("application/json", "application/xml")))
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
...
}
I don't want to specify the return MediaType at controller class level (or method level) so my last chance if I can't find a cleaner solution is to manually replace the */* with the mediatype i want after I download the api-docs.json from the remote url.
Does anybody ever had the same issue?
Any help would be much appreciated, thanks in advance
Managed to resolve by myself. In the end I've overridden the bean ResponseMessagesReader with another bean MyResponseMessagesReader that do almost the same of the original class, but in the method below I've changed the condition of produces that in the case of an empty set it adds the json media type
private void applyMyReturnTypeOverride(OperationContext context) {
...
if (produces.isEmpty()) {
produces.add(MediaType.APPLICATION_JSON);
}
...
}
This is the only joint point I was able to slip into the processo of operation creation, maybe there's a better way (implementing OperationBuilderPlugin with some neatest strategy) but my time was ticking out for this solution.
Hope this helps other people that needs the same API generation behaviour

Springdoc GroupedOpenApi not following global parameters set with OperationCustomizer

When using GroupedOpenApi to define an API group, the common set of parameters that are added to every endpoint is not present in the parameters list.
Below are the respective codes
#Bean
public GroupedOpenApi v1Apis() {
return GroupedOpenApi.builder().group("v1 APIs")
// hide all v2 APIs
.pathsToExclude("/api/v2/**", "/v2/**")
// show all v1 APIs
.pathsToMatch("/api/v1/**", "/v1/**")
.build();
}
And the class to add the Standard Headers to all the endpoints
#Component
public class GlobalHeaderAdder implements OperationCustomizer {
#Override
public Operation customize(Operation operation, HandlerMethod handlerMethod) {
operation.addParametersItem(new Parameter().$ref("#/components/parameters/ClientID"));
operation.addSecurityItem(new SecurityRequirement().addList("Authorization"));
List<Parameter> parameterList = operation.getParameters();
if (parameterList!=null && !parameterList.isEmpty()) {
Collections.rotate(parameterList, 1);
}
return operation;
}
}
Actual Output
Expected Output
Workaround
Adding the paths to be included/excluded in the application properties file solves the error. But something at the code level will be much appreciated.
Attach the required OperationCustomizerobject while building the Api Group.
#Bean
public GroupedOpenApi v1Apis(GlobalHeaderAdder globalHeaderAdder) {
return GroupedOpenApi.builder().group("v1 APIs")
// hide all v2 APIs
.pathsToExclude("/api/v2/**", "/v2/**")
// show all v1 APIs
.pathsToMatch("/api/v1/**", "/v1/**")
.addOperationCustomizer(globalHeaderAdded)
.build();
}
Edit: Answer updated with reference to #Value not providing values from application properties Spring Boot
Alternative to add and load OperationCustomizer in the case you declare yours open api groups by properties springdoc.group-configs[0].group= instead definition by Java code in a Spring Configuration GroupedOpenApi.builder().
#Bean
public Map<String, GroupedOpenApi> configureGroupedsOpenApi(Map<String, GroupedOpenApi> groupedsOpenApi, OperationCustomizer operationCustomizer) {
groupedsOpenApi.forEach((id, groupedOpenApi) -> groupedOpenApi.getOperationCustomizers()
.add(operationCustomizer));
return groupedsOpenApi;
}

Mandatory header for all API in openapi 3.0

I am using OpenAPI 3.0 with Spring-boot 5 and therefore have no configuration YAML. I have a header that contains the client Identification ID(This is not an authentication header). I want to make that a mandatory header param. Added below OpenAPI configuration
#Configuration
public class OpenAPIConfiguration {
#Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.components(new Components()
.addParameters("myCustomHeader", new Parameter().in("header").schema(new StringSchema()).required(true).description("myCustomHeader").name("myCustomHeader")))
.info(new Info()
.title("My Rest Application")
.version("1.2.26"));
}
}
However, the swagger UI does not show the required param in any API. Can someone help as to what I am doing wrong?
Adding parameter definition to a custom OpenAPI bean will not work because the parameter won't get propagated to the operations definitions. You can achieve your goal using OperationCustomizer:
#Bean
public OperationCustomizer customize() {
return (operation, handlerMethod) -> operation.addParametersItem(
new Parameter()
.in("header")
.required(true)
.description("myCustomHeader")
.name("myCustomHeader"));
}
The OperationCustomizer interface was introduced in the springdoc-openapi 1.2.22. In previous versions you would need to use OpenApiCustomiser:
#Component
public class MyOpenApiCustomizer implements OpenApiCustomiser {
private static final List<Function<PathItem, Operation>> OPERATION_GETTERS = Arrays.asList(
PathItem::getGet, PathItem::getPost, PathItem::getDelete, PathItem::getHead,
PathItem::getOptions, PathItem::getPatch, PathItem::getPut);
private Stream<Operation> getOperations(PathItem pathItem) {
return OPERATION_GETTERS.stream()
.map(getter -> getter.apply(pathItem))
.filter(Objects::nonNull);
}
#Override
public void customise(OpenAPI openApi) {
openApi.getPaths().values().stream()
.flatMap(this::getOperations)
.forEach(this::customize);
}
private void customize(Operation operation) {
operation.addParametersItem(
new Parameter()
.in("header")
.required(true)
.description("myCustomHeader")
.name("myCustomHeader"));
}
}

Setting base names based on request headers in ReloadableResourceBundleMessageSource

I have a spring boot application which has the following messages outside the jar/war
/i18n/myApplication/messages/companyA/messages.properties
/i18n/myApplication/messages/companyA/messages_fr_FR.properties
/i18n/myApplication/messages/companyB/messages.properties
/i18n/myApplication/messages/companyB/messages_fr_FR.properties
/i18n/myApplication/messages/companyB/messages_zh_HK.properties
In request header I would get the following
X-Company=CompanyA
Accept-Language=fr-FR
How do I set baseNames dynamically based on the company and locale?
Also for CompanyA I do not want to look for messages in CompanyB for Eg:
if
X-Company=CompanyA
Accept-Language=zh-HK
I should be able to default to en-IN properties.
Any new company that gets added I don't want to make any code changes to support it
I was thinking of extending ReloadableResourceBundleMessageSource
In order for our application to be able to determine which locale is currently being used, we need to add a LocaleResolverbean:
`
#Bean
public LocaleResolver localeResolver() {
    SessionLocaleResolver slr = new SessionLocaleResolver();
    slr.setDefaultLocale(Locale.US);
    return slr;
}
`
The LocaleResolver interface has implementations that determine the current locale based on the session, cookies, the Accept-Language header, or a fixed value.
Next, we need to add an interceptor bean that will switch to a new locale based on the value of the lang parameter appended to a request:
`
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
    LocaleChangeInterceptor lci = new LocaleChangeInterceptor();
    lci.setParamName("lang");
    return lci;
}
`
In order to take effect, this bean needs to be added to the application’s interceptor registry.
To achieve this, our #Configuration class has to implement the WebMvcConfigurer interface and override the addInterceptors() method:
`
#Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(localeChangeInterceptor());
}
`
It you read the documentation giù will find the simplest example Accept-language header which perfectly fits your needs

Resources