How to make Spring Boot Reactive endpoints visible to SpringDoc OpenApi - spring-boot

I am working on a new Spring Boot reactive server application. The application exposes reactive endpoints as shown below:
#Configuration
public class XXXXRouter {
#Bean
public RouterFunction<ServerResponse> routes(ClaimsHandler handler) {
return nest(path("/api/xxxx"), nestedRoutes(handler));
}
private static RouterFunction<ServerResponse> nestedRoutes(xxxxHandler handler) {
return route(GET("/"), handler::allClaims)
.andRoute(GET("/{id}"), handler::byId)
.andRoute(GET("/{id}/notes"), handler::notesById);
}
}
I need to make these endpoints to swagger-ui like framework. I am using following dependencies to generate required swagger-ui and api-docs:
implementation 'org.springdoc:springdoc-openapi-webflux-core:1.2.32'
implementation 'org.springdoc:springdoc-openapi-webflux-ui:1.2.32'
I also have the following API config:
#Configuration
public class OpenAPIConfig {
#Bean
public OpenAPI customOpenAPI() {
return new OpenAPI().components(new Components()).info(new Info().title("Contact Application API")
.description("This is a sample Spring Boot RESTful service using springdoc-openapi and OpenAPI 3."));
}
}
localhost:8080/swagger-ui.html shows swagger-ui page. But, endpoints are not shown there.
Here is the output from localhost:8080/v3/api-docs:
{"openapi":"3.0.1","info":{"title":"Contact Application API","description":"This is a sample Spring Boot RESTful service using springdoc-openapi and OpenAPI 3."},"servers":[{"url":"http://localhost:8080","description":"Generated server url"}],"paths":{},"components":{}}
My question is how do we make the reactive endpoints available to swagger-ui.html?

Related

How to disable interceptor call for Actuators in Springboot application

I am trying to implement Prometheus in my microservices based spring boot application, deployed over weblogic server. As part of POC,I have included the configs as part of one war. To enable it, i have set below config -
Application.properties
management:
endpoint:
prometheus:
enabled: true
endpoints:
web:
exposure:
include: "*"
Gradle -
implementation 'io.micrometer:micrometer-registry-prometheus'
But the actuator request is getting blocked by existing interceptors. It asks to pass values in headers specific to our project. Through postman(http:localhost:8080/abc/actuator/prometheus), I am able to test my POC(with required headers) and it returns time-series data expected by Prometheus. But Prometheus is not able to scrap data on its own(with pull approach), as the call lacks headers in request.
I tried following links (link1,link2) to bypass it, but my request still got intercepted by existing interceptor.
Interceptors blocking the request are part of dependent jars.
Edited --
I have used following way to exclude all calls to interceptor -
#Configuration
public class MyConfig implements WebMvcConfigurer{
#Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(new MyCustomInterceptor()).addPathPatterns("**/actuator/**");
}
}
MyCustomInterceptor
#Component
public class MyCustomInterceptor implements HandlerInterceptor{
}
I have not implemented anything custom in MyCustomInterceptor(as i only want to exclude all calls to 'actuator' endpoint from other interceptors).
#Configuration
public class ActuatorConfig extends WebMvcEndpointManagementContextConfiguration {
public WebMvcEndpointHandlerMapping webEndpointServletHandlerMapping(WebAnnotationEndpointDiscoverer endpointDiscoverer,
EndpointMediaTypes endpointMediaTypes,
CorsEndpointProperties corsProperties,
WebEndpointProperties webEndpointProperties) {
WebMvcEndpointHandlerMapping mapping = super.webEndpointServletHandlerMapping(
endpointDiscoverer,
endpointMediaTypes,
corsProperties,
webEndpointProperties);
mapping.setInterceptors(null);
return mapping;
}
}
Maybe you can override with setting null. I got code from https://github.com/spring-projects/spring-boot/issues/11234
AFAIK Spring HandlerInterceptor do not intercept actuator's endpoints by default.
Spring Boot can't intercept actuator access

Spring Boot 2 Actuator without Spring Boot and #EnableAutoConfiguration

