Polishing in SchemaMappingInspector

This commit is contained in:
rstoyanchev
2023-04-20 15:46:05 +01:00
parent d933828a23
commit fb6a225193
3 changed files with 50 additions and 38 deletions

View File

@@ -414,8 +414,8 @@ Spring for GraphQL defines the `SelfDescribingDataFetcher` interface to allow a
`DataFetcher` to expose return type information. All Spring `DataFetcher` implementations
implement this interface. That includes those for <<controllers>>, and those for
<<data.querydsl>> and <<data.querybyexample>> Spring Data repositories. For annotated
controllers, the return type is derived from the declared return type on `@SchemaMapping`
methods.
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,
and the properties of Java objects returned from `DataFetcher` implementations in order
@@ -432,18 +432,23 @@ GraphQL schema inspection:
<1> List of schema fields and their source types that are not mapped
<2> List of schema types that are skipped, as explained next
There are limits to what schema mappings inspection can do, in particular when there is insufficient Java
type information. This is the case if an annotated controller method returns
`java.lang.Object` such as for a `union` type, or if a `DataFetcher` does not implement
`SelfDescribingDataFetcher`. If a schema type is skipped, its name is listed as such in
the report, and a DEBUG message is logged to provide a reason for why it was skipped.
There are limits to what schema mappings inspection can do, in particular when there is
insufficient Java type information. This is the case if an annotated controller method is
declared to return `java.lang.Object`, or if the return type has an unspecified generic
parameter such as `List<?>`, or if the `DataFetcher` does not implement
`SelfDescribingDataFetcher` and the return type is not even known. In such cases, the
Java object type structure remains unknown, and the schema type is listed as skipped in
the resulting report. For every skipped type, a DEBUG message is logged to indicate why
it was skipped.
For schema `interface` types, the inspection currently checks only fields declared
directly on the interface against properties of the Java return type declared by the
`DataFetcher`. Additional fields on concrete implementations are not inspected, as there
is insufficient information about what Java types may be returned at runtime. This could
be improved in a future release to extend inspection to schema `interface` implementing
types to look for a match among subtypes of the declared Java return type.
Schema union types are always skipped because there is no way for a controller method to
declare such a return type in Java, and the Java type structure is unknown.
Schema interface types are supported only as far as fields declared directly, which are
compared against properties on the Java type declared by a `SelfDescribingDataFetcher`.
Additional fields on concrete implementations are not inspected. This could be improved
in a future release to also inspect scheam `interface` implementation types and to try
to find a match among subtypes of the declared Java return type.
[[execution.graphqlsource.operation-caching]]

View File

@@ -48,24 +48,30 @@ import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* Provide {@link #inspect(GraphQLSchema, RuntimeWiring)} method that checks if
* schema fields are covered by either a {@link DataFetcher} registration, or a
* Java object property. Fields that have neither are reported as unmapped in
* the output {@link Report}.
* Declares an {@link #inspect(GraphQLSchema, RuntimeWiring)} method that checks
* if schema fields are covered either by a {@link DataFetcher} registration,
* or match a Java object property. Fields that have neither are reported as
* "unmapped" in the resulting {@link Report Resport}.
*
* <p>The inspection depends on {@code DataFetcher}s to expose return type
* information by implementing {@link SelfDescribingDataFetcher}. This allows
* checking if Java object types have properties that match schema fields.
* If a {@code DataFetcher} does not implement this interface, then the Java
* object type is not known, and the field type is reported as skipped.
* <p>The inspection depends on {@code DataFetcher}s to be
* {@link SelfDescribingDataFetcher} to be able to compare schema type and Java
* object type structure. If a {@code DataFetcher} does not implement this
* interface, then the Java type remains unknown, and the field type is reported
* as "skipped".
*
* <p>The {@link SelfDescribingDataFetcher} for annotated controller methods
* exposes the declared return type of the controller method. If the return type
* is {@link Object} such as for a union, then the Java object structure is
* not known, and the field output type is reported as skipped.
* <p>The {@code SelfDescribingDataFetcher} for an annotated controller method
* derives type information from the controller method signature. If the declared
* return type is {@link Object}, or an unspecified generic parameter such as
* {@code List<?>} then the Java type structure remains unknown, and the field
* output type is reported as skipped.
*
* <p>Union types are automatically skipped because there is no way for an
* annotated controller method to declare the actual Java types.
* <p>Unions are always skipped because there is no way for an annotated
* controller method to express that in a return type, and the Java type
* structure remains unknown.
*
* <p>Interfaces are supported only as far as fields declared directly on the
* interface, which are compared against properties of the Java type declared
* by a {@code SelfDescribingDataFetcher}.
*
* @author Brian Clozel
* @author Rossen Stoyanchev
@@ -242,9 +248,9 @@ class SchemaMappingInspector {
/**
* Container of unmapped fields and skipped types.
* @param unmappedFields fields with neither {@link DataFetcher} mapping nor Object property
* @param skippedTypes types that could not be verified, e.g. union
* The report produced as a result of schema mappings inspection.
* @param unmappedFields a map with type names as keys, and unmapped field names as values
* @param skippedTypes the names of types skipped by the inspection
*/
public record Report(MultiValueMap<String, String> unmappedFields, Set<String> skippedTypes) {

View File

@@ -21,19 +21,20 @@ import graphql.schema.DataFetcher;
import org.springframework.core.ResolvableType;
/**
* Specialized {@link DataFetcher} that can provide information about itself.
* Specialized {@link DataFetcher} that exposes additional details such as
* return type information.
*
* @author Brian Clozel
* @author Rossen Stoyanchev
* @since 1.2.0
*/
public interface SelfDescribingDataFetcher<T> extends DataFetcher<T> {
/**
* The type that the {@link DataFetcher} returns.
* <p>This could be a type from a {@code @Controller} method declaration
* or the type expected to be returned by the {@link DataFetcher}
* possibly backed by a Spring Data repository.
* The concrete type of the returned instance might be a subclass.
* @return the type of the data returned by the data fetcher.
* The return type of this {@link DataFetcher}.
* <p>This could be derived from the method signature of an annotated
* {@code @Controller} method, the domain type of a {@link DataFetcher}
* backed by a Spring Data repository, or other.
*/
ResolvableType getReturnType();