Skip to content

Commit 2f1c4b1

Browse files
committed
FederationSchemaFactory checks for unmapped entities
Closes gh-1088
1 parent 310424e commit 2f1c4b1

File tree

2 files changed

+34
-2
lines changed

2 files changed

+34
-2
lines changed

spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@
2525

2626
import com.apollographql.federation.graphqljava.Federation;
2727
import com.apollographql.federation.graphqljava.SchemaTransformer;
28+
import graphql.language.TypeDefinition;
2829
import graphql.schema.DataFetcher;
2930
import graphql.schema.GraphQLSchema;
3031
import graphql.schema.TypeResolver;
@@ -177,12 +178,23 @@ public GraphQLSchema createGraphQLSchema(TypeDefinitionRegistry registry, Runtim
177178
* @param wiring the existing runtime wiring
178179
*/
179180
public SchemaTransformer createSchemaTransformer(TypeDefinitionRegistry registry, RuntimeWiring wiring) {
181+
checkEntityMappings(registry);
180182
Assert.state(this.typeResolver != null, "afterPropertiesSet not called");
181183
return Federation.transform(registry, wiring)
182184
.fetchEntities(new EntitiesDataFetcher(this.handlerMethods, getExceptionResolver()))
183185
.resolveEntityType(this.typeResolver);
184186
}
185187

188+
private void checkEntityMappings(TypeDefinitionRegistry registry) {
189+
for (TypeDefinition<?> type : registry.types().values()) {
190+
type.getDirectives().forEach((directive) -> {
191+
boolean isEntityType = directive.getName().equalsIgnoreCase("key");
192+
Assert.state(!isEntityType || this.handlerMethods.containsKey(type.getName()),
193+
"No EntityMapping method for federated type: '" + type.getName() + "'");
194+
});
195+
}
196+
}
197+
186198

187199
public record EntityMappingInfo(String typeName, HandlerMethod handlerMethod) {
188200

spring-graphql/src/test/java/org/springframework/graphql/data/federation/EntityMappingInvocationTests.java

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,6 +52,7 @@
5252
import org.springframework.stereotype.Controller;
5353

5454
import static org.assertj.core.api.Assertions.assertThat;
55+
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
5556

5657
/**
5758
* Tests for requests handled through {@code @EntityMapping} methods.
@@ -175,6 +176,18 @@ void batchingWithoutResult(Class<?> controllerClass) {
175176
assertError(helper, 2, "INTERNAL_ERROR", "Entity fetcher returned null or completed empty");
176177
}
177178

179+
@Test
180+
void unmappedEntity() {
181+
Map<String, Object> variables =
182+
Map.of("representations", List.of(
183+
Map.of("__typename", "Book", "id", "-99"),
184+
Map.of("__typename", "Book", "id", "4"),
185+
Map.of("__typename", "Book", "id", "5")));
186+
187+
assertThatIllegalStateException().isThrownBy(() -> executeWith(EmptyController.class, variables))
188+
.withMessage("No EntityMapping method for federated type: 'Book'");
189+
}
190+
178191
private static ResponseHelper executeWith(Class<?> controllerClass, Map<String, Object> variables) {
179192
ExecutionGraphQlRequest request = TestExecutionRequest.forDocumentAndVars(document, variables);
180193
Mono<ExecutionGraphQlResponse> responseMono = graphQlService(controllerClass).execute(request);
@@ -299,6 +312,13 @@ public GraphQLError handle(IllegalArgumentException ex, DataFetchingEnvironment
299312
}
300313

301314

315+
@SuppressWarnings("unused")
316+
@Controller
317+
private static class EmptyController {
318+
319+
}
320+
321+
302322
private static class BookBatchService {
303323

304324
public List<Book> book(List<Integer> idList, List<Map<String, Object>> representations) {

0 commit comments

Comments
 (0)