I am trying to set up Spring Actuator with existing Gradle Spring MVC project. I am not able to use #EnableAutoConfiguration.
Unfortunately, I am not able to reach actuator endpoints, I think I am missing something.
The Spring dependencies in the project are:
// springVersion = 5.1.+
implementation(
"org.springframework:spring-beans:$springVersion",
"org.springframework:spring-webmvc:$springVersion",
"org.springframework:spring-jdbc:$springVersion")
implementation 'org.springframework.boot:spring-boot-starter-actuator'
I am trying to configure project with following:
#Configuration
#Import({EndpointAutoConfiguration.class,
MetricsEndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
MappingsEndpointAutoConfiguration.class,
InfoEndpointAutoConfiguration.class})
#EnableWebMvc
public class DI_App {
}
In properties file, I added:
management.endpoints.web.exposure.include=*
Non of actuator endpoints is enabled, I am getting 404 when trying to access them.
I went through many related questions, but non of the solutions worked for me.
I might need to define custom EndpointHandlerMapping but not sure how to do this, it seems unavailable.
(Ref: https://stackoverflow.com/a/53010693)
EDIT:
Currently, my app config looks like this:
#Configuration
#EnableWebMvc
#ComponentScan("com.test.springtest")
#Import({
ConfigurationPropertiesReportEndpointAutoConfiguration.class,
EndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
HealthIndicatorAutoConfiguration.class,
InfoEndpointAutoConfiguration.class,
InfoContributorAutoConfiguration.class,
LogFileWebEndpointAutoConfiguration.class,
LoggersEndpointAutoConfiguration.class,
WebMvcMetricsAutoConfiguration.class,
ManagementWebSecurityAutoConfiguration.class,
ManagementContextAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class
})
public class DI_App {
private final ApplicationContext _applicationContext;
DI_App(ApplicationContext applicationContext) {
_applicationContext = applicationContext;
System.setProperty("management.endpoints.web.exposure.include", "*");
System.setProperty("management.endpoints.jmx.exposure.exclude", "*");
System.setProperty("management.endpoints.web.base-path", "/manage");
System.setProperty("management.server.port", "10100");
}
#Bean
public WebMvcEndpointHandlerMapping endpointHandlerMapping(Collection<ExposableWebEndpoint> endpoints) {
List<String> mediaTypes = List.of(MediaType.APPLICATION_JSON_VALUE, ActuatorMediaType.V2_JSON);
EndpointMediaTypes endpointMediaTypes = new EndpointMediaTypes(mediaTypes, mediaTypes);
WebEndpointDiscoverer discoverer = new WebEndpointDiscoverer(_applicationContext,
new ConversionServiceParameterValueMapper(),
endpointMediaTypes,
List.of(EndpointId::toString),
emptyList(),
emptyList());
return new WebMvcEndpointHandlerMapping(new EndpointMapping("/manage"),
endpoints,
endpointMediaTypes,
new CorsConfiguration(),
new EndpointLinksResolver(discoverer.getEndpoints()));
}
}
I had to add dispatcherServlet bean, in order to be able to add ManagementContextAutoConfiguration.class to Imports:
#Component
public class AppDispatcherServlet implements DispatcherServletPath {
#Override
public String getPath() {
return "/";
}
}
Current state is that when going to /manage endpoint I get this:
{"_links":{"self":{"href":"http://localhost:10100/dev/manage","templated":false},"info":{"href":"http://localhost:10100/dev/manage/info","templated":false}}}
But http://localhost:10100/dev/manage/info returns 404 and no other endpoints are available.
I'm using Maven, not Gradle, but was in a similar situation. I had a working spring-boot-actuator 1.4.2.RELEASE Health actuator endpoint with Spring MVC 4.3.21. Upgraded to spring-boot-starter-actuator 2.6.1 and Spring MVC 5.3.13 and the following works for me to reach /myAppContext/health.
The DispatcherServletAutoConfiguration import may be able to replace your explicit DispatcherServlet bean. My case doesn't include the Info actuator endpoint but the key thing for me was the specific Imports below. Order is somewhat important for certain imports, at least in my testing.
I know very little about spring boot so this is the result of enabling auto configuration, pouring through spring boot TRACE log output, and trying lots of different import combinations.
#Configuration
#EnableWebMvc
#Import({
DispatcherServletAutoConfiguration.class,
WebMvcAutoConfiguration.class,
WebEndpointAutoConfiguration.class,
EndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
WebMvcEndpointManagementContextConfiguration.class
})
#PropertySource("classpath:/health.properties")
public class MyAppActuatorConfig {
// 1.x version had EndpointHandlerMapping and HealthMvcEndpoint beans here.
// There may be a more spring-boot-ish way to get this done : )
}
And a minimal health.properties that suited my deployment specifics where security was already in place:
management.endpoints.web.base-path=/
management.endpoint.health.show-details=when-authorized
management.endpoint.health.show-components=when-authorized

