Skip to content

Commit

Permalink
Refactoring in SchemaMappingInspector
Browse files Browse the repository at this point in the history
Break down inspectType into two methods, one to inspect fields in a
GraphQLFieldContainer, and another to inspect a field output type.
The former is more straight forward and applies to Query, Mutation,
and Subscription. The latter does type handling such as unwrapping,
and nesting, and also requires a non-null Java ResolvableType.
  • Loading branch information
rstoyanchev committed Apr 26, 2023
1 parent 29c5361 commit 3df8fcb
Showing 1 changed file with 63 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,12 @@ private SchemaMappingInspector(GraphQLSchema schema, RuntimeWiring runtimeWiring
*/
public Report inspect() {

inspectType(this.schema.getQueryType(), null, false);

inspectFields(this.schema.getQueryType(), null);
if (this.schema.isSupportingMutations()) {
inspectType(this.schema.getMutationType(), null, false);
inspectFields(this.schema.getMutationType(), null);
}

if (this.schema.isSupportingSubscriptions()) {
inspectType(this.schema.getSubscriptionType(), null, false);
inspectFields(this.schema.getSubscriptionType(), null);
}

inspectDataFetcherRegistrations();
Expand All @@ -128,83 +126,91 @@ public Report inspect() {
}

/**
* Inspect the given schema {@link GraphQLType}, which is either one of the top-level
* types Query, Mutation, or Subscription, or the output type for a field in which
* case there should also be {@link ResolvableType} for the corresponding Java type.
*
* @param type the GraphQL schema type to inspect
* @param resolvableType the corresponding Java type, but {@code null} for top-level types
* @param isSubscriptionField whether this is the type for a subscription field
* Inspect the given {@code GraphQLFieldsContainer} check against {@code DataFetcher}
* registrations, or Java properties in the given {@code ResolvableType}.
* @param fields the GraphQL schema type to inspect
* @param resolvableType the Java type to match against, or {@code null} if
* not applicable such as for Query, Mutation, or Subscription
*/
@SuppressWarnings("rawtypes")
private void inspectType(GraphQLType type, @Nullable ResolvableType resolvableType, boolean isSubscriptionField) {
Assert.notNull(type, "No GraphQLType");
private void inspectFields(GraphQLFieldsContainer fields, @Nullable ResolvableType resolvableType) {

Map<String, DataFetcher> dataFetcherMap = this.runtimeWiring.getDataFetcherForType(fields.getName());

for (GraphQLFieldDefinition field : fields.getFieldDefinitions()) {
String fieldName = field.getName();
if (dataFetcherMap.containsKey(fieldName)) {
DataFetcher<?> fetcher = dataFetcherMap.get(fieldName);
if (fetcher instanceof SelfDescribingDataFetcher<?> selfDescribingDataFetcher) {
inspectFieldType(
field.getType(), selfDescribingDataFetcher.getReturnType(),
(fields == this.schema.getSubscriptionType()));
}
else if (isNotScalarOrEnumType(field.getType())) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped '" + typeNameToString(field.getType()) + "': " +
fetcher.getClass().getName() + " does not implement SelfDescribingDataFetcher.");
}
this.reportBuilder.addSkippedType(typeNameToString(field.getType()));
}
}
else if (resolvableType == null || !hasProperty(resolvableType, fieldName)) {
this.reportBuilder.addUnmappedField(fields.getName(), fieldName);
}
}
}

