Skip to content

Commit 7dd4ac0

Browse files
committed
Make SchemaMappingReport public
See gh-672
1 parent 1a5dacc commit 7dd4ac0

File tree

4 files changed

+122
-122
lines changed

4 files changed

+122
-122
lines changed

Diff for: spring-graphql/src/main/java/org/springframework/graphql/execution/DefaultSchemaResourceGraphQlSourceBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ protected GraphQLSchema initGraphQlSchema() {
138138

139139
configureGraphQl(builder -> {
140140
GraphQLSchema schema = builder.build().getGraphQLSchema();
141-
SchemaMappingInspector.Report report = SchemaMappingInspector.inspect(schema, runtimeWiring);
141+
SchemaMappingReport report = SchemaMappingInspector.inspect(schema, runtimeWiring);
142142
logger.info(report);
143143
});
144144

Diff for: spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java

+43-93
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
* Declares an {@link #inspect(GraphQLSchema, RuntimeWiring)} method that checks
5555
* if schema fields are covered either by a {@link DataFetcher} registration,
5656
* or match a Java object property. Fields that have neither are reported as
57-
* "unmapped" in the resulting {@link Report Resport}. The inspection also
57+
* "unmapped" in the resulting {@link SchemaMappingReport}. The inspection also
5858
* performs a reverse check for {@code DataFetcher} registrations against schema
5959
* fields that don't exist.
6060
*
@@ -82,7 +82,7 @@
8282
* @author Rossen Stoyanchev
8383
* @since 1.2.0
8484
*/
85-
class SchemaMappingInspector {
85+
final class SchemaMappingInspector {
8686

8787
private static final Log logger = LogFactory.getLog(SchemaMappingInspector.class);
8888

@@ -91,12 +91,19 @@ class SchemaMappingInspector {
9191

9292
private final RuntimeWiring runtimeWiring;
9393

94-
private final ReportBuilder reportBuilder = new ReportBuilder();
95-
9694
private final Set<String> inspectedTypes = new HashSet<>();
9795

9896
private final ReactiveAdapterRegistry reactiveAdapterRegistry = ReactiveAdapterRegistry.getSharedInstance();
9997

98+
private final MultiValueMap<String, String> unmappedFields = new LinkedMultiValueMap<>();
99+
100+
private final Map<FieldCoordinates, DataFetcher<?>> unmappedDataFetchers = new LinkedHashMap<>();
101+
102+
private final Set<String> skippedTypes = new LinkedHashSet<>();
103+
104+
@Nullable
105+
private SchemaMappingReport report;
106+
100107

101108
private SchemaMappingInspector(GraphQLSchema schema, RuntimeWiring runtimeWiring) {
102109
Assert.notNull(schema, "GraphQLSchema is required");
@@ -107,34 +114,41 @@ private SchemaMappingInspector(GraphQLSchema schema, RuntimeWiring runtimeWiring
107114

108115

109116
/**
110-
* Inspect all fields, starting from Query, Mutation, and Subscription, and
111-
* working recursively down through the types they return.
112-
* @return a report with unmapped fields and skipped types.
117+
* Perform an inspection and create a {@link SchemaMappingReport}.
118+
* The inspection is one once only, during the first call to this method.
113119
*/
114-
public Report inspect() {
120+
public SchemaMappingReport getOrCreateReport() {
121+
if (this.report == null) {
122+
checkSchema();
123+
checkDataFetcherRegistrations();
124+
this.report = new SchemaMappingReport(
125+
this.unmappedFields, this.unmappedDataFetchers, this.skippedTypes);
126+
}
127+
return this.report;
128+
}
129+
130+
private void checkSchema() {
131+
132+
checkFieldsContainer(this.schema.getQueryType(), null);
115133

116-
inspectFields(this.schema.getQueryType(), null);
117134
if (this.schema.isSupportingMutations()) {
118-
inspectFields(this.schema.getMutationType(), null);
135+
checkFieldsContainer(this.schema.getMutationType(), null);
119136
}
137+
120138
if (this.schema.isSupportingSubscriptions()) {
121-
inspectFields(this.schema.getSubscriptionType(), null);
139+
checkFieldsContainer(this.schema.getSubscriptionType(), null);
122140
}
123-
124-
inspectDataFetcherRegistrations();
125-
126-
return this.reportBuilder.build();
127141
}
128142

129143
/**
130-
* Inspect the given {@code GraphQLFieldsContainer} check against {@code DataFetcher}
131-
* registrations, or Java properties in the given {@code ResolvableType}.
132-
* @param fields the GraphQL schema type to inspect
144+
* Check the given {@code GraphQLFieldsContainer} against {@code DataFetcher}
145+
* registrations, or Java properties of the given {@code ResolvableType}.
146+
* @param fields the GraphQL interface or object type to check
133147
* @param resolvableType the Java type to match against, or {@code null} if
134148
* not applicable such as for Query, Mutation, or Subscription
135149
*/
136150
@SuppressWarnings("rawtypes")
137-
private void inspectFields(GraphQLFieldsContainer fields, @Nullable ResolvableType resolvableType) {
151+
private void checkFieldsContainer(GraphQLFieldsContainer fields, @Nullable ResolvableType resolvableType) {
138152

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

@@ -143,7 +157,7 @@ private void inspectFields(GraphQLFieldsContainer fields, @Nullable ResolvableTy
143157
if (dataFetcherMap.containsKey(fieldName)) {
144158
DataFetcher<?> fetcher = dataFetcherMap.get(fieldName);
145159
if (fetcher instanceof SelfDescribingDataFetcher<?> selfDescribingDataFetcher) {
146-
inspectFieldType(
160+
checkFieldType(
147161
field.getType(), selfDescribingDataFetcher.getReturnType(),
148162
(fields == this.schema.getSubscriptionType()));
149163
}
@@ -153,18 +167,18 @@ else if (isNotScalarOrEnumType(field.getType())) {
153167
}
154168
}
155169
else if (resolvableType == null || !hasProperty(resolvableType, fieldName)) {
156-
this.reportBuilder.addUnmappedField(fields.getName(), fieldName);
170+
this.unmappedFields.add(fields.getName(), fieldName);
157171
}
158172
}
159173
}
160174

161175
/**
162-
* Inspect the output {@link GraphQLType} of a field.
176+
* Check the output {@link GraphQLType} of a field against the given DataFetcher return type.
163177
* @param outputType the field type to inspect
164178
* @param resolvableType the expected Java return type
165179
* @param isSubscriptionField whether this is for a subscription field
166180
*/
167-
private void inspectFieldType(GraphQLType outputType, ResolvableType resolvableType, boolean isSubscriptionField) {
181+
private void checkFieldType(GraphQLType outputType, ResolvableType resolvableType, boolean isSubscriptionField) {
168182

169183
// Remove GraphQL type wrappers, and nest within Java generic types
170184
outputType = unwrapIfNonNull(outputType);
@@ -201,7 +215,7 @@ else if (outputType instanceof GraphQLList listType) {
201215
}
202216

203217
// Nest within the
204-
inspectFields(fieldContainer, resolvableType);
218+
checkFieldsContainer(fieldContainer, resolvableType);
205219
}
206220

207221
private GraphQLType unwrapIfNonNull(GraphQLType type) {
@@ -283,19 +297,19 @@ private boolean hasProperty(ResolvableType resolvableType, String fieldName) {
283297

284298
private void addSkippedType(GraphQLType type, Supplier<String> reason) {
285299
String typeName = typeNameToString(type);
286-
this.reportBuilder.addSkippedType(typeName);
300+
this.skippedTypes.add(typeName);
287301
if (logger.isDebugEnabled()) {
288302
logger.debug("Skipped '" + typeName + "': " + reason.get());
289303
}
290304
}
291305

292306
@SuppressWarnings("rawtypes")
293-
private void inspectDataFetcherRegistrations() {
307+
private void checkDataFetcherRegistrations() {
294308
this.runtimeWiring.getDataFetchers().forEach((typeName, registrations) ->
295309
registrations.forEach((fieldName, fetcher) -> {
296310
FieldCoordinates coordinates = FieldCoordinates.coordinates(typeName, fieldName);
297311
if (this.schema.getFieldDefinition(coordinates) == null) {
298-
this.reportBuilder.addUnmappedDataFetcher(coordinates, fetcher);
312+
this.unmappedDataFetchers.put(coordinates, fetcher);
299313
}
300314
}));
301315
}
@@ -307,73 +321,9 @@ private void inspectDataFetcherRegistrations() {
307321
* @param runtimeWiring for {@code DataFetcher} registrations
308322
* @return the created report
309323
*/
310-
public static Report inspect(GraphQLSchema schema, RuntimeWiring runtimeWiring) {
311-
SchemaMappingInspector inspector = new SchemaMappingInspector(schema, runtimeWiring);
312-
return inspector.inspect();
324+
public static SchemaMappingReport inspect(GraphQLSchema schema, RuntimeWiring runtimeWiring) {
325+
return new SchemaMappingInspector(schema, runtimeWiring).getOrCreateReport();
313326
}
314327

315328

316-
317-
/**
318-
* The report produced as a result of schema mappings inspection.
319-
* @param unmappedFields map with type names as keys, and unmapped field names as values
320-
* @param unmappedDataFetchers map with unmapped {@code DataFetcher}s and their field coordinates
321-
* @param skippedTypes the names of types skipped by the inspection
322-
*/
323-
public record Report(
324-
MultiValueMap<String, String> unmappedFields,
325-
Map<FieldCoordinates, DataFetcher<?>> unmappedDataFetchers,
326-
Set<String> skippedTypes) {
327-
328-
@Override
329-
public String toString() {
330-
return "GraphQL schema inspection:\n" +
331-
"\tUnmapped fields: " + this.unmappedFields + "\n" +
332-
"\tUnmapped DataFetcher registrations: " + this.unmappedDataFetchers + "\n" +
333-
"\tSkipped types: " + this.skippedTypes;
334-
}
335-
}
336-
337-
338-
/**
339-
* Builder for a {@link Report}.
340-
*/
341-
private static class ReportBuilder {
342-
343-
private final MultiValueMap<String, String> unmappedFields = new LinkedMultiValueMap<>();
344-
345-
private final Map<FieldCoordinates, DataFetcher<?>> unmappedDataFetchers = new LinkedHashMap<>();
346-
347-
private final Set<String> skippedTypes = new LinkedHashSet<>();
348-
349-
/**
350-
* Add an unmapped field.
351-
*/
352-
public void addUnmappedField(String typeName, String fieldName) {
353-
this.unmappedFields.add(typeName, fieldName);
354-
}
355-
356-
/**
357-
* Add an unmapped {@code DataFetcher} registration.
358-
*/
359-
public void addUnmappedDataFetcher(FieldCoordinates coordinates, DataFetcher<?> dataFetcher) {
360-
this.unmappedDataFetchers.put(coordinates, dataFetcher);
361-
}
362-
363-
/**
364-
* Add a skipped type name.
365-
*/
366-
public void addSkippedType(String typeName) {
367-
this.skippedTypes.add(typeName);
368-
}
369-
370-
public Report build() {
371-
return new Report(
372-
new LinkedMultiValueMap<>(this.unmappedFields),
373-
new LinkedHashMap<>(this.unmappedDataFetchers),
374-
new LinkedHashSet<>(this.skippedTypes));
375-
}
376-
377-
}
378-
379329
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2020-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.graphql.execution;
18+
19+
20+
import java.util.Map;
21+
import java.util.Set;
22+
23+
import graphql.schema.DataFetcher;
24+
import graphql.schema.FieldCoordinates;
25+
26+
import org.springframework.util.MultiValueMap;
27+
28+
29+
/**
30+
* The report produced as a result of schema mappings inspection.
31+
* @param unmappedFields map with type names as keys, and unmapped field names as values
32+
* @param unmappedDataFetchers map with unmapped {@code DataFetcher}s and their field coordinates
33+
* @param skippedTypes the names of types skipped by the inspection
34+
*
35+
* @since 1.2.0
36+
*/
37+
public record SchemaMappingReport(
38+
MultiValueMap<String, String> unmappedFields,
39+
Map<FieldCoordinates, DataFetcher<?>> unmappedDataFetchers,
40+
Set<String> skippedTypes) {
41+
42+
@Override
43+
public String toString() {
44+
return "GraphQL schema inspection:\n" +
45+
"\tUnmapped fields: " + this.unmappedFields + "\n" +
46+
"\tUnmapped DataFetcher registrations: " + this.unmappedDataFetchers + "\n" +
47+
"\tSkipped types: " + this.skippedTypes;
48+
}
49+
50+
}

0 commit comments

Comments
 (0)