Instantiating server in Spring Framework 5 and Spring Boot 2

I am using Spring Boot 2 just to try some reactive programming with Spring 5. I created some standard MVC controller.
#RestController
#RequestMapping("/judge/rest")
public class BasicController {
private static final Logger LOGGER = LoggerFactory.getLogger(BasicController.class);
#GetMapping("/hello")
public Mono<String> handle() {
LOGGER.debug("Invoking hello controller");
return Mono.just("Hello WebFlux");
}
}
And standard router function.
#Configuration
public class WebConfig {
#Bean
public RouterFunction<?> helloRoute() {
return route(GET("/judge/router/hello"),
request -> ServerResponse.ok().body(fromPublisher(Mono.just("Hello Router WebFlux"), String.class)));
}
}
My main spring boot application looks like this
#SpringBootApplication
public class JudgeRuleEngineApplication {
public static void main(String[] args) {
SpringApplication.run(JudgeRuleEngineApplication.class, args);
}
}
But in documentation for spring 5 I ran into
HttpHandler httpHandler = RouterFunctions.toHttpHandler(route);
ReactorHttpHandlerAdapter adapter =
new ReactorHttpHandlerAdapter(httpHandler);
HttpServer server = HttpServer.create("localhost", 8080);
server.startAndAwait(adapter);
It seems that server is intantiated manually.
My question is when should I instantiate the server like this? Because so far it seems with #SpringBootApplication and main it handles requests just fine.
As the document says
Now there is just one piece of the puzzle missing: running a router
function in an HTTP server. You can convert a router function into a
HttpHandler by using RouterFunctions.toHttpHandler(RouterFunction).
The HttpHandler allows you to run on a wide variety of reactive
runtimes: Reactor Netty, RxNetty, Servlet 3.1+, and Undertow.
Which means the above code which you have shown, uses Reactor Netty as the reactive runtime. If you wish to use any other runtimes which has reactive native adapter, you can do so. In such cases you would instantiate the server like this.
By default Spring boot default to Reactor Netty.

Adding authorization to Annotation-driven swagger.json with Jersey 2 and Spring Boot

