diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java
index 4520d663..cd5b75a2 100644
--- a/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java
+++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java
@@ -138,7 +138,7 @@ final class DefaultSchemaResourceGraphQlSourceBuilder
configureGraphQl(builder -> {
GraphQLSchema schema = builder.build().getGraphQLSchema();
- SchemaMappingReport report = SchemaMappingInspector.inspect(schema, runtimeWiring);
+ SchemaReport report = SchemaMappingInspector.inspect(schema, runtimeWiring);
logger.info(report);
});
diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java
index 5b2bb571..8e25819c 100644
--- a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java
+++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java
@@ -16,12 +16,13 @@
package org.springframework.graphql.execution;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.function.Supplier;
import graphql.schema.DataFetcher;
import graphql.schema.FieldCoordinates;
@@ -52,14 +53,10 @@ import org.springframework.util.MultiValueMap;
/**
* 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 SchemaMappingReport}. The inspection also
- * performs a reverse check for {@code DataFetcher} registrations against schema
- * fields that don't exist.
+ * if schema mappings.
*
- *
The schema field inspection depends on {@code DataFetcher}s to be
- * {@link SelfDescribingDataFetcher} to be able to compare schema type and Java
+ *
Schema mapping checks depend on {@code DataFetcher}s to be
+ * {@link SelfDescribingDataFetcher} in order 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".
@@ -95,14 +92,10 @@ final class SchemaMappingInspector {
private final ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
- private final MultiValueMap unmappedFields = new LinkedMultiValueMap<>();
-
- private final Map> unmappedDataFetchers = new LinkedHashMap<>();
-
- private final Set skippedTypes = new LinkedHashSet<>();
+ private final ReportBuilder reportBuilder = new ReportBuilder();
@Nullable
- private SchemaMappingReport report;
+ private SchemaReport report;
private SchemaMappingInspector(GraphQLSchema schema, RuntimeWiring runtimeWiring) {
@@ -114,20 +107,19 @@ final class SchemaMappingInspector {
/**
- * Perform an inspection and create a {@link SchemaMappingReport}.
+ * Perform an inspection and create a {@link SchemaReport}.
* The inspection is one once only, during the first call to this method.
*/
- public SchemaMappingReport getOrCreateReport() {
+ public SchemaReport getOrCreateReport() {
if (this.report == null) {
- checkSchema();
+ checkSchemaFields();
checkDataFetcherRegistrations();
- this.report = new SchemaMappingReport(
- this.unmappedFields, this.unmappedDataFetchers, this.skippedTypes);
+ this.report = this.reportBuilder.build();
}
return this.report;
}
- private void checkSchema() {
+ private void checkSchemaFields() {
checkFieldsContainer(this.schema.getQueryType(), null);
@@ -143,52 +135,50 @@ final class SchemaMappingInspector {
/**
* Check the given {@code GraphQLFieldsContainer} against {@code DataFetcher}
* registrations, or Java properties of the given {@code ResolvableType}.
- * @param fields the GraphQL interface or object type to check
+ * @param fieldContainer the GraphQL interface or object type to check
* @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 checkFieldsContainer(GraphQLFieldsContainer fields, @Nullable ResolvableType resolvableType) {
+ private void checkFieldsContainer(GraphQLFieldsContainer fieldContainer, @Nullable ResolvableType resolvableType) {
- Map dataFetcherMap = this.runtimeWiring.getDataFetcherForType(fields.getName());
+ String typeName = fieldContainer.getName();
+ Map dataFetcherMap = this.runtimeWiring.getDataFetcherForType(typeName);
- for (GraphQLFieldDefinition field : fields.getFieldDefinitions()) {
+ for (GraphQLFieldDefinition field : fieldContainer.getFieldDefinitions()) {
String fieldName = field.getName();
- if (dataFetcherMap.containsKey(fieldName)) {
- DataFetcher> fetcher = dataFetcherMap.get(fieldName);
- if (fetcher instanceof SelfDescribingDataFetcher> selfDescribingDataFetcher) {
- checkFieldType(
- field.getType(), selfDescribingDataFetcher.getReturnType(),
- (fields == this.schema.getSubscriptionType()));
- }
- else if (isNotScalarOrEnumType(field.getType())) {
- addSkippedType(field.getType(), () ->
- fetcher.getClass().getName() + " does not implement SelfDescribingDataFetcher.");
- }
+ DataFetcher> dataFetcher = dataFetcherMap.get(fieldName);
+ if (dataFetcher != null) {
+ checkField(fieldContainer, field, dataFetcher);
}
else if (resolvableType == null || !hasProperty(resolvableType, fieldName)) {
- this.unmappedFields.add(fields.getName(), fieldName);
+ this.reportBuilder.unmappedField(FieldCoordinates.coordinates(typeName, fieldName));
}
}
}
/**
* Check the output {@link GraphQLType} of a field against the given DataFetcher return type.
- * @param outputType the field type to inspect
- * @param resolvableType the expected Java return type
- * @param isSubscriptionField whether this is for a subscription field
+ * @param parent the parent of the field
+ * @param field the field to inspect
+ * @param dataFetcher the registered DataFetcher
*/
- private void checkFieldType(GraphQLType outputType, ResolvableType resolvableType, boolean isSubscriptionField) {
+ private void checkField(GraphQLFieldsContainer parent, GraphQLFieldDefinition field, DataFetcher> dataFetcher) {
+
+ ResolvableType resolvableType = ResolvableType.NONE;
+ if (dataFetcher instanceof SelfDescribingDataFetcher> selfDescribingDataFetcher) {
+ resolvableType = selfDescribingDataFetcher.getReturnType();
+ }
// Remove GraphQL type wrappers, and nest within Java generic types
- outputType = unwrapIfNonNull(outputType);
+ GraphQLType outputType = unwrapIfNonNull(field.getType());
if (isPaginatedType(outputType)) {
outputType = getPaginatedType((GraphQLObjectType) outputType);
resolvableType = nestForConnection(resolvableType);
}
else if (outputType instanceof GraphQLList listType) {
outputType = unwrapIfNonNull(listType.getWrappedType());
- resolvableType = nestForList(resolvableType, isSubscriptionField);
+ resolvableType = nestForList(resolvableType, (parent == this.schema.getSubscriptionType()));
}
else {
resolvableType = nestIfReactive(resolvableType);
@@ -202,15 +192,16 @@ final class SchemaMappingInspector {
// Can we inspect GraphQL type?
if (!(outputType instanceof GraphQLFieldsContainer fieldContainer)) {
if (isNotScalarOrEnumType(outputType)) {
- String schemaTypeName = outputType.getClass().getSimpleName();
- addSkippedType(outputType, () -> "inspection does not support " + schemaTypeName + ".");
+ FieldCoordinates coordinates = FieldCoordinates.coordinates(parent.getName(), field.getName());
+ addSkippedType(outputType, coordinates, "Unsupported schema type");
}
return;
}
// Can we inspect Java type?
if (resolvableType.resolve(Object.class) == Object.class) {
- addSkippedType(outputType, () -> "inspection could not determine the Java object return type.");
+ FieldCoordinates coordinates = FieldCoordinates.coordinates(parent.getName(), field.getName());
+ addSkippedType(outputType, coordinates, "No Java type information");
return;
}
@@ -236,6 +227,9 @@ final class SchemaMappingInspector {
}
private ResolvableType nestForConnection(ResolvableType type) {
+ if (type == ResolvableType.NONE) {
+ return type;
+ }
type = nestIfReactive(type);
if (logger.isDebugEnabled() && type.getGenerics().length != 1) {
logger.debug("Expected Connection type to have a generic parameter: " + type);
@@ -256,6 +250,9 @@ final class SchemaMappingInspector {
}
private ResolvableType nestForList(ResolvableType type, boolean subscription) {
+ if (type == ResolvableType.NONE) {
+ return type;
+ }
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(type.resolve(Object.class));
if (adapter != null) {
if (logger.isDebugEnabled() && adapter.isNoValue()) {
@@ -266,7 +263,7 @@ final class SchemaMappingInspector {
return type;
}
}
- if (logger.isDebugEnabled() && (!type.isArray() && type.getGenerics().length != 1)) {
+ if (logger.isDebugEnabled() && !type.isArray() && type.getGenerics().length != 1) {
logger.debug("Expected List compatible type: " + type);
}
return type.getNested(2);
@@ -295,21 +292,21 @@ final class SchemaMappingInspector {
}
}
- private void addSkippedType(GraphQLType type, Supplier reason) {
+ private void addSkippedType(GraphQLType type, FieldCoordinates coordinates, String reason) {
String typeName = typeNameToString(type);
- this.skippedTypes.add(typeName);
+ this.reportBuilder.skippedType(type, coordinates);
if (logger.isDebugEnabled()) {
- logger.debug("Skipped '" + typeName + "': " + reason.get());
+ logger.debug("Skipped '" + typeName + "': " + reason);
}
}
@SuppressWarnings("rawtypes")
private void checkDataFetcherRegistrations() {
this.runtimeWiring.getDataFetchers().forEach((typeName, registrations) ->
- registrations.forEach((fieldName, fetcher) -> {
+ registrations.forEach((fieldName, dataFetcher) -> {
FieldCoordinates coordinates = FieldCoordinates.coordinates(typeName, fieldName);
if (this.schema.getFieldDefinition(coordinates) == null) {
- this.unmappedDataFetchers.put(coordinates, fetcher);
+ this.reportBuilder.unmappedRegistration(coordinates, dataFetcher);
}
}));
}
@@ -321,9 +318,120 @@ final class SchemaMappingInspector {
* @param runtimeWiring for {@code DataFetcher} registrations
* @return the created report
*/
- public static SchemaMappingReport inspect(GraphQLSchema schema, RuntimeWiring runtimeWiring) {
+ public static SchemaReport inspect(GraphQLSchema schema, RuntimeWiring runtimeWiring) {
return new SchemaMappingInspector(schema, runtimeWiring).getOrCreateReport();
}
+ /**
+ * Helps to build a {@link SchemaReport}.
+ */
+ private class ReportBuilder {
+
+ private final List unmappedFields = new ArrayList<>();
+
+ private final Map> unmappedRegistrations = new LinkedHashMap<>();
+
+ private final List skippedTypes = new ArrayList<>();
+
+ public void unmappedField(FieldCoordinates coordinates) {
+ this.unmappedFields.add(coordinates);
+ }
+
+ public void unmappedRegistration(FieldCoordinates coordinates, DataFetcher> dataFetcher) {
+ this.unmappedRegistrations.put(coordinates, dataFetcher);
+ }
+
+ public void skippedType(GraphQLType type, FieldCoordinates coordinates) {
+ this.skippedTypes.add(new DefaultSkippedType(type, coordinates));
+ }
+
+ public SchemaReport build() {
+ return new DefaultSchemaReport(this.unmappedFields, this.unmappedRegistrations, this.skippedTypes);
+ }
+
+ }
+
+
+ /**
+ * Default implementation of {@link SchemaReport}.
+ */
+ private class DefaultSchemaReport implements SchemaReport {
+
+ private final List unmappedFields;
+
+ private final Map> unmappedRegistrations;
+
+ private final List skippedTypes;
+
+ public DefaultSchemaReport(
+ List unmappedFields, Map> unmappedRegistrations,
+ List skippedTypes) {
+
+ this.unmappedFields = Collections.unmodifiableList(unmappedFields);
+ this.unmappedRegistrations = Collections.unmodifiableMap(unmappedRegistrations);
+ this.skippedTypes = Collections.unmodifiableList(skippedTypes);
+ }
+
+ @Override
+ public List unmappedFields() {
+ return this.unmappedFields;
+ }
+
+ @Override
+ public Map> unmappedRegistrations() {
+ return this.unmappedRegistrations;
+ }
+
+ @Override
+ public List skippedTypes() {
+ return this.skippedTypes;
+ }
+
+ @Override
+ public GraphQLSchema schema() {
+ return SchemaMappingInspector.this.schema;
+ }
+
+ @Override
+ @Nullable
+ public DataFetcher> dataFetcher(FieldCoordinates coordinates) {
+ return SchemaMappingInspector.this.runtimeWiring
+ .getDataFetcherForType(coordinates.getTypeName())
+ .get(coordinates.getFieldName());
+ }
+
+ @Override
+ public String toString() {
+ return "GraphQL schema inspection:\n" +
+ "\tUnmapped fields: " + formatUnmappedFields() + "\n" +
+ "\tUnmapped registrations: " + this.unmappedRegistrations + "\n" +
+ "\tSkipped types: " + this.skippedTypes;
+ }
+
+ private String formatUnmappedFields() {
+ MultiValueMap map = new LinkedMultiValueMap<>();
+ this.unmappedFields.forEach(coordinates -> {
+ List fields = map.computeIfAbsent(coordinates.getTypeName(), s -> new ArrayList<>());
+ fields.add(coordinates.getFieldName());
+ });
+ return map.toString();
+ }
+
+ }
+
+
+ /**
+ * Default implementation of a {@link SchemaReport.SkippedType}.
+ */
+ private record DefaultSkippedType(
+ GraphQLType type, FieldCoordinates fieldCoordinates) implements SchemaReport.SkippedType {
+
+ @Override
+ public String toString() {
+ return typeNameToString(this.type);
+ }
+
+ }
+
}
diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingReport.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingReport.java
deleted file mode 100644
index a2c54d2a..00000000
--- a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingReport.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright 2020-2023 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.springframework.graphql.execution;
-
-
-import java.util.Map;
-import java.util.Set;
-
-import graphql.schema.DataFetcher;
-import graphql.schema.FieldCoordinates;
-
-import org.springframework.util.MultiValueMap;
-
-
-/**
- * The report produced as a result of schema mappings inspection.
- * @param unmappedFields map with type names as keys, and unmapped field names as values
- * @param unmappedDataFetchers map with unmapped {@code DataFetcher}s and their field coordinates
- * @param skippedTypes the names of types skipped by the inspection
- *
- * @since 1.2.0
- */
-public record SchemaMappingReport(
- MultiValueMap unmappedFields,
- Map> unmappedDataFetchers,
- Set skippedTypes) {
-
- @Override
- public String toString() {
- return "GraphQL schema inspection:\n" +
- "\tUnmapped fields: " + this.unmappedFields + "\n" +
- "\tUnmapped DataFetcher registrations: " + this.unmappedDataFetchers + "\n" +
- "\tSkipped types: " + this.skippedTypes;
- }
-
-}
diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaReport.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaReport.java
new file mode 100644
index 00000000..5a8ee775
--- /dev/null
+++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaReport.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2020-2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.graphql.execution;
+
+import java.util.List;
+import java.util.Map;
+
+import graphql.language.ListType;
+import graphql.language.NonNullType;
+import graphql.schema.DataFetcher;
+import graphql.schema.FieldCoordinates;
+import graphql.schema.GraphQLSchema;
+import graphql.schema.GraphQLType;
+
+import org.springframework.lang.Nullable;
+
+/**
+ * Report produced as a result of inspecting schema mappings.
+ *
+ * The inspection 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 {@link #unmappedFields()}.
+ * The inspection also checks if any {@code DataFetcher} are registered against
+ * schema fields that don't exist and reports those as {@link #unmappedRegistrations()}.
+ *
+ * @author Rossen Stoyanchev
+ * @since 1.2.0
+ */
+public interface SchemaReport {
+
+ /**
+ * Return the inspected schema with type and field definitions.
+ */
+ GraphQLSchema schema();
+
+ /**
+ * Return the coordinates of unmapped fields. Such fields have neither a
+ * {@link DataFetcher} registration, such as a {@code @SchemaMapping}
+ * method, nor a matching Java property in the return type from the parent
+ * {@code DataFetcher}.
+ */
+ List unmappedFields();
+
+ /**
+ * Return the coordinates for invalid {@link DataFetcher} registrations
+ * referring to fields that don't exist in the schema.
+ */
+ Map> unmappedRegistrations();
+
+ /**
+ * Return types skipped during the inspection, either because the schema type
+ * is not supported, e.g. union, or because there is insufficient Java type
+ * information, e.g. controller method that returns {@code Object} or wrapper
+ * type (collection, reactive, asynchronous) with wildcard generics.
+ */
+ List skippedTypes();
+
+ /**
+ * Return the {@code DataFetcher} for the given field coordinates, if registered.
+ */
+ @Nullable
+ DataFetcher> dataFetcher(FieldCoordinates coordinates);
+
+
+ /**
+ * Information about a schema type skipped during the inspection.
+ */
+ interface SkippedType {
+
+ /**
+ * Return the type that was skipped. This corresponds to the output type
+ * of the {@link #fieldCoordinates() field} where the type was
+ * encountered, possibly with {@link NonNullType} and {@link ListType}
+ * wrapper types removed.
+ */
+ GraphQLType type();
+
+ /**
+ * Return the coordinates of the field where the type was encountered.
+ */
+ FieldCoordinates fieldCoordinates();
+
+ }
+
+}
diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java
index 5cecf44e..160d1d5d 100644
--- a/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java
+++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java
@@ -20,10 +20,10 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.CompletableFuture;
import graphql.schema.FieldCoordinates;
+import graphql.schema.GraphQLNamedType;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
@@ -68,7 +68,7 @@ class SchemaMappingInspectorTests {
greeting: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, EmptyController.class);
+ SchemaReport report = inspectSchema(schema, EmptyController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Query", "greeting");
}
@@ -79,7 +79,7 @@ class SchemaMappingInspectorTests {
greeting: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, GreetingController.class);
+ SchemaReport report = inspectSchema(schema, GreetingController.class);
assertThatReport(report).isEmpty();
}
@@ -96,7 +96,7 @@ class SchemaMappingInspectorTests {
missing: Boolean
}
""";
- SchemaMappingReport report = inspectSchema(schema, BookController.class);
+ SchemaReport report = inspectSchema(schema, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Book", "missing");
}
@@ -128,7 +128,7 @@ class SchemaMappingInspectorTests {
missing: Boolean
}
""";
- SchemaMappingReport report = inspectSchema(schema, BookController.class);
+ SchemaReport report = inspectSchema(schema, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Book", "missing");
}
@@ -141,7 +141,7 @@ class SchemaMappingInspectorTests {
greeting: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, EmptyController.class);
+ SchemaReport report = inspectSchema(schema, EmptyController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Query", "greeting");
}
@@ -166,7 +166,7 @@ class SchemaMappingInspectorTests {
name: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, GreetingController.class);
+ SchemaReport report = inspectSchema(schema, GreetingController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Mutation", "createBook");
}
@@ -185,7 +185,7 @@ class SchemaMappingInspectorTests {
name: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, GreetingController.class, BookController.class);
+ SchemaReport report = inspectSchema(schema, GreetingController.class, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(0).hasSkippedTypeCount(0);
}
@@ -205,7 +205,7 @@ class SchemaMappingInspectorTests {
name: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, GreetingController.class);
+ SchemaReport report = inspectSchema(schema, GreetingController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Mutation", "createBook");
}
@@ -230,7 +230,7 @@ class SchemaMappingInspectorTests {
name: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, GreetingController.class);
+ SchemaReport report = inspectSchema(schema, GreetingController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Subscription", "bookSearch");
}
@@ -249,7 +249,7 @@ class SchemaMappingInspectorTests {
name: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, GreetingController.class, BookController.class);
+ SchemaReport report = inspectSchema(schema, GreetingController.class, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(0).hasSkippedTypeCount(0);
}
@@ -269,7 +269,7 @@ class SchemaMappingInspectorTests {
name: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, GreetingController.class);
+ SchemaReport report = inspectSchema(schema, GreetingController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Subscription", "bookSearch");
}
@@ -291,7 +291,7 @@ class SchemaMappingInspectorTests {
name: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, BookController.class);
+ SchemaReport report = inspectSchema(schema, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(0).hasSkippedTypeCount(0);
}
@@ -308,7 +308,7 @@ class SchemaMappingInspectorTests {
fetcher: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, BookController.class);
+ SchemaReport report = inspectSchema(schema, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(0).hasSkippedTypeCount(0);
}
@@ -331,7 +331,7 @@ class SchemaMappingInspectorTests {
lastName: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, BatchMappingBookController.class);
+ SchemaReport report = inspectSchema(schema, BatchMappingBookController.class);
assertThatReport(report).hasUnmappedFieldCount(0).hasSkippedTypeCount(0);
}
@@ -348,7 +348,7 @@ class SchemaMappingInspectorTests {
missing: Boolean
}
""";
- SchemaMappingReport report = inspectSchema(schema, BookController.class);
+ SchemaReport report = inspectSchema(schema, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Book", "missing");
}
@@ -359,7 +359,7 @@ class SchemaMappingInspectorTests {
anything: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, GreetingController.class);
+ SchemaReport report = inspectSchema(schema, GreetingController.class);
assertThatReport(report).hasUnmappedDataFetcherCount(1).containsUnmappedDataFetchersFor("Query", "greeting");
}
@@ -382,7 +382,7 @@ class SchemaMappingInspectorTests {
missing: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, BookController.class);
+ SchemaReport report = inspectSchema(schema, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Author", "missing");
}
@@ -404,7 +404,7 @@ class SchemaMappingInspectorTests {
missing: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, TeamController.class);
+ SchemaReport report = inspectSchema(schema, TeamController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("TeamMember", "missing");
}
@@ -423,7 +423,7 @@ class SchemaMappingInspectorTests {
missing: Boolean
}
""";
- SchemaMappingReport report = inspectSchema(schema, BookController.class);
+ SchemaReport report = inspectSchema(schema, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(1).containsUnmappedFields("Book", "missing");
}
@@ -444,7 +444,7 @@ class SchemaMappingInspectorTests {
name: String
}
""";
- SchemaMappingReport report = inspectSchema(schema, UnionController.class);
+ SchemaReport report = inspectSchema(schema, UnionController.class);
assertThatReport(report).hasUnmappedFieldCount(0).hasSkippedTypeCount(1).containsSkippedTypes("FooBar");
}
@@ -466,7 +466,7 @@ class SchemaMappingInspectorTests {
.type("Query", builder -> builder.dataFetcher("bookById", environment -> null))
.build();
- SchemaMappingReport report = SchemaMappingInspector.inspect(schema, wiring);
+ SchemaReport report = SchemaMappingInspector.inspect(schema, wiring);
assertThatReport(report).hasUnmappedFieldCount(0).hasSkippedTypeCount(1).containsSkippedTypes("Book");
}
@@ -482,7 +482,7 @@ class SchemaMappingInspectorTests {
name: String
}
""";
- SchemaMappingReport report = inspectSchema(schemaContent, BookController.class);
+ SchemaReport report = inspectSchema(schemaContent, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(0).hasSkippedTypeCount(1).containsSkippedTypes("Book");
}
@@ -499,7 +499,7 @@ class SchemaMappingInspectorTests {
.type("Query", builder -> builder.dataFetcher("greeting", environment -> null))
.build();
- SchemaMappingReport report = SchemaMappingInspector.inspect(schema, wiring);
+ SchemaReport report = SchemaMappingInspector.inspect(schema, wiring);
assertThatReport(report).hasUnmappedFieldCount(0).hasSkippedTypeCount(0);
}
@@ -531,18 +531,18 @@ class SchemaMappingInspectorTests {
id: ID
}
""";
- SchemaMappingReport report = inspectSchema(schema, BookController.class);
+ SchemaReport report = inspectSchema(schema, BookController.class);
assertThatReport(report).hasUnmappedFieldCount(1).hasSkippedTypeCount(0);
assertThat(report.toString()).isEqualTo("""
GraphQL schema inspection:
Unmapped fields: {Book=[missing]}
- Unmapped DataFetcher registrations: {Book.fetcher=BookController#fetcher[1 args], Query.paginatedBooks=BookController#paginatedBooks[0 args], Query.bookObject=BookController#bookObject[1 args], Query.bookById=BookController#bookById[1 args]}
+ Unmapped registrations: {Book.fetcher=BookController#fetcher[1 args], Query.paginatedBooks=BookController#paginatedBooks[0 args], Query.bookObject=BookController#bookObject[1 args], Query.bookById=BookController#bookById[1 args]}
Skipped types: []""");
}
}
- private SchemaMappingReport inspectSchema(String schemaContent, Class>... controllers) {
+ private SchemaReport inspectSchema(String schemaContent, Class>... controllers) {
GraphQLSchema schema = SchemaGenerator.createdMockedSchema(schemaContent);
RuntimeWiring runtimeWiring = createRuntimeWiring(controllers);
return SchemaMappingInspector.inspect(schema, runtimeWiring);
@@ -565,7 +565,7 @@ class SchemaMappingInspectorTests {
return wiringBuilder.build();
}
- static SchemaInspectionReportAssert assertThatReport(SchemaMappingReport actual) {
+ static SchemaInspectionReportAssert assertThatReport(SchemaReport actual) {
return new SchemaInspectionReportAssert(actual);
}
@@ -687,9 +687,9 @@ class SchemaMappingInspectorTests {
private static class SchemaInspectionReportAssert
- extends AbstractAssert {
+ extends AbstractAssert {
- public SchemaInspectionReportAssert(SchemaMappingReport actual) {
+ public SchemaInspectionReportAssert(SchemaReport actual) {
super(actual, SchemaInspectionReportAssert.class);
}
@@ -698,8 +698,8 @@ class SchemaMappingInspectorTests {
if (!this.actual.unmappedFields().isEmpty()) {
failWithMessage("Report contains missing fields: %s", this.actual.unmappedFields());
}
- if (!this.actual.unmappedDataFetchers().isEmpty()) {
- failWithMessage("Report contains missing DataFetcher registrations for %s", this.actual.unmappedDataFetchers());
+ if (!this.actual.unmappedRegistrations().isEmpty()) {
+ failWithMessage("Report contains missing DataFetcher registrations for %s", this.actual.unmappedRegistrations());
}
if (!this.actual.skippedTypes().isEmpty()) {
failWithMessage("Report contains skipped types: %s", this.actual.skippedTypes());
@@ -708,8 +708,7 @@ class SchemaMappingInspectorTests {
public SchemaInspectionReportAssert hasUnmappedFieldCount(int expected) {
isNotNull();
- Integer actual = this.actual.unmappedFields().values().stream().map(List::size).reduce(0, Integer::sum);
- if (actual != expected) {
+ if (this.actual.unmappedFields().size() != expected) {
failWithMessage("Expected %s unmapped fields, found %s.", expected, this.actual.unmappedFields());
}
return this;
@@ -717,7 +716,7 @@ class SchemaMappingInspectorTests {
public SchemaInspectionReportAssert hasUnmappedDataFetcherCount(int expected) {
isNotNull();
- if (this.actual.unmappedDataFetchers().size() != expected) {
+ if (this.actual.unmappedRegistrations().size() != expected) {
failWithMessage("Expected %s unmapped fields, found %s.", expected, this.actual.unmappedFields());
}
return this;
@@ -734,8 +733,11 @@ class SchemaMappingInspectorTests {
public SchemaInspectionReportAssert containsUnmappedFields(String typeName, String... fieldNames) {
isNotNull();
List expected = Arrays.asList(fieldNames);
- List actual = this.actual.unmappedFields().get(typeName);
- if (actual == null || !actual.containsAll(expected)) {
+ List actual = this.actual.unmappedFields().stream()
+ .filter(coordinates -> coordinates.getTypeName().equals(typeName))
+ .map(FieldCoordinates::getFieldName)
+ .toList();
+ if (!actual.containsAll(expected)) {
failWithMessage("Expected unmapped fields for %s: %s, found %s", typeName, expected, actual);
}
return this;
@@ -744,18 +746,20 @@ class SchemaMappingInspectorTests {
public SchemaInspectionReportAssert containsUnmappedDataFetchersFor(String typeName, String... fieldNames) {
isNotNull();
List expected = Arrays.stream(fieldNames)
- .map(field -> FieldCoordinates.coordinates(typeName, field)).toList();
- if (!this.actual.unmappedDataFetchers().keySet().containsAll(expected)) {
- failWithMessage("Expected unmapped DataFetchers for %s, found %s",
- expected, this.actual.unmappedDataFetchers());
+ .map(field -> FieldCoordinates.coordinates(typeName, field))
+ .toList();
+ if (!this.actual.unmappedRegistrations().keySet().containsAll(expected)) {
+ failWithMessage("Expected unmapped DataFetchers for %s, found %s", expected, this.actual.unmappedRegistrations());
}
return this;
}
- public SchemaInspectionReportAssert containsSkippedTypes(String... typeNames) {
+ public SchemaInspectionReportAssert containsSkippedTypes(String... fieldCoordinates) {
isNotNull();
- List expected = Arrays.asList(typeNames);
- Set actual = this.actual.skippedTypes();
+ List expected = Arrays.asList(fieldCoordinates);
+ List actual = this.actual.skippedTypes().stream()
+ .map(skippedType -> ((GraphQLNamedType) skippedType.type()).getName())
+ .toList();
if (!actual.containsAll(expected)) {
failWithMessage("Expected skipped types: %s, found %s", expected, actual);
}