diff --git a/build.gradle b/build.gradle index 408214d5..884869eb 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ description = "Spring for GraphQL" ext { moduleProjects = [project(":spring-graphql"), project(":spring-graphql-test")] springFrameworkVersion = "6.2.6" - graphQlJavaVersion = "23.1" + graphQlJavaVersion = "24.0" springBootVersion = "3.4.3" } diff --git a/spring-graphql-docs/modules/ROOT/pages/observability.adoc b/spring-graphql-docs/modules/ROOT/pages/observability.adoc index 60070ec7..928e1733 100644 --- a/spring-graphql-docs/modules/ROOT/pages/observability.adoc +++ b/spring-graphql-docs/modules/ROOT/pages/observability.adoc @@ -96,7 +96,7 @@ By default, the following KeyValues are created: |=== |Name | Description |`graphql.error.type` _(required)_|Class name of the data fetching error -|`graphql.loader.type` _(required)_|Class name of the elements being fetched. +|`graphql.loader.name` _(required)_|Name of the DataLoader being used. |`graphql.outcome` _(required)_|Outcome of the GraphQL data fetching operation, "SUCCESS" or "ERROR". |=== diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java index 468fdc30..d779e6cf 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,12 +60,11 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { private final Supplier defaultOptionsSupplier; - /** * Default constructor. */ public DefaultBatchLoaderRegistry() { - this(DataLoaderOptions::newOptions); + this(DataLoaderOptions::newDefaultOptions); } /** @@ -99,24 +98,24 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { public void registerDataLoaders(DataLoaderRegistry registry, GraphQLContext context) { BatchLoaderContextProvider contextProvider = () -> context; for (ReactorBatchLoader loader : this.loaders) { - DataLoaderOptions options = loader.getOptions(); - options = options.setBatchLoaderContextProvider(contextProvider); - DataLoader dataLoader = DataLoaderFactory.newDataLoader(loader, options); - registerDataLoader(loader.getName(), dataLoader, registry); + DataLoaderOptions options = loader.getOptions() + .transform((opt) -> opt.setBatchLoaderContextProvider(contextProvider)); + DataLoader dataLoader = DataLoaderFactory.newDataLoader(loader.getName(), loader, options); + registerDataLoader(dataLoader, registry); } for (ReactorMappedBatchLoader loader : this.mappedLoaders) { - DataLoaderOptions options = loader.getOptions(); - options = options.setBatchLoaderContextProvider(contextProvider); - DataLoader dataLoader = DataLoaderFactory.newMappedDataLoader(loader, options); - registerDataLoader(loader.getName(), dataLoader, registry); + DataLoaderOptions options = loader.getOptions() + .transform((opt) -> opt.setBatchLoaderContextProvider(contextProvider)); + DataLoader dataLoader = DataLoaderFactory.newMappedDataLoader(loader.getName(), loader, options); + registerDataLoader(dataLoader, registry); } } - private void registerDataLoader(String name, DataLoader dataLoader, DataLoaderRegistry registry) { - if (registry.getDataLoader(name) != null) { - throw new IllegalStateException("More than one DataLoader named '" + name + "'"); + private void registerDataLoader(DataLoader dataLoader, DataLoaderRegistry registry) { + if (registry.getDataLoader(dataLoader.getName()) != null) { + throw new IllegalStateException("More than one DataLoader named '" + dataLoader.getName() + "'"); } - registry.register(name, dataLoader); + registry.register(dataLoader.getName(), dataLoader); } @@ -183,20 +182,21 @@ public class DefaultBatchLoaderRegistry implements BatchLoaderRegistry { } private Supplier initOptionsSupplier() { - - Supplier optionsSupplier = () -> - new DataLoaderOptions((this.options != null) ? - this.options : DefaultBatchLoaderRegistry.this.defaultOptionsSupplier.get()); - - if (this.optionsBuilderConsumer == null) { - return optionsSupplier; - } - return () -> { - DataLoaderOptions options = optionsSupplier.get(); - return options.transform(this.optionsBuilderConsumer); + DataLoaderOptions.Builder builder; + if (this.options != null) { + builder = DataLoaderOptions.newOptions(this.options); + } + else { + builder = DataLoaderOptions.newOptions(DefaultBatchLoaderRegistry.this.defaultOptionsSupplier.get()); + } + if (this.optionsBuilderConsumer != null) { + this.optionsBuilderConsumer.accept(builder); + } + return builder.build(); }; } + } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/observation/DataLoaderObservationContext.java b/spring-graphql/src/main/java/org/springframework/graphql/observation/DataLoaderObservationContext.java index 024e0978..26a81752 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/observation/DataLoaderObservationContext.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/observation/DataLoaderObservationContext.java @@ -20,6 +20,7 @@ import java.util.List; import io.micrometer.observation.Observation; import org.dataloader.BatchLoaderEnvironment; +import org.dataloader.DataLoader; /** * Context that holds information for metadata collection during observations @@ -30,17 +31,27 @@ import org.dataloader.BatchLoaderEnvironment; */ public class DataLoaderObservationContext extends Observation.Context { + private final DataLoader dataLoader; + private final List keys; private final BatchLoaderEnvironment environment; private List result = List.of(); - DataLoaderObservationContext(List keys, BatchLoaderEnvironment environment) { + DataLoaderObservationContext(DataLoader dataLoader, List keys, BatchLoaderEnvironment environment) { + this.dataLoader = dataLoader; this.keys = keys; this.environment = environment; } + /** + * Return the {@link DataLoader} being used for the operation. + */ + public DataLoader getDataLoader() { + return this.dataLoader; + } + /** * Return the keys for loading by the {@link org.dataloader.DataLoader}. */ diff --git a/spring-graphql/src/main/java/org/springframework/graphql/observation/DefaultDataLoaderObservationConvention.java b/spring-graphql/src/main/java/org/springframework/graphql/observation/DefaultDataLoaderObservationConvention.java index 8e184a8d..fa5a042a 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/observation/DefaultDataLoaderObservationConvention.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/observation/DefaultDataLoaderObservationConvention.java @@ -24,6 +24,7 @@ import io.micrometer.common.KeyValues; import org.springframework.graphql.observation.GraphQlObservationDocumentation.DataLoaderHighCardinalityKeyNames; import org.springframework.graphql.observation.GraphQlObservationDocumentation.DataLoaderLowCardinalityKeyNames; +import org.springframework.util.StringUtils; /** * Default implementation for a {@link DataLoaderObservationConvention} @@ -38,7 +39,7 @@ public class DefaultDataLoaderObservationConvention implements DataLoaderObserva private static final KeyValue ERROR_TYPE_NONE = KeyValue.of(DataLoaderLowCardinalityKeyNames.ERROR_TYPE, "NONE"); - private static final KeyValue LOADER_TYPE_UNKNOWN = KeyValue.of(DataLoaderLowCardinalityKeyNames.LOADER_TYPE, "unknown"); + private static final KeyValue LOADER_TYPE_UNKNOWN = KeyValue.of(DataLoaderLowCardinalityKeyNames.LOADER_NAME, "unknown"); private static final KeyValue OUTCOME_SUCCESS = KeyValue.of(DataLoaderLowCardinalityKeyNames.OUTCOME, "SUCCESS"); @@ -78,10 +79,10 @@ public class DefaultDataLoaderObservationConvention implements DataLoaderObserva } protected KeyValue loaderType(DataLoaderObservationContext context) { - if (context.getResult().isEmpty()) { - return LOADER_TYPE_UNKNOWN; + if (StringUtils.hasText(context.getDataLoader().getName())) { + return KeyValue.of(DataLoaderLowCardinalityKeyNames.LOADER_NAME, context.getDataLoader().getName()); } - return KeyValue.of(DataLoaderLowCardinalityKeyNames.LOADER_TYPE, context.getResult().get(0).getClass().getSimpleName()); + return LOADER_TYPE_UNKNOWN; } protected KeyValue outcome(DataLoaderObservationContext context) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationDocumentation.java b/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationDocumentation.java index ee81a08b..a86ba0b4 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationDocumentation.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationDocumentation.java @@ -21,6 +21,7 @@ import io.micrometer.common.docs.KeyName; import io.micrometer.observation.Observation; import io.micrometer.observation.ObservationConvention; import io.micrometer.observation.docs.ObservationDocumentation; +import org.dataloader.DataLoader; /** * Documented {@link io.micrometer.common.KeyValue KeyValues} for {@link graphql.GraphQL GraphQL server observations}. @@ -205,12 +206,12 @@ public enum GraphQlObservationDocumentation implements ObservationDocumentation }, /** - * {@link Class#getSimpleName()} of the returned elements. + * {@link DataLoader#getName()} of the data loader. */ - LOADER_TYPE { + LOADER_NAME { @Override public String asString() { - return "graphql.loader.type"; + return "graphql.loader.name"; } }, diff --git a/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationInstrumentation.java b/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationInstrumentation.java index 982de281..d5be3435 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationInstrumentation.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/observation/GraphQlObservationInstrumentation.java @@ -278,7 +278,7 @@ public class GraphQlObservationInstrumentation extends SimplePerformantInstrumen Observation observation = GraphQlObservationDocumentation.DATA_LOADER .observation(GraphQlObservationInstrumentation.this.dataLoaderObservationConvention, DEFAULT_DATA_LOADER_CONVENTION, - () -> new DataLoaderObservationContext(keys, environment), + () -> new DataLoaderObservationContext(dataLoader, keys, environment), GraphQlObservationInstrumentation.this.observationRegistry); if (environment.getContext() instanceof GraphQLContext graphQLContext) { Observation parentObservation = graphQLContext.get(ObservationThreadLocalAccessor.KEY); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistryTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistryTests.java index fd0bf301..ec0ce549 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistryTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/DefaultBatchLoaderRegistryTests.java @@ -46,7 +46,7 @@ class DefaultBatchLoaderRegistryTests { private final BatchLoaderRegistry batchLoaderRegistry = new DefaultBatchLoaderRegistry(() -> { // Disable batching, so we can test loading immediately - return DataLoaderOptions.newOptions().setBatchingEnabled(false); + return DataLoaderOptions.newOptions().setBatchingEnabled(false).build(); }); private final DataLoaderRegistry dataLoaderRegistry = DataLoaderRegistry.newRegistry().build(); @@ -105,7 +105,7 @@ class DefaultBatchLoaderRegistryTests { @Test void dataLoaderOptions() throws Exception { - DataLoaderOptions defaultOptions = DataLoaderOptions.newOptions().setBatchingEnabled(false); + DataLoaderOptions defaultOptions = DataLoaderOptions.newOptions().setBatchingEnabled(false).build(); DefaultBatchLoaderRegistry batchLoaderRegistry = new DefaultBatchLoaderRegistry(() -> defaultOptions); AtomicInteger counter = new AtomicInteger(1); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/observation/GraphQlObservationInstrumentationTests.java b/spring-graphql/src/test/java/org/springframework/graphql/observation/GraphQlObservationInstrumentationTests.java index 321e059d..7c011f3a 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/observation/GraphQlObservationInstrumentationTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/observation/GraphQlObservationInstrumentationTests.java @@ -411,7 +411,7 @@ class GraphQlObservationInstrumentationTests { .hasObservationWithNameEqualTo("graphql.dataloader") .that() .hasLowCardinalityKeyValue("graphql.outcome", "SUCCESS") - .hasLowCardinalityKeyValue("graphql.loader.type", "Author") + .hasLowCardinalityKeyValue("graphql.loader.name", "org.springframework.graphql.Author") .hasHighCardinalityKeyValue("graphql.loader.size", "4") .hasContextualNameEqualTo("graphql dataloader author"); }