I'm trying to add Basic Authentication to Swagger UI for a to a Swagger-annotated Jersey 2.0 web service built with Spring Boot. I'm using:
Spring Boot 1.5.4
spring-boot-starter-jersey
Swagger UI 3.0.4
(Maven package) swagger-jersey2-jaxrs 1.5.13
I'm able to generate a swagger.json file with the following JerseyConfig and with Swagger annotations on my Resources. This article was immensely helpful in getting this far.
#Component
public class JerseyConfiguration extends ResourceConfig {
private static Log logger = LogFactory.getLog(JerseyConfiguration.class);
#Value("${spring.jersey.application-path:/}")
private String apiPath;
public JerseyConfiguration() {
registerEndpoints();
configureSwagger();
}
private void registerEndpoints() {
register(MyEndpoints.class);
// Generate Jersey WADL at /<Jersey's servlet path>/application.wadl
register(WadlResource.class);
// Lets us get to static content like swagger
property(ServletProperties.FILTER_STATIC_CONTENT_REGEX, "((/swagger/.*)|(.*\\.html))");
}
/**
* Configure the Swagger documentation for this API.
*/
private void configureSwagger() {
// Creates file at localhost:port/swagger.json
this.register(ApiListingResource.class);
this.register(SwaggerSerializers.class);
BeanConfig config = new BeanConfig();
config.setConfigId("example-jersey-app");
config.setTitle("Spring Boot + Jersey + Swagger");
config.setVersion("2");
config.setContact("Me <me#example.com>");
config.setSchemes(new String[] {"http", "https"});
config.setResourcePackage("com.example.api");
config.setBasePath(this.apiPath);
config.setPrettyPrint(true);
config.setScan(true);
}
}
Now I want to be able to use Basic Authentication to connect to these services from Swagger UI. I've configured it in Spring and can use it to authenticate to the site, but not from Swagger UI.
Unfortunately, none of the Spring Boot examples currently on the Swagger sample site include Jersey and authentication, and none of the Jersey examples use Spring Boot and #SpringBootApplication like I'm using on in my project.
How do I get Basic Auth to show up in the Swagger UI?
I was able to get this to work by adding ServletConfigAware to JerseyConfiguration. Then I could use the same style of Swagger configuration used in the Swagger Bootstrap.java examples.
#Component
public class JerseyConfiguration extends ResourceConfig implements ServletConfigAware{
private ServletConfig servletConfig;
// ... this is all unchanged ...
/**
* Configure the Swagger documentation for this API.
*/
private void configureSwagger() {
// Creates file at localhost:port/swagger.json
this.register(ApiListingResource.class);
this.register(SwaggerSerializers.class);
BeanConfig config = new BeanConfig();
// ... this is all unchanged ...
config.setScan(true);
Swagger swagger = new Swagger();
swagger.securityDefinition("basicAuth", new BasicAuthDefinition());
new SwaggerContextService().withServletConfig(servletConfig).updateSwagger(swagger);
}
#Override
public void setServletConfig(ServletConfig servletConfig) {
logger.info("Setting ServletConfig");
this.servletConfig = servletConfig;
}
}
After making these changes, and adding annotations like the following to my endpoints:
#Api(value = "/api", description = "My super API",
authorizations = {#Authorization(value="basicAuth")})
#Path("api")
#Component
public class MyApi {
I saw the following changes:
Added to my swagger.json:
"securityDefinitions":{"basicAuth":{"type":"basic"}}
...
"security":[{"basicAuth":[]}]}}
Also, in Swagger UI, a new green button appeared in the same row as the Schemes dropdown, that says "Authorize" with an open lock on it. If I click on it, a popup shows up where I can enter the username and password. Now those credentials are sent to the API when I use the Swagger UI "Try It" feature.

Spring Boot with Spring Social Google provider

Any example how to integrate Spring Boot application with Spring Social Google (GabiAxel/spring-social-google) provider? I found this project, but it seems to be unfinished. Spring Boot explains how to get it working with Spring Facebook, Twitter, but is it the same for log in with Google?
As you have mentioned in your question, you can use that project hosted on github.
You can use this dependency
In a Configuration class, you will have to extend SocialConfigurerAdapter, override the addConnectionFactories method and add GoogleConnectionFactory. For example :
#Configuration
#EnableSocial
public class SocialConfig extends SocialConfigurerAdapter {
#Override
public void addConnectionFactories(ConnectionFactoryConfigurer connectionFactoryConfigurer, Environment environment) {
GoogleConnectionFactory googleConnectionFactory = new GoogleConnectionFactory(environment.getProperty("spring.social.google.app-id"), environment.getProperty("spring.social.google.app-secret"));
googleConnectionFactory.setScope("https://www.googleapis.com/auth/plus.login");
connectionFactoryConfigurer.addConnectionFactory(googleConnectionFactory);
}
#Bean
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
public Google google(ConnectionRepository repository) {
Connection<Google> connection = repository.findPrimaryConnection(Google.class);
return connection != null ? connection.getApi() : null;
}
}
You can use this along with the Spring Social examples.

Resources