Upgrade to GraphQL Java 24.0

This commit sets the new GraphQL Java baseline to 24.0 for this Spring
for GraphQL generation.

This also uses the new `DataLoader#getName` property in the batch loader
registry and the observability instrumentation.

Closes gh-1210
Closes gh-1211
This commit is contained in:
Brian Clozel
2025-05-16 09:27:24 +02:00
parent 998ba45fb5
commit 5747def15b
9 changed files with 53 additions and 40 deletions

View File

@@ -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"
}

View File

@@ -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".
|===

View File

@@ -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<DataLoaderOptions> 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<DataLoaderOptions> initOptionsSupplier() {
Supplier<DataLoaderOptions> 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();
};
}
}

View File

@@ -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}.
*/

View File

@@ -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) {

View File

@@ -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";
}
},

View File

@@ -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);

View File

@@ -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);

View File

@@ -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");
}