Swagger configured in Spring Boot shows only methods with POST and GET mapping - spring

Swagger configured in Spring Boot shows only one method with POST mapping and one method with GET mapping from every controller. Swagger ignores another methods with GET and POST mapping and ignores all methods with PUT and DELETE mappings. My configuration:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("my.project.controllers"))
.paths(PathSelectors.ant("/api/*"))
.build();
}
}
Dependency in pom.xml:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
<scope>compile</scope>
</dependency>
My controllers code:
#RestController
#RequestMapping(value = "/api/users", produces = "application/json; charset=UTF-8")
public class UserController {
#Autowired
private UserService userService;
protected UserService getService() {
return userService;
}
#RequestMapping(method = GET)
public Page<User> query(#RequestParam Map<String, Object> parameters, Pageable pageable) {
return getService().query(parameters, pageable);
}
#ResponseStatus(CREATED)
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<User> create(#RequestBody User entity) {
return ResponseEntity.status(HttpStatus.CREATED).body(getService().create(entity));
}
#RequestMapping(value = "/{id:[0-9]+}", method = RequestMethod.PUT)
public ResponseEntity<User> update(#PathVariable Long id, #RequestBody User entity) {
return ResponseEntity.ok(getService().update(id, entity));
}
#RequestMapping("/current")
public ResponseEntity current() {
return ResponseEntity.ok(userService.getUser());
}
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/{id:[0-9]+}/enable", method = RequestMethod.POST)
public void enable(#PathVariable("id") final long id) {
userService.enable(id);
}
#ResponseStatus(HttpStatus.OK)
#RequestMapping(value = "/{id:[0-9]+}/disable", method = RequestMethod.POST)
public void disable(#PathVariable("id") final long id) {
userService.disable(id);
}
#RequestMapping(value = "/histories", method = RequestMethod.GET)
public List<UserHistory> histories() {
return userService.histories();
}
}
May be i need add some more configuration or add something else?

