Spring Data REST Endpoints Not Generating In Swagger UI - spring-boot

I've implemented a controller using #BasePathAwareController which also takes advantage of Spring Data REST (for finds to expose sort/size etc.)
along with some custom endpoints (for updates etc.). The application is working as expected and the endpoints Spring
Data REST generates are working as expected and I can see the self links appear in the responses, however, i can't see
these endpoints in Swagger UI. I can just see my custom endpoints
defined in my Controller.
According to this post I need to use Swagger 3.0.0-SNAPSHOT with #EnableSwagger2WebMvc
Here's my configurations:
My app.yml:
spring:
data:
rest:
basePath: /api/v1
My POM file:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/>
</parent>
<properties>
<springfox.swagger.version>3.0.0-SNAPSHOT</springfox.swagger.version>
</properties>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox.swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox.swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-data-rest</artifactId>
<version>${springfox.swagger.version}</version>
</dependency>
Swagger Config File:
#Configuration
#Import(SpringDataRestConfiguration.class)
#EnableSwagger2WebMvc
public class SwaggerConfig {
#Bean
public Docket api(ServletContext servletContext) {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("my.package.name"))
.paths(PathSelectors.any())
.build().apiInfo(apiEndPointsInfo());
}
private ApiInfo apiEndPointsInfo() {
return new ApiInfoBuilder().title("My App REST API's")
.description("My App REST API's")
.version("1.0").build();
}
}
My repo:
#RepositoryRestResource(exported=true, collectionResourceRel="group", path="group")
public interface GroupRepository extends JpaRepository<Group, Long>, JpaSpecificationExecutor<Group> {
}
Why can't i see the default endpoints that Spring Data REST produces?

I found the issue to my problem. I wasn't aware that Spring Data REST doesn't expose the generated endpoints under the controller package name I had specified here:
.apis(RequestHandlerSelectors.basePackage("my.package.name"))
so when I changed the above line to:
.apis(RequestHandlerSelectors.any())
and I could see the JPA respository endpoints.

Related

Spring boot - springdoc custom url for swagger-ui

