Polish GraphQL changes
See gh-29140 Closes gh-29194
This commit is contained in:
@@ -35,6 +35,12 @@ import io.micrometer.core.instrument.Timer;
|
||||
import org.springframework.boot.actuate.metrics.AutoTimer;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Micrometer-based {@link SimpleInstrumentation}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
|
||||
|
||||
private final MeterRegistry registry;
|
||||
@@ -127,7 +133,7 @@ public class GraphQlMetricsInstrumentation extends SimpleInstrumentation {
|
||||
|
||||
private Timer.Sample sample;
|
||||
|
||||
private AtomicLong dataFetchingCount = new AtomicLong(0L);
|
||||
private final AtomicLong dataFetchingCount = new AtomicLong();
|
||||
|
||||
RequestMetricsInstrumentationState(AutoTimer autoTimer, MeterRegistry registry) {
|
||||
this.timer = autoTimer.builder("graphql.request");
|
||||
|
||||
@@ -34,7 +34,7 @@ import org.springframework.util.CollectionUtils;
|
||||
* Factory methods for Tags associated with a GraphQL request.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 1.0.0
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public final class GraphQlTags {
|
||||
|
||||
@@ -72,7 +72,7 @@ public final class GraphQlTags {
|
||||
builder.append('$');
|
||||
for (Object segment : pathSegments) {
|
||||
try {
|
||||
int index = Integer.parseUnsignedInt(segment.toString());
|
||||
Integer.parseUnsignedInt(segment.toString());
|
||||
builder.append("[*]");
|
||||
}
|
||||
catch (NumberFormatException exc) {
|
||||
@@ -90,13 +90,13 @@ public final class GraphQlTags {
|
||||
|
||||
public static Tag dataFetchingPath(InstrumentationFieldFetchParameters parameters) {
|
||||
ExecutionStepInfo executionStepInfo = parameters.getExecutionStepInfo();
|
||||
StringBuilder dataFetchingType = new StringBuilder();
|
||||
StringBuilder dataFetchingPath = new StringBuilder();
|
||||
if (executionStepInfo.hasParent() && executionStepInfo.getParent().getType() instanceof GraphQLObjectType) {
|
||||
dataFetchingType.append(((GraphQLObjectType) executionStepInfo.getParent().getType()).getName());
|
||||
dataFetchingType.append('.');
|
||||
dataFetchingPath.append(((GraphQLObjectType) executionStepInfo.getParent().getType()).getName());
|
||||
dataFetchingPath.append('.');
|
||||
}
|
||||
dataFetchingType.append(executionStepInfo.getPath().getSegmentName());
|
||||
return Tag.of("path", dataFetchingType.toString());
|
||||
dataFetchingPath.append(executionStepInfo.getPath().getSegmentName());
|
||||
return Tag.of("path", dataFetchingPath.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ class GraphQlMetricsInstrumentationTests {
|
||||
|
||||
Timer timer = this.registry.find("graphql.request").timer();
|
||||
assertThat(timer).isNotNull();
|
||||
assertThat(timer.takeSnapshot().count()).isEqualTo(1);
|
||||
assertThat(timer.count()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -120,7 +120,7 @@ class GraphQlMetricsInstrumentationTests {
|
||||
|
||||
Timer timer = this.registry.find("graphql.datafetcher").timer();
|
||||
assertThat(timer).isNotNull();
|
||||
assertThat(timer.takeSnapshot().count()).isEqualTo(1);
|
||||
assertThat(timer.count()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -135,7 +135,7 @@ class GraphQlMetricsInstrumentationTests {
|
||||
|
||||
Timer timer = this.registry.find("graphql.datafetcher").timer();
|
||||
assertThat(timer).isNotNull();
|
||||
assertThat(timer.takeSnapshot().count()).isEqualTo(1);
|
||||
assertThat(timer.count()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -150,7 +150,7 @@ class GraphQlMetricsInstrumentationTests {
|
||||
|
||||
Timer timer = this.registry.find("graphql.datafetcher").timer();
|
||||
assertThat(timer).isNotNull();
|
||||
assertThat(timer.takeSnapshot().count()).isEqualTo(1);
|
||||
assertThat(timer.count()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -168,7 +168,7 @@ class GraphQlWebMvcSecurityAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
static InMemoryUserDetailsManager userDetailsService() {
|
||||
InMemoryUserDetailsManager userDetailsService() {
|
||||
User.UserBuilder userBuilder = User.withDefaultPasswordEncoder();
|
||||
UserDetails rob = userBuilder.username("rob").password("rob").roles("USER").build();
|
||||
UserDetails admin = userBuilder.username("admin").password("admin").roles("USER", "ADMIN").build();
|
||||
|
||||
@@ -905,7 +905,7 @@ A single GraphQL query can involve many `DataFetcher` calls, so there is a dedic
|
||||
|
||||
|
||||
The `graphql.request.datafetch.count` https://micrometer.io/docs/concepts#_distribution_summaries[distribution summary] counts the number of non-trivial `DataFetcher` calls made per request.
|
||||
This metric is useful for detecting "N+1" data fetching issues and consider batch loading; it provides the `"TOTAL"` number of data fetcher calls made over the `"COUNT"` of recorded requests, as well as the `"MAX"` calls made for a single request over the considered period.
|
||||
This metric is useful for detecting "N+1" data fetching issues and considering batch loading; it provides the `"TOTAL"` number of data fetcher calls made over the `"COUNT"` of recorded requests, as well as the `"MAX"` calls made for a single request over the considered period.
|
||||
More options are available for <<application-properties#application-properties.actuator.management.metrics.distribution.maximum-expected-value, configuring distributions with application properties>>.
|
||||
|
||||
A single response can contain many GraphQL errors, counted by the `graphql.error` counter:
|
||||
|
||||
@@ -49,7 +49,7 @@ You can declare `RuntimeWiringConfigurer` beans in your Spring config to get acc
|
||||
Spring Boot detects such beans and adds them to the {spring-graphql-docs}#execution-graphqlsource[GraphQlSource builder].
|
||||
|
||||
Typically, however, applications will not implement `DataFetcher` directly and will instead create {spring-graphql-docs}#controllers[annotated controllers].
|
||||
Spring Boot will automatically register `@Controller` classes with annotated handler methods and registers those as `DataFetcher`s.
|
||||
Spring Boot will automatically detect `@Controller` classes with annotated handler methods and register those as `DataFetcher`s.
|
||||
Here's a sample implementation for our greeting query with a `@Controller` class:
|
||||
|
||||
[source,java,indent=0,subs="verbatim"]
|
||||
|
||||
@@ -25,11 +25,11 @@ import org.springframework.graphql.data.method.annotation.QueryMapping;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
@Controller
|
||||
public class ProjectsController {
|
||||
public class ProjectController {
|
||||
|
||||
private final List<Project> projects;
|
||||
|
||||
public ProjectsController() {
|
||||
public ProjectController() {
|
||||
this.projects = Arrays.asList(new Project("spring-boot", "Spring Boot"),
|
||||
new Project("spring-graphql", "Spring GraphQL"), new Project("spring-framework", "Spring Framework"));
|
||||
}
|
||||
@@ -28,13 +28,13 @@ import org.springframework.security.web.DefaultSecurityFilterChain;
|
||||
|
||||
import static org.springframework.security.config.Customizer.withDefaults;
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true)
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
DefaultSecurityFilterChain springWebFilterChain(HttpSecurity http) throws Exception {
|
||||
public DefaultSecurityFilterChain springWebFilterChain(HttpSecurity http) throws Exception {
|
||||
return http.csrf((csrf) -> csrf.disable())
|
||||
// Demonstrate that method security works
|
||||
// Best practice to use both for defense in depth
|
||||
@@ -43,7 +43,7 @@ public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("deprecation")
|
||||
public static InMemoryUserDetailsManager userDetailsService() {
|
||||
public InMemoryUserDetailsManager userDetailsService() {
|
||||
User.UserBuilder userBuilder = User.withDefaultPasswordEncoder();
|
||||
UserDetails rob = userBuilder.username("rob").password("rob").roles("USER").build();
|
||||
UserDetails admin = userBuilder.username("admin").password("admin").roles("USER", "ADMIN").build();
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest;
|
||||
import org.springframework.graphql.test.tester.GraphQlTester;
|
||||
|
||||
@GraphQlTest(ProjectsController.class)
|
||||
@GraphQlTest(ProjectController.class)
|
||||
class ProjectControllerTests {
|
||||
|
||||
@Autowired
|
||||
|
||||
Reference in New Issue
Block a user