Based on your controller, I think you should add one more star in the path matcher in your swagger config:
.paths(PathSelectors.ant("/api/**"))
e.g /api/users/current would not be matched by the /api/* but by /api/**, and this why you are getting only the base path endpoints documented.

Related

Define a bean from external library

I have a poblem.
I am working with api binance and I add this library to use all functionalities.
<dependency>
<groupId>io.github.binance</groupId>
<artifactId>binance-connector-java</artifactId>
<version>1.3.0</version>
</dependency>
This is the code of external library
public class Market {
private final String baseUrl;
private final RequestHandler requestHandler;
private final boolean showLimitUsage;
public Market(String baseUrl, String apiKey, boolean showLimitUsage) {
this.baseUrl = baseUrl;
this.requestHandler = new RequestHandler(apiKey);
this.showLimitUsage = showLimitUsage;
}
When I try to inject this classe I always receive the following error on SpringBoot
Consider defining a bean of type 'com.binance.connector.client.impl.spot.Market' in your configuration.
I have an controller and I use like this.
#RestController
public class ExampleController {
#Autowired
private Market market;
#RequestMapping(value = "/ping", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<String> ping() {
return ResponseEntity.ok(market.ping());
}
}
I try to add the class with ComponentScan.
The code of external library is only for read.

How to bypass the Spring #PreAuthorize annotation on RestController for tests?

How can I create a bypass #Preauthorize so that I can test in local with out calling the actual because annotation will be loaded before the class loads ?
#RestController
#RequestMapping("/test")
public class ResourceController {
#RequestMapping(method = GET)
#PreAuthorize
#ResponseBody
public String message(){ return "Hello World"; }
You can use spring-security-test to achieve this.
pom.xml
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>5.3.3.RELEASE</version>
<scope>test</scope>
</dependency>
Supposedly your controller looks like:
#PreAuthorize("hasRole('ROLE_ADMIN')")
public User createUser(final User user) {
......
}
And your test can look like:
public class MyControllerTests {
private MockMvc mvc;
#BeforeEach
void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
void testCreateWithProperPermission() throws Exception {
final User user = new User();
user.setName("Test");
final MvcResult mvcResult = mvc.perform(MockMvcRequestBuilders.post("/v1/foo/").with(user("foo").roles("ADMIN"))
.content(new ObjectMapper().writeValueAsString(user))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
final String responseBody = mvcResult.getResponse().getContentAsString();
final User created = new ObjectMapper().readValue(responseBody, User.class);
// verify the saved entity's data is correct
assertThat(created).isNotNull();
assertThat(created)
.hasFieldOrPropertyWithValue("name", user.getName());
}
You can have a test profile in your code which you then activate when running tests against the code. You can then use the predefined user and password in your tests.
#Configuration
public class TestConfig {
#EnableWebSecurity
#Profile("test")
class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user").password("password").roles("ROLE1", "ROLE2", "ROLE3").build());
return manager;
}
}
}

Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type:

I am trying to expose REST service, but while hitting it from POSTMAN i am getting below :
WARNING: Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class java.util.ArrayList
Where as i have also included below jar files which are required :
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.1</version>
</dependency>
Here is my REST controller Code :
#RestController
#RequestMapping("/MayankAPI")
public class TestRestAPI {
#RequestMapping(value="/sayHello" , method = RequestMethod.POST)
public TestPojo postData(#RequestBody String payload) {
System.out.println("Hello post"+payload);
TestPojo payload1=new TestPojo();
payload1.setStudentName("Jack");
return payload1;
}
#RequestMapping(value="/sayHello" , method = RequestMethod.GET)
public List<TestPojo> getData(String payload) {
System.out.println("Hello get"+payload);
List<TestPojo> payload1=new ArrayList<TestPojo>();
TestPojo tp = new TestPojo();
tp.setStudentName("Jack");
payload1.add(tp);
return payload1;
}
Here is my bean which i am trying to return :
public class TestPojo {
private String studentName;
private String studentId;
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
}
Please help me where i am doing wrong.
I know it too late but still
Alternate Solution:
you need to enable Spring project as Web MVC as follow:
#Configuration
#EnableWebMvc
#ComponentScan("basePackages = com.test")
public class MiBenefitConfiguration
{
}
This is happening because MappingJackson2HttpMessageConverter is not registered in my App config file as below.
#Configuration
#ComponentScan("basePackages = com.test")
public class MiBenefitConfiguration extends WebMvcConfigurationSupport{
#Bean
public ObjectMapper getObjectMapper() {
return new ObjectMapper();
}
#Bean
public MappingJackson2HttpMessageConverter messageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(getObjectMapper());
return converter;
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(messageConverter());
addDefaultHttpMessageConverters(converters);
}
}

Swagger UI does not show all the controllers of my project

I have few controllers which have the same request path prefix i.e.
/v4/users/{userId}/
And then the methods in the controllers are annotated with separate path mappings. The swagger ui shows only one of those controllers.
#Component
public class BaseController {
}
/******************/
#RestController
#RequestMapping("/v4/users/{userId}/payment")
public class PaymentController extends BaseController {
#RequestMapping(value = "/initiate", method = RequestMethod.POST)
public HttpEntity<Object> initiatePayment() {...}
#RequestMapping(value = "/success", method = RequestMethod.GET)
public HttpEntity<Object> success() {...}
}
#RestController
#RequestMapping("/v4/users/{userId}/preferences")
public class CustomerPreferencesController extends BaseController{
#RequestMapping(value = "/deals/{dealId}", method = RequestMethod.POST)
public HttpEntity<Object> favouriteDeal(...) {...}
//some more methods
}
There is one more controller that has the same path prefix i.e. /v4/users/{userId} which shows up on swagger-ui but not the above two controllers.
pom.xml entries:
<!-- Swagger Spring -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.0.2</version>
</dependency>
UPDATE - 1
#Bean
public Docket mobileAPI() {
return new Docket(DocumentationType.SWAGGER_2)
.host(swaggerHost)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo())
.pathMapping("/");
}

how to get list of objects via requestbody in spring boot api

To get list of objects via #RequestBody in controller and process each object in a list to do a business logic.
I have tried this but not working
#RequestMapping(value="/updateservicetype", method=RequestMethod.POST,produces="application/json")
public #ResponseBody ServiceTypesMessage updateServiceType(#RequestBody List<BarberServiceType> serviceTypes,final HttpServletResponse response){
also tried following:
#RequestMapping(value="/updateservicetype", method=RequestMethod.POST,produces="application/json")
public #ResponseBody ServiceTypesMessage updateServiceType(#RequestBody BarberServiceType[] serviceTypes,final HttpServletResponse response){
Below works for me
#RequestMapping(value = "/payments", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody List<Payment> batchCreate(#RequestBody List<Payment> payments) {
return paymentService.create(payments);
}
You will need Jackson in the class path
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.0</version>
</dependency>
Json in put is
[{"sort":"10-20-30","account":"1234"},{"sort":"10-20-30","account":"1234"}]
You should use a wrapper class for your required list,
in another word, create a new class and name it "BarverServiceTypeRequest"
this class should contain your List:
public class BarberServiceTypeRequest{
private List<BarberServiceType> serviceTypes;
public List<BarberServiceType> getserviceTypes() {
return serviceTypes;
}
public void setServiceTypes(List<BarberServiceType>serviceTypes) {
this.date = date;
}
then your controller will be like:
#RequestMapping(value="/updateservicetype", method=RequestMethod.POST,produces="application/json")
public #ResponseBody ServiceTypesMessage updateServiceType(#RequestBody BarberServiceTypeRequest request, final HttpServletResponse response){
Your JSON object will be like:
{
"serviceTypes": [{"sort":"10-20-30","account":"1234"},{"sort":"10-20-30","account":"1234"}]
}
sure you can access your list with:
request.getserviceTypes();

Resources