I need to use a specific url for Swagger-ui. I have tried to use the property "springdoc.swagger-ui.path" but it only redirects.
application.propperties:
springdoc.swagger-ui.path=/helloWorld/swagger
Url in browser: http://localhost:8181/helloWorld/swagger
but the url changes to the following when hits enter button:
http://localhost:8181/manager/swagger-ui/index.html?configUrl=/manager/swagger/swagger.json/swagger-config
the question is, how can i make the path be only http://localhost:8181/helloWorld/swagger/index.html or http://localhost:8181/helloWorld/swagger once i've hit enter (I need the word "swagger-ui" and configUrl disappear)?
I`m uisng Springdoc and even tried with springfox
Pom.xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.2</version>
</dependency>
application.propperties:
springdoc.api-docs.path=/helloWorld/swagger/swagger.json
springdoc.swagger-ui.path=/helloWorld/swagger
I guess it's version dependent regarding the answer.
if you're using 2.8.0 and above, you could use following dependencies:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
</dependency>
And create a controller which will just redirect to the main swagger page, like this:
#Controller
public class SwaggerController {
#RequestMapping("/")
public String index() {
return "redirect:swagger-ui.html";
}
}
But if you use 2.6.1 or like that you can personalize your swagger configuration java class extending WebMvcConfigurerAdapter and overwriting the addViewController, like this:
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addRedirectViewController(MAIN_URL, SWAGGER_UI_URL).setContextRelative(true);
registry.addRedirectViewController(MAIN_URL + "/", SWAGGER_UI_URL).setContextRelative(true);
}
That's it. Both approaches used the same dependencies but with different versions.

Failed to start bean 'documentationPluginsBootstrapper' Spring boot Swagger implementation using Spring Fox?

I am using springfox-boot-starter dependency for swagger UI in my spring boot application but when I try to run the application I am getting below error
Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NoSuchMethodError: org.springframework.plugin.core.PluginRegistry.getPluginFor(Ljava/lang/Object;)Ljava/util/Optional;
at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:184)
at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:52)
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356)
at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:157)
at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:121)
at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:885)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
my config file:
#Bean
public Docket productApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.swagger.demo.controller"))
.paths(regex("/student.*"))
.build()
.enableUrlTemplating(true)
.produces(DEFAULT_PRODUCES_AND_CONSUMES)
.consumes(DEFAULT_PRODUCES_AND_CONSUMES)
.apiInfo(apiEndPointsInfo());
}
private ApiInfo apiEndPointsInfo() {
return new ApiInfoBuilder()
.title("demo")
.description("demo")
.version("1.0")
.build();
}
Below is the dependency which I am using.
pom.xml file:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
Why I am getting above error I don't get anything. Any help would be highly appreciated. Thanks in advance.
I had a similar issue and found the solution here.
Basically you have to include in the file application.properties the following configuration:
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
As it seems Spring Boot 2+ set as default the PathPathern-based matcher, while Spring Fox expects the Ant-based matcher.
The problem with this solution is that you cannot use Spring Actuator, since it uses PathPattern based URL matching. In this case, check this Spring Fox issue.
SwaggerConfig.java
#Configuration
//Enable Swagger
#EnableSwagger2
public class SwaggerConfig
{
//creating bean
#Bean
public Docket api()
{
//creating constructor of Docket class that accepts parameter DocumentationType
return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.basePackage("com.example.demo.controller")).paths(PathSelectors.regex("/api.*")).build();
}
}
And Application.properties file :
spring.mvc.pathmatch.matching-strategy=ant-path-matcher

How to make both validation annotations in a ConfigurationProperties bean and a #FeignClient interface work together?

Let's say I have this application.yml (which will be environment-dependent e.g. via Spring profiles):
app.remote:
url: http://whatever.url.it.is:8080/
and matching Java-style configuration properties class:
#Configuration
#ConfigurationProperties("app.remote")
public class MyRemoteProperties {
#NotBlank
private String url;
// matching getter/setter...
}
I want some kind of client for my remote url:
#Service
#FeignClient(value = "remote", url = "${app.remote.url}")
public interface MyRemote {
#GetMapping("/what/ever/rest/api")
String stuff();
}
Unfortunately I can't get the validation work for MyRemoteProperties e.g. when the app.remote.url property is blank (empty) the application doesn't start (Spring fails at wiring the MyRemote bean) and I get this error:
Caused by: java.lang.IllegalStateException: No Feign Client for
loadBalancing defined. Did you forget to include
spring-cloud-starter-netflix-ribbon?
(and I don't want load-balancing; I assume this is because the URL is empty at some point, then it expects some load-balancer config hence Ribbon here in the error message).
Or maybe I don't known how to plug it into the MyRemote interface's configuration, e.g. I also tried:
#FeignClient(value = "remote", configuration = MyRemoteProperties.class)
But same result.
How do I get this validation thing to work?
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
At some point where the interface is called:
#Service
public RandomServiceOrController {
#Autowired
private MyRemote myRemote;
public void processMyStuff() {
// ...
String myStuff = myRemote.stuff();
// ...
}
}
Don't forget the #Validated annotation on your Java properties class:
#Validated
#Configuration
#ConfigurationProperties("app.remote")
public class MyRemoteProperties {
#NotBlank
private String url;
// matching getter/setter...
}
Your application won't start because of the missing property, not because of a non-defined-loadbalancing-client-you-don't-need (thus making its error message more awkward).

SpringBoot 2 + Junit5: null with #Value

I have an application with SpringBoot2 and Junit5, and now I'm trying to make a test. I have a this class called OrderService that looks like this:
#Component
public class OrderService {
#Value("#{'${food.requires.box}'.split(',')}")
private List<String> foodRequiresBox;
#Value("#{'${properties.prioritization}'.split(',')}")
private List<String> prioritizationProperties;
#Value("${further.distance}")
private Integer slotMeterRange;
#Value("${slot.meters.long}")
private Double slotMetersLong;
As you can see, the class has many #Value annotations that extracts values from application.properties file.
In the POM file I have these dependences:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.0.5.RELEASE</version>
</dependency>
Tn the test/resources folder I have the application.properties file with this information:
properties.prioritization:vip,food
food.requires.box:pizza,cake,flamingo
further.distance:2
slot.meters.long:0.5
The test file looks like this:
#ExtendWith(SpringExtension.class)
#TestPropertySource(locations="classpath:application.properties")
public class OrderServiceTest {
OrderService orderService;
#BeforeEach
void before(){
orderService = new OrderService();
}
#Test
void findAll() {
Order order = new Order().withDescription("2x Pizza with Salad\\n2x Kebab with Fries\\n1x Hot dog with Fries\\n2x Pizza with Fries");
assertTrue(orderService.orderHasFood.test(order));
}
}
But the test throws NullPointerException when it tries to use foodRequiresBox, so there is a problem to read the application.properties file.
Could you tell how can I read the application.properties file for the tests?
1st Solution
I would recommend to use Spring's internal annotation called #SpringJUnitConfig
This annotation is actually the same as #ExtendWith(SpringExtension.class) BUT you can configure your spring application contexts for your test in the same way you used to use #ContextConfiguration.
Or if you want a full Spring Boot Test you could just combine:
#SpringJUnitConfig
#SpringBootTest
public class OrderServiceTest {
...
}
2nd Solution
Another way is to not use Spring at all, but mock all the internal stuff with e.g. Mockito and write a plain simple Unit Test.
You could then set your normally via Spring injected annotated #Value fields via org.springframework.test.util.ReflectionTestUtils.
I'd recommend using org.springframework.test.util.ReflectionTestUtils (as indicated in #mrkernelpanic second solution) using the setField() method, in order to avoid initializing the full Spring context.
Here is a snippet:
// ReflexionTestUtils.setField(targetObject, "targetFieldName", valueToInject);
ReflexionTestUtils.setField(orderService, "foodRequiresBox", "pizza,cake,flamingo");

Unable to load Swagger-UI with spring boot

I am trying to dynamically document my SpringBoot application's REST API using Swagger.
In order to do that I am using springfox and it's swagger integration. I have followed the official documentation, which can be found here.
Once I finished implementing the integration I can access the JSON output via http://localhost:8080/v2/api-docs, however I am unable to see any information in the Swagger UI via http://localhost:8080/swagger-ui.html.
This is my setup:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<properties>
<java.version>1.8</java.version>
<main.basedir>${basedir}/../..</main.basedir>
<ing.continuous-delivery.version>00.04.04</ing.continuous-delivery.version>
<maven.assembly.version>2.3</maven.assembly.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.3.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- tag::spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<!-- end::spring -->
<!-- tag::web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
<version>1.5.3.RELEASE</version>
</dependency>
<!-- end::web -->
<!-- tag::swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
<!-- end:: swagger -->
</dependencies>
</project>
Web MVC configuration:
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Security configuration:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.authorizeRequests().antMatchers("/**", "/swagger-resources").permitAll();
httpSecurity.csrf().disable();
httpSecurity.headers().frameOptions().disable();
}
}
Swagger configuration:
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any()).build();
}
}
Thanks for the help!
I'm bad for English, that's why GoogleTranslate.
Nowadays spingdoc is available, which implements openapi3, it gives you the same expected functionality and it is much easier to use it, already in an answer to another question I explained how to do it, in addition to how to customize the information of the api using java annotations
SpringDoc simplifies the generation and maintenance of API documents, based on the OpenAPI 3 specification, for Spring Boot 1.x and 2.x applications.
For magic to happen we simply add the dependency to our pom:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.2.32</version>
</dependency>
then access the description that already has it http://localhost:8080/v3/api-docs/
and for swagger: http://localhost:8080/swagger-ui.html
that's all there is to it.
While trying to access the swagger UI, you need to add your API's base url before /swagger-ui.html.
Something like this:
http://localhost:8080/nameOfAPI/swagger-ui.html
This article helped me:
https://www.baeldung.com/swagger-2-documentation-for-spring-rest-api

Resources