Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Spring's new RestClient with auto configuration (#3198) #3199

6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Features

- Add support for Spring Rest Client ([#3199](https://github.com/getsentry/sentry-java/pull/3199))

## 7.6.0

### Features
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;

Expand All @@ -34,6 +35,11 @@ WebClient webClient(WebClient.Builder builder) {
return builder.build();
}

@Bean
RestClient restClient(RestClient.Builder builder) {
return builder.build();
}

@Bean
public JobDetailFactoryBean jobDetail() {
JobDetailFactoryBean jobDetailFactory = new JobDetailFactoryBean();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Hooks;
Expand All @@ -14,10 +15,12 @@
public class TodoController {
private final RestTemplate restTemplate;
private final WebClient webClient;
private final RestClient restClient;

public TodoController(RestTemplate restTemplate, WebClient webClient) {
public TodoController(RestTemplate restTemplate, WebClient webClient, RestClient restClient) {
this.restTemplate = restTemplate;
this.webClient = webClient;
this.restClient = restClient;
}

@GetMapping("/todo/{id}")
Expand All @@ -42,4 +45,13 @@ Todo todoWebClient(@PathVariable Long id) {
.map(response -> response)))
.block();
}

@GetMapping("/todo-restclient/{id}")
Todo todoRestClient(@PathVariable Long id) {
return restClient
.get()
.uri("https://jsonplaceholder.typicode.com/todos/{id}", id)
.retrieve()
.body(Todo.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
Expand All @@ -65,6 +66,7 @@
import org.springframework.graphql.execution.DataFetcherExceptionResolverAdapter;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.servlet.HandlerExceptionResolver;
Expand Down Expand Up @@ -357,6 +359,17 @@ public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IHub hu
}
}

@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(RestClientAutoConfiguration.class)
@ConditionalOnClass(RestClient.class)
@Open
static class SentrySpanRestClientConfiguration {
@Bean
public SentrySpanRestClientCustomizer sentrySpanRestClientCustomizer(IHub hub) {
return new SentrySpanRestClientCustomizer(hub);
}
}

@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(WebClientAutoConfiguration.class)
@ConditionalOnClass(WebClient.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.sentry.spring.boot.jakarta;

import com.jakewharton.nopen.annotation.Open;
import io.sentry.IHub;
import io.sentry.spring.jakarta.tracing.SentrySpanClientHttpRequestInterceptor;
import org.jetbrains.annotations.NotNull;
import org.springframework.boot.web.client.RestClientCustomizer;
import org.springframework.web.client.RestClient;

@Open
class SentrySpanRestClientCustomizer implements RestClientCustomizer {
private final @NotNull SentrySpanClientHttpRequestInterceptor interceptor;

public SentrySpanRestClientCustomizer(final @NotNull IHub hub) {
this.interceptor = new SentrySpanClientHttpRequestInterceptor(hub, false);
}

@Override
public void customize(final @NotNull RestClient.Builder restClientBuilder) {
restClientBuilder.requestInterceptors(
clientHttpRequestInterceptors -> {
// As the SentrySpanClientHttpRequestInterceptor is being created in this class, this
// might not work
// if somebody registers it from an outside.
if (!clientHttpRequestInterceptors.contains(interceptor)) {
clientHttpRequestInterceptors.add(interceptor);
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import org.springframework.core.Ordered
import org.springframework.core.annotation.Order
import org.springframework.scheduling.quartz.SchedulerFactoryBean
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.web.client.RestClient
import org.springframework.web.client.RestTemplate
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.servlet.HandlerExceptionResolver
Expand Down Expand Up @@ -649,6 +650,23 @@ class SentryAutoConfigurationTest {
}
}

@Test
fun `when tracing is enabled and RestClient is on the classpath, SentrySpanRestClientCustomizer bean is created`() {
contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj", "sentry.traces-sample-rate=1.0")
.run {
assertThat(it).hasSingleBean(SentrySpanRestClientCustomizer::class.java)
}
}

@Test
fun `when tracing is enabled and RestClient is not on the classpath, SentrySpanRestClientCustomizer bean is not created`() {
contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj", "sentry.traces-sample-rate=1.0")
.withClassLoader(FilteredClassLoader(RestClient::class.java))
.run {
assertThat(it).doesNotHaveBean(SentrySpanRestClientCustomizer::class.java)
}
}

@Test
fun `when tracing is enabled and WebClient is on the classpath, SentrySpanWebClientCustomizer bean is created`() {
contextRunner.withPropertyValues("sentry.dsn=http://key@localhost/proj", "sentry.traces-sample-rate=1.0")
Expand Down
Loading
Loading