/**
* Inspect the output {@link GraphQLType} of a field.
* @param outputType the field type to inspect
* @param resolvableType the expected Java return type
* @param isSubscriptionField whether this is for a subscription field
*/
private void inspectFieldType(GraphQLType outputType, ResolvableType resolvableType, boolean isSubscriptionField) {

// Remove GraphQL type wrappers, and nest within Java generic types
type = unwrapIfNonNull(type);
if (isConnectionType(type)) {
type = getConnectionNodeType(type);
resolvableType = nestForConnection(resolvableType, type);
outputType = unwrapIfNonNull(outputType);
if (isConnectionType(outputType)) {
outputType = getConnectionNodeType(outputType);
resolvableType = nestForConnection(resolvableType);
}
else if (type instanceof GraphQLList listType) {
type = unwrapIfNonNull(listType.getWrappedType());
resolvableType = nestForList(resolvableType, type, isSubscriptionField);
else if (outputType instanceof GraphQLList listType) {
outputType = unwrapIfNonNull(listType.getWrappedType());
resolvableType = nestForList(resolvableType, isSubscriptionField);
}
else if (resolvableType != null) {
else {
resolvableType = nestIfReactive(resolvableType);
}

// Type already inspected?
if (addAndCheckIfAlreadyInspected(type)) {
if (addAndCheckIfAlreadyInspected(outputType)) {
return;
}

// Can we inspect GraphQL type?
if (!(type instanceof GraphQLFieldsContainer fieldContainer)) {
if (isNotScalarOrEnumType(type)) {
if (!(outputType instanceof GraphQLFieldsContainer fieldContainer)) {
if (isNotScalarOrEnumType(outputType)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped '" + typeNameToString(type) + "': " +
"inspection does not support " + type.getClass().getSimpleName() + ".");
logger.debug("Skipped '" + typeNameToString(outputType) + "': " +
"inspection does not support " + outputType.getClass().getSimpleName() + ".");
}
this.reportBuilder.addSkippedType(typeNameToString(type));
this.reportBuilder.addSkippedType(typeNameToString(outputType));
}
return;
}

// Can we inspect Java type?
if (resolvableType != null && resolvableType.resolve(Object.class) == Object.class) {
if (resolvableType.resolve(Object.class) == Object.class) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped '" + typeNameToString(type) + "': " +
logger.debug("Skipped '" + typeNameToString(outputType) + "': " +
"inspection could not determine the Java object return type.");
}
this.reportBuilder.addSkippedType(typeNameToString(type));
this.reportBuilder.addSkippedType(typeNameToString(outputType));
return;
}

String typeName = fieldContainer.getName();
Map<String, DataFetcher> dataFetcherMap = this.runtimeWiring.getDataFetcherForType(typeName);

for (GraphQLFieldDefinition field : fieldContainer.getFieldDefinitions()) {
String fieldName = field.getName();
if (dataFetcherMap.containsKey(fieldName)) {
DataFetcher<?> fetcher = dataFetcherMap.get(fieldName);
if (fetcher instanceof SelfDescribingDataFetcher<?> selfDescribingDataFetcher) {
inspectType(
field.getType(), selfDescribingDataFetcher.getReturnType(),
(type == this.schema.getSubscriptionType()));
}
else if (isNotScalarOrEnumType(field.getType())) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped '" + typeNameToString(field.getType()) + "': " +
fetcher.getClass().getName() + " does not implement SelfDescribingDataFetcher.");
}
this.reportBuilder.addSkippedType(typeNameToString(field.getType()));
}
}
else if (resolvableType == null || !hasProperty(resolvableType, fieldName)) {
this.reportBuilder.addUnmappedField(typeName, fieldName);
}
}
// Nest within the
inspectFields(fieldContainer, resolvableType);
}

private GraphQLType unwrapIfNonNull(GraphQLType type) {
Expand All @@ -225,8 +231,7 @@ private GraphQLType getConnectionNodeType(GraphQLType type) {
return type;
}

private ResolvableType nestForConnection(@Nullable ResolvableType type, GraphQLType graphQLType) {
Assert.state(type != null, "No Java type for " + typeNameToString(graphQLType));
private ResolvableType nestForConnection(ResolvableType type) {
type = nestIfReactive(type);
Assert.state(type.hasGenerics(), "Expected type with generics: " + type);
return type.getNested(2);
Expand All @@ -242,8 +247,7 @@ private ResolvableType nestIfReactive(ResolvableType type) {
return type;
}

private ResolvableType nestForList(@Nullable ResolvableType type, GraphQLType graphQlType, boolean subscription) {
Assert.state(type != null, "No Java type for " + typeNameToString(graphQlType));
private ResolvableType nestForList(ResolvableType type, boolean subscription) {
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(type.resolve(Object.class));
if (adapter != null) {
Assert.state(!adapter.isNoValue(), "Expected List compatible type: " + type);
Expand Down

0 comments on commit 3df8fcb

Please sign in to comment.