How to hide endpoints from OpenAPI documentation with Springdoc - java

Springdoc automatically generates a API documentation for all handler methods. Even if there are no OpenAPI annotations.
How can I hide endpoints from the API documentation?

The #io.swagger.v3.oas.annotations.Hidden annotation can be used at the method or class level of a controller to hide one or all endpoints.
(See: https://springdoc.org/faq.html#how-can-i-hide-an-operation-or-a-controller-from-documentation)
Example:
#Hidden // Hide all endpoints
#RestController
#RequestMapping(path = "/test")
public class TestController {
private String test = "Test";
#Operation(summary = "Get test string", description = "Returns a test string", tags = { "test" })
#ApiResponses(value = { #ApiResponse(responseCode = "200", description = "Success" ) })
#GetMapping(value = "", produces = MediaType.TEXT_PLAIN_VALUE)
public #ResponseBody String getTest() {
return test;
}
#Hidden // Hide this endpoint
#PutMapping(value = "", consumes = MediaType.TEXT_PLAIN_VALUE)
#ResponseStatus(HttpStatus.OK)
public void setTest(#RequestBody String test) {
this.test = test;
}
}
Edit:
Its also possible to generate the API documentation only for controllers of specific packages.
Add following to your application.properties file:
springdoc.packagesToScan=package1, package2
(See: https://springdoc.org/faq.html#how-can-i-explicitly-set-which-packages-to-scan)

If you are working with Swagger Api and you want to hide specific endpoint then use #ApiOperation(value = "Get Building",hidden=true) on that endpoint...hidden attribute should be true.
#RestController
#Api(tags="Building")
#RequestMapping(value="/v2/buildings")
public class BuildingsController {
#ApiOperation(value = "Get Building",hidden=true)
#GetMapping(value = "/{reference}")
public Account getBuildings(#PathVariable String reference) {
....
}

Its also possible to generate the API doc only for specific Path.
Add following to your application.yml file:
springdoc:
paths-to-match: /api/**, /v1

Related

Java Inheritance Annotation

I didn't understand how works the inheritance annotation on interface. For my case, I use openapi-generator to generate a server interface. After that, I create my own implementation of the interface and it works. I only need to add the #RestController on my implementation class.
The question is :
The #Path annotation on my generate interface don't have #Inherited annotation (like the #Post method), so if I correctly understood I shouldn't have my #Path (and #Post) inherited on my implementation. So I'm wondering why my application work and the path is correct ? Shouldn't I have a 404 ?
The generated interface :
#Path("/esignCallback")
#Api(description = "the esignCallback API")
#javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", date = "2022-05-30T18:27:51.487+02:00[Europe/Berlin]")public interface EsignCallbackApi {
#POST
#Consumes({ "application/json" })
#ApiOperation(value = "callback for esign service", notes = "", tags={ "EsignCallback" })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully received the callback", response = Void.class),
#ApiResponse(code = 404, message = "Request not found", response = Void.class) })
Response esignCallback(#Valid #NotNull CallbackRequestChange callbackRequestChange);
}
My implementation :
#RestController
public class EsignCallback implements EsignCallbackApi {
#Override
public Response esignCallback(
#Valid #NotNull CallbackRequestChange callbackRequestChange) {
// implementation
}
}
thanks in advance for your help.

How to change declared body type from String to custom DTO type in Swagger using SpringBoot and SpringFox

I have a rest controller with one method. This method takes one String argument annotated as #RequestBody. For some reason not mentioned here, I'm forced to use type String and manually convert it to TestDTO. From the API's consumer point of view body is type of TestDTO and I want to show this type in SwaggerUI.
Unfortunately (which is quite obvious) swagger shows that body is type of String. Look at the picture below.
What I want to achieve is to have String body in java code and TestDTO in swagger code. How can I force Swagger to show it? I tried to find annotations and its properties, but failed.
Rest controller code below:
#RestController
#Api(tags = { "test" }, description = "test related resources")
public class TestController {
#Autowired
ObjectMapper mapper;
#RequestMapping(path = "/test", method = RequestMethod.POST)
public void confirm(#RequestBody String requestBody) throws IOException {
//do sth with body
TestDTO dto = mapper.readValue(requestBody, TestDTO.class);
//do sth with dto
}
}
class TestDTO{
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
I figured it out. Two changes need to be made.
First, like in #Dave Pateral's answer #ApiImplicitParams must be added
#RestController
#Api(tags = { "test" }, description = "test related resources")
public class TestController {
#Autowired
ObjectMapper mapper;
#ApiImplicitParams({
#ApiImplicitParam(name = "requestBody", required = true,
dataType = "TestDTO", paramType = "body")
})
#RequestMapping(path = "/test", method = RequestMethod.POST)
public void confirm(#RequestBody String requestBody) throws IOException {
//do sth with body
TestDTO dto = mapper.readValue(requestBody, TestDTO.class);
//do sth with dto
}
}
And then implicit Model must be registered in the docket, minimal working example below
#Configuration
public class SwaggerConfiguration {
#Autowired
private TypeResolver typeResolver;
#Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.additionalModels(typeResolver.resolve(TestDTO.class));
}
}
And the result is
Try put this annotation on your method:
#ApiImplicitParam(name = "test", value = "testDTO", required = true, dataType = "TestDTO")

Swagger Controller Mappings

With Swagger-UI I am trying to display methods that have the same base URL underneath one Swagger container regardless of which controller they fall under. Imagine this code.
// Current Swagger Config Class
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(getPaths())
.build()
.apiInfo(getApiInfo());
}
private Predicate<String> getPaths(){
return or(regex("/attributes.*"),
regex("/other.*"));
}
// Controller 1
#RestController
#RequestMapping("/attributes")
#Api(value = "User Attribute Services", description = "User Attributes API")
public class UserAttributesController {
#Autowired
private UserAttributesService userService;
#Autowired
private UserAttributeValidator userAttributeValidator;
#RequestMapping(value = "/user/search", method = RequestMethod.POST, produces = "application/json")
public List<User> searchUser(#RequestBody List<UserDTO> userDTOList){
// meaningless implementation ...
}
#RequestMapping(value = "/user/insert", method = RequestMethod.POST)
public boolean insertUser(#RequestBody List<UserDTO> userDTOList){
// meaningless implementation ...
}
#RequestMapping(value = "/user/update{id}", method = RequestMethod.PATCH)
public boolean updateUser(#PathVariable id, #RequestBody UserDTO updateDetails){
// meaningless implementation ...
}
#RequestMapping(value = "/user/delete/{id}", method = RequestMethod.DELETE)
public boolean deleteUser(#PathVariable id){
// meaningless implementation ...
}
}
// Controller 2
#RestController
#RequestMapping("/attributes")
#Api(value = "Tech Attribute Services", description = "Tech Attributes API")
public class TechAttributesController {
#Autowired
private TechAttributesService techService;
#Autowired
private TechAttributeValidator techAttributeValidator;
#RequestMapping(value = "/tech/search", method = RequestMethod.POST, produces = "application/json")
public List<Tech> searchTech(#RequestBody List<TechDTO> techDTOList){
// meaningless implementation ...
}
#RequestMapping(value = "/tech/insert", method = RequestMethod.POST)
public boolean insertTech(#RequestBody List<TechDTO> techDTOList){
// meaningless implementation ...
}
#RequestMapping(value = "/tech/update{id}", method = RequestMethod.PATCH)
public boolean updateTech(#PathVariable id, #RequestBody TechDTO updateDetails){
// meaningless implementation ...
}
#RequestMapping(value = "/tech/delete/{id}", method = RequestMethod.DELETE)
public boolean deleteTech(#PathVariable id){
// meaningless implementation ...
}
}
My application swagger page is displaying the attributes underneath seperate containers. Eventhough I specified the regex path of '/attributes' in my Swagger Configuration the fact that these are in different controllers is overriding what I want. If I put all these methods into 1 controller I get the desired result, but this is only a subset of the attribute classes I have and I do not want a massive controller that spans over 1k lines.
Extra: If you noticed the services are almost identical for the attributes. I have been trying to find a clever way of declaring them once and simply overriding the method declations instead of redefining similiar services over again, but because the #RequestMapping annotation needs to be a constant value I could not find a way to change the annotation depending on the implementation rather than the declaration.
Swagger-UI Picture

#JsonView doesn't work for me (Spring 4.1.5, Jackson 2.5.1)

I have a field in my Entity with #JsonView annotation:
#JsonView(View.Secure.class)
private String password;
Inside my controller:
#RequestMapping(method = RequestMethod.GET, produces = "application/json")
#JsonView(View.Secure.class)
public ResponseEntity<?> getAllUsers(){
return createUserListResponse();
}
My View class:
public class View {
public static class Secure {}
}
I've expected that response will contain only "password" field, but instead it contains nothing. When i remove annotation #JsonView(View.Secure.class) from Controller - it works as usual and returns all fields. What am i doing wrong? Is it required to add some additional configuration into Spring config?
I used this tutorial: https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring

java.lang.AssertionError: Content type not set - Spring Controller Junit Tests

I am trying to do some unit testing on my controllers. No matter what I do all controller tests return
java.lang.AssertionError: Content type not set
I am testing that the methods return json and xml data.
Here is an example of the controller:
#Controller
#RequestMapping("/mypath")
public class MyController {
#Autowired
MyService myService;
#RequestMapping(value="/schema", method = RequestMethod.GET)
public ResponseEntity<MyObject> getSchema(HttpServletRequest request) {
return new ResponseEntity<MyObject>(new MyObject(), HttpStatus.OK);
}
}
The unit test is set up like this:
public class ControllerTest() {
private static final String path = "/mypath/schema";
private static final String jsonPath = "$.myObject.val";
private static final String defaultVal = "HELLO";
MockMvc mockMvc;
#InjectMocks
MyController controller;
#Mock
MyService myService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = standaloneSetup(controller)
.setMessageConverters(new MappingJackson2HttpMessageConverter(),
new Jaxb2RootElementHttpMessageConverter()).build();
when(myService.getInfo(any(String.class))).thenReturn(information);
when(myService.getInfo(any(String.class), any(Date.class))).thenReturn(informationOld);
}
#Test
public void pathReturnsJsonData() throws Exception {
mockMvc.perform(get(path).contentType(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(jsonPath(jsonPath).value(defaultVal));
}
}
I am using:
Spring 4.0.2
Junit 4.11
Gradle 1.12
I have seen the SO question Similiar Question but no matter what combination of contentType and expect in my unit test I get the same result.
Any help would be much appreciated.
Thanks
Your solution depends on what kinds of annotation you want to use in your project.
You can add #ResponseBody to your getSchema method in Controller
Or, maybe adding produces attribute in your #RequestMapping can solve it too.
#RequestMapping(value="/schema",
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON_VALUE} )
Final choice, add headers to your ResponseEntity (which is one of the main objective of using this class)
//...
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json; charset=utf-8");
return new ResponseEntity<MyObject>(new MyObject(), headers, HttpStatus.OK);
Edit : I've just seen you want Json AND Xml Data, so the better choice would be the produces attribute:
#RequestMapping(value="/schema",
method = RequestMethod.GET,
produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE} )
You need to add
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE,
method = RequestMethod.GET
value = "/schema")
And <mvc:annotation-driven />in your xml config or #EnableWebMvc

Categories