diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java index ec9efbc9..0c56de76 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java @@ -26,6 +26,9 @@ import java.util.stream.Collectors; import com.apollographql.federation.graphqljava.Federation; import com.apollographql.federation.graphqljava.SchemaTransformer; +import graphql.language.Argument; +import graphql.language.BooleanValue; +import graphql.language.Directive; import graphql.language.TypeDefinition; import graphql.schema.DataFetcher; import graphql.schema.GraphQLSchema; @@ -189,8 +192,7 @@ public final class FederationSchemaFactory List unmappedEntities = new ArrayList<>(); for (TypeDefinition type : registry.types().values()) { type.getDirectives().forEach((directive) -> { - boolean isEntityType = directive.getName().equalsIgnoreCase("key"); - if (isEntityType && !this.handlerMethods.containsKey(type.getName())) { + if (isResolvableKeyDirective(directive) && !this.handlerMethods.containsKey(type.getName())) { unmappedEntities.add(type.getName()); } }); @@ -201,6 +203,18 @@ public final class FederationSchemaFactory } } + private boolean isResolvableKeyDirective(Directive directive) { + if (!directive.getName().equalsIgnoreCase("key")) { + return false; + } + Argument argument = directive.getArgument("resolvable"); + if (argument != null) { + Object value = argument.getValue(); + return (value instanceof BooleanValue bv && bv.isValue()); + } + return true; + } + public record EntityMappingInfo(String typeName, HandlerMethod handlerMethod) { diff --git a/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java b/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java index 6bf59945..1fdc6194 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/server/WebGraphQlRequest.java @@ -24,7 +24,6 @@ import java.util.Map; import org.jspecify.annotations.Nullable; -import org.springframework.graphql.ExecutionGraphQlRequest; import org.springframework.graphql.GraphQlRequest; import org.springframework.graphql.support.DefaultExecutionGraphQlRequest; import org.springframework.http.HttpCookie; @@ -48,7 +47,7 @@ import org.springframework.web.util.UriComponentsBuilder; * @author Rossen Stoyanchev * @since 1.0.0 */ -public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest implements ExecutionGraphQlRequest { +public class WebGraphQlRequest extends DefaultExecutionGraphQlRequest { private static final MultiValueMap EMPTY_COOKIES = CollectionUtils.unmodifiableMultiValueMap(new LinkedMultiValueMap<>()); diff --git a/spring-graphql/src/test/resources/books/federation-schema.graphqls b/spring-graphql/src/test/resources/books/federation-schema.graphqls index 251dca33..0f891859 100644 --- a/spring-graphql/src/test/resources/books/federation-schema.graphqls +++ b/spring-graphql/src/test/resources/books/federation-schema.graphqls @@ -1,6 +1,9 @@ +extend schema @link(url: "https://specs.apollo.dev/federation/v2.9", import: ["@key", "@extends", "@external"] ) + type Book @key(fields: "id") @extends { id: ID! @external author: Author + publisher: Publisher } type Author { @@ -8,3 +11,7 @@ type Author { firstName: String lastName: String } + +type Publisher @key(fields: "id", resolvable: false) { + id: ID! @external +}