Add hook for the inspection of schema mappings

Closes gh-672
This commit is contained in:
rstoyanchev
2023-04-28 12:52:29 +01:00
parent 7db96fd77f
commit d3ea23e878
3 changed files with 44 additions and 11 deletions

View File

@@ -417,18 +417,30 @@ implement this interface. That includes those for <<controllers>>, and those for
controllers, the return type is derived from the declared return type on a
`@SchemaMapping` method.
On startup, Spring for GraphQL inspects all schema fields, `DataFetcher` registrations,
On startup, Spring for GraphQL can inspect schema fields, `DataFetcher` registrations,
and the properties of Java objects returned from `DataFetcher` implementations to check
if all schema fields are covered either by an explicitly registered `DataFetcher`, or
a matching Java object property. The inspection also performs a reverse check looking for
`DataFetcher` registrations against schema fields that don't exist. This inspection is
performed automatically, and results in a report that is logged at INFO level on startup.
For example:
`DataFetcher` registrations against schema fields that don't exist.
To enable inspection of schema mappings:
[source,java,indent=0,subs="verbatim,quotes"]
----
GraphQlSource.Builder builder = ...
builder.schemaResources(..)
.inspectSchemaMappings(report -> {
logger.debug(report);
})
----
Below is an example report:
----
GraphQL schema inspection:
Unmapped fields: {Book=[title], Author[firstName, lastName]} // <1>
Unmapped DataFetcher registrations: {Book.reviews=BookController#reviews[1 args]} <2>
Unmapped registrations: {Book.reviews=BookController#reviews[1 args]} <2>
Skipped types: [BookOrAuthor] // <3>
----

View File

@@ -24,6 +24,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import graphql.language.InterfaceTypeDefinition;
@@ -71,6 +72,9 @@ final class DefaultSchemaResourceGraphQlSourceBuilder
@Nullable
private BiFunction<TypeDefinitionRegistry, RuntimeWiring, GraphQLSchema> schemaFactory;
@Nullable
private Consumer<SchemaReport> schemaReportConsumer;
@Override
public DefaultSchemaResourceGraphQlSourceBuilder schemaResources(Resource... resources) {
@@ -96,6 +100,12 @@ final class DefaultSchemaResourceGraphQlSourceBuilder
return this;
}
@Override
public GraphQlSource.SchemaResourceBuilder inspectSchemaMappings(Consumer<SchemaReport> consumer) {
this.schemaReportConsumer = consumer;
return this;
}
@Override
public DefaultSchemaResourceGraphQlSourceBuilder schemaFactory(
BiFunction<TypeDefinitionRegistry, RuntimeWiring, GraphQLSchema> schemaFactory) {
@@ -136,11 +146,13 @@ final class DefaultSchemaResourceGraphQlSourceBuilder
// SchemaMappingInspector needs RuntimeWiring, but cannot run here since type
// visitors may transform the schema, for example to add Connection types.
configureGraphQl(builder -> {
GraphQLSchema schema = builder.build().getGraphQLSchema();
SchemaReport report = SchemaMappingInspector.inspect(schema, runtimeWiring);
logger.info(report);
});
if (this.schemaReportConsumer != null) {
configureGraphQl(builder -> {
GraphQLSchema schema = builder.build().getGraphQLSchema();
SchemaReport report = SchemaMappingInspector.inspect(schema, runtimeWiring);
this.schemaReportConsumer.accept(report);
});
}
return (this.schemaFactory != null ?
this.schemaFactory.apply(registry, runtimeWiring) :

View File

@@ -19,7 +19,6 @@ package org.springframework.graphql.execution;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import graphql.GraphQL;
import graphql.execution.instrumentation.Instrumentation;
@@ -201,6 +200,16 @@ public interface GraphQlSource {
*/
SchemaResourceBuilder defaultTypeResolver(TypeResolver typeResolver);
/**
* Enable inspection of schema mappings to find unmapped fields and
* unmapped {@code DataFetcher} registrations. For more details, see
* {@link SchemaReport} and the reference documentation.
* @param reportConsumer a hook to inspect the report
* @return the current builder
* @since 1.2.0
*/
SchemaResourceBuilder inspectSchemaMappings(Consumer<SchemaReport> reportConsumer);
/**
* Configure a function to create the {@link GraphQLSchema} from the
* given {@link TypeDefinitionRegistry} and {@link RuntimeWiring}.