Inline nested SchemaMappingInspection class

This commit is contained in:
rstoyanchev
2023-04-14 12:13:32 +01:00
parent 145ab22bf3
commit ed94ef0bce
3 changed files with 126 additions and 110 deletions

View File

@@ -135,9 +135,9 @@ final class DefaultSchemaResourceGraphQlSourceBuilder
configureGraphQl(builder -> {
GraphQLSchema schema = builder.build().getGraphQLSchema();
SchemaMappingInspector.Report schemaInspectionReport = new SchemaMappingInspector().inspectSchemaMappings(schema, runtimeWiring);
if(!schemaInspectionReport.isEmpty()) {
logger.info(schemaInspectionReport.getSummary());
SchemaMappingInspector.Report report = SchemaMappingInspector.inspect(schema, runtimeWiring);
if (!report.isEmpty()) {
logger.info(report.getSummary());
}
});

View File

@@ -64,124 +64,139 @@ class SchemaMappingInspector {
private static final Log logger = LogFactory.getLog(SchemaMappingInspector.class);
Report inspectSchemaMappings(GraphQLSchema schema, RuntimeWiring runtimeWiring) {
private final GraphQLSchema schema;
private final RuntimeWiring runtimeWiring;
private final Set<String> seenTypes = new HashSet<>();
private SchemaMappingInspector(GraphQLSchema schema, RuntimeWiring runtimeWiring) {
this.schema = schema;
this.runtimeWiring = runtimeWiring;
}
public Report inspectMappings() {
ReportBuilder report = ReportBuilder.create();
SchemaMappingInspection inspection = new SchemaMappingInspection(runtimeWiring);
inspection.inspectOperation(schema.getQueryType(), report);
inspection.inspectOperation(schema.getMutationType(), report);
inspection.inspectOperation(schema.getSubscriptionType(), report);
inspectOperation(schema.getQueryType(), report);
inspectOperation(schema.getMutationType(), report);
inspectOperation(schema.getSubscriptionType(), report);
return report.build();
}
private static class SchemaMappingInspection {
private final RuntimeWiring runtimeWiring;
private final Set<String> seenTypes = new HashSet<>();
SchemaMappingInspection(RuntimeWiring runtimeWiring) {
this.runtimeWiring = runtimeWiring;
}
@SuppressWarnings("rawtypes")
void inspectOperation(@Nullable GraphQLObjectType operationType, ReportBuilder report) {
if (operationType != null) {
Map<String, DataFetcher> operationDataFetchers = this.runtimeWiring.getDataFetcherForType(operationType.getName());
for (GraphQLFieldDefinition fieldDefinition : operationType.getFieldDefinitions()) {
if (operationDataFetchers.containsKey(fieldDefinition.getName())) {
DataFetcher fieldDataFetcher = operationDataFetchers.get(fieldDefinition.getName());
if (fieldDataFetcher instanceof SelfDescribingDataFetcher<?> selfDescribingDataFetcher) {
inspectType(fieldDefinition.getType(), selfDescribingDataFetcher.getReturnType(), report);
}
}
else {
report.missingOperation(operationType, fieldDefinition);
}
}
}
}
private void inspectType(GraphQLType type, ResolvableType declaredType, ReportBuilder report) {
if (type instanceof GraphQLObjectType objectType) {
inspectObjectType(objectType, declaredType, report);
}
else if (type instanceof GraphQLList listType) {
inspectType(listType.getWrappedType(), declaredType.getNested(2), report);
}
else if (type instanceof GraphQLNonNull nonNullType) {
inspectType(nonNullType.getWrappedType(), declaredType, report);
}
else if (type instanceof GraphQLNamedType namedType && logger.isTraceEnabled()){
logger.trace("Cannot inspect type '" + namedType.getName() + "', inspector does not support "
+ type.getClass().getSimpleName());
}
}
@SuppressWarnings("rawtypes")
private void inspectObjectType(GraphQLObjectType objectType, ResolvableType declaredType, ReportBuilder report) {
if (isTypeAlreadyInspected(objectType)) {
return;
}
if (isConnectionType(objectType)) {
objectType = getEdgeNodeType(objectType);
declaredType = declaredType.getNested(2);
}
Map<String, DataFetcher> typeDataFetcher = this.runtimeWiring.getDataFetcherForType(objectType.getName());
Class<?> declaredClass = unwrapPublisherTypes(declaredType);
for (GraphQLFieldDefinition field : objectType.getFieldDefinitions()) {
if (typeDataFetcher.containsKey(field.getName())) {
DataFetcher fieldDataFetcher = typeDataFetcher.get(field.getName());
if (fieldDataFetcher instanceof SelfDescribingDataFetcher<?> typedFieldDataFetcher) {
inspectType(field.getType(), typedFieldDataFetcher.getReturnType(), report);
@SuppressWarnings("rawtypes")
private void inspectOperation(@Nullable GraphQLObjectType operationType, ReportBuilder report) {
if (operationType != null) {
Map<String, DataFetcher> operationDataFetchers = this.runtimeWiring.getDataFetcherForType(operationType.getName());
for (GraphQLFieldDefinition fieldDefinition : operationType.getFieldDefinitions()) {
if (operationDataFetchers.containsKey(fieldDefinition.getName())) {
DataFetcher fieldDataFetcher = operationDataFetchers.get(fieldDefinition.getName());
if (fieldDataFetcher instanceof SelfDescribingDataFetcher<?> selfDescribingDataFetcher) {
inspectType(fieldDefinition.getType(), selfDescribingDataFetcher.getReturnType(), report);
}
}
else {
try {
if (declaredClass == null || BeanUtils.getPropertyDescriptor(declaredClass, field.getName()) == null) {
report.missingField(objectType, field);
}
}
catch (BeansException exc) {
logger.debug("Failed while inspecting " + declaredType + " for property " + field.getName() + "", exc);
}
report.missingOperation(operationType, fieldDefinition);
}
}
}
private boolean isConnectionType(GraphQLObjectType objectType) {
return (objectType.getName().endsWith("Connection") &&
objectType.getField("edges") != null && objectType.getField("pageInfo") != null);
}
@NotNull
private GraphQLObjectType getEdgeNodeType(GraphQLObjectType objectType) {
GraphQLList edgesListType = (GraphQLList) unwrapNonNull(objectType.getField("edges").getType());
GraphQLObjectType edgeType = (GraphQLObjectType) edgesListType.getWrappedType();
return (GraphQLObjectType) unwrapNonNull(edgeType.getField("node").getType());
}
private <T extends GraphQLType> GraphQLType unwrapNonNull(GraphQLType outputType) {
return (outputType instanceof GraphQLNonNull wrapper ? wrapper.getWrappedType() : outputType);
}
@Nullable
private Class<?> unwrapPublisherTypes(ResolvableType declaredType) {
Class<?> rawClass = declaredType.getRawClass();
if (rawClass != null) {
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(declaredType.getRawClass());
if (adapter != null) {
return declaredType.getNested(2).getRawClass();
}
}
return rawClass;
}
private boolean isTypeAlreadyInspected(GraphQLObjectType objectType) {
return !this.seenTypes.add(objectType.getName());
}
}
private void inspectType(GraphQLType type, ResolvableType declaredType, ReportBuilder report) {
if (type instanceof GraphQLObjectType objectType) {
inspectObjectType(objectType, declaredType, report);
}
else if (type instanceof GraphQLList listType) {
inspectType(listType.getWrappedType(), declaredType.getNested(2), report);
}
else if (type instanceof GraphQLNonNull nonNullType) {
inspectType(nonNullType.getWrappedType(), declaredType, report);
}
else if (type instanceof GraphQLNamedType namedType && logger.isTraceEnabled()){
logger.trace("Cannot inspect type '" + namedType.getName() + "', inspector does not support "
+ type.getClass().getSimpleName());
}
}
@SuppressWarnings("rawtypes")
private void inspectObjectType(GraphQLObjectType objectType, ResolvableType declaredType, ReportBuilder report) {
if (isTypeAlreadyInspected(objectType)) {
return;
}
if (isConnectionType(objectType)) {
objectType = getEdgeNodeType(objectType);
declaredType = declaredType.getNested(2);
}
Map<String, DataFetcher> typeDataFetcher = this.runtimeWiring.getDataFetcherForType(objectType.getName());
Class<?> declaredClass = unwrapPublisherTypes(declaredType);
for (GraphQLFieldDefinition field : objectType.getFieldDefinitions()) {
if (typeDataFetcher.containsKey(field.getName())) {
DataFetcher fieldDataFetcher = typeDataFetcher.get(field.getName());
if (fieldDataFetcher instanceof SelfDescribingDataFetcher<?> typedFieldDataFetcher) {
inspectType(field.getType(), typedFieldDataFetcher.getReturnType(), report);
}
}
else {
try {
if (declaredClass == null || BeanUtils.getPropertyDescriptor(declaredClass, field.getName()) == null) {
report.missingField(objectType, field);
}
}
catch (BeansException exc) {
logger.debug("Failed while inspecting " + declaredType + " for property " + field.getName() + "", exc);
}
}
}
}
private boolean isConnectionType(GraphQLObjectType objectType) {
return (objectType.getName().endsWith("Connection") &&
objectType.getField("edges") != null && objectType.getField("pageInfo") != null);
}
@NotNull
private GraphQLObjectType getEdgeNodeType(GraphQLObjectType objectType) {
GraphQLList edgesListType = (GraphQLList) unwrapNonNull(objectType.getField("edges").getType());
GraphQLObjectType edgeType = (GraphQLObjectType) edgesListType.getWrappedType();
return (GraphQLObjectType) unwrapNonNull(edgeType.getField("node").getType());
}
private <T extends GraphQLType> GraphQLType unwrapNonNull(GraphQLType outputType) {
return (outputType instanceof GraphQLNonNull wrapper ? wrapper.getWrappedType() : outputType);
}
@Nullable
private Class<?> unwrapPublisherTypes(ResolvableType declaredType) {
Class<?> rawClass = declaredType.getRawClass();
if (rawClass != null) {
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(declaredType.getRawClass());
if (adapter != null) {
return declaredType.getNested(2).getRawClass();
}
}
return rawClass;
}
private boolean isTypeAlreadyInspected(GraphQLObjectType objectType) {
return !this.seenTypes.add(objectType.getName());
}
/**
* Inspect the schema mappings and produce a report.
* @param schema the schema to inspect
* @param runtimeWiring for {@code DataFetcher} mappings
* @return the resulting report
*/
public static Report inspect(GraphQLSchema schema, RuntimeWiring runtimeWiring) {
return new SchemaMappingInspector(schema, runtimeWiring).inspectMappings();
}
record Report(MultiValueMap<String, String> missingOperations, MultiValueMap<String, String> missingFields) {
String getSummary() {
@@ -190,7 +205,7 @@ class SchemaMappingInspector {
builder.append("no missing mapping.");
}
else {
builder.append(getDetailedReport());
builder.append(getDetailedReport());
}
return builder.toString();
}
@@ -210,6 +225,7 @@ class SchemaMappingInspector {
}
private static class ReportBuilder {
private final MultiValueMap<String, String> missingOperations = new LinkedMultiValueMap<>();

View File

@@ -492,7 +492,7 @@ class SchemaMappingInspectorTests {
.toGraphQlSource()
.schema();
return new SchemaMappingInspector().inspectSchemaMappings(schema, wiringBuilder.build());
return SchemaMappingInspector.inspect(schema, wiringBuilder.build());
}
private RuntimeWiringConfigurer createRuntimeWiring(Class<?>... handlerTypes) {