E5E9 Initial work on Traversal Improvements in GraphqlSchema builds by bbakerman · Pull Request #2463 · graphql-java/graphql-java · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions src/main/java/graphql/schema/CodeRegistryVisitor.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
package graphql.schema;

import graphql.Internal;
import graphql.introspection.Introspection;
import graphql.schema.DataFetcher;
import graphql.schema.FieldCoordinates;
import graphql.schema.GraphQLCodeRegistry;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLFieldsContainer;
import graphql.schema.GraphQLInterfaceType;
import graphql.schema.GraphQLSchemaElement;
import graphql.schema.GraphQLTypeVisitorStub;
import graphql.schema.GraphQLUnionType;
import graphql.schema.TypeResolver;
import graphql.util.TraversalControl;
import graphql.util.TraverserContext;

Expand All @@ -12,11 +23,12 @@
* This ensure that all fields have data fetchers and that unions and interfaces have type resolvers
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this to a schem.impl package just to clean things up. Its kinda of a private thing and not general purpose

*/
@Internal
class CodeRegistryVisitor extends GraphQLTypeVisitorStub {
public class CodeRegistryVisitor extends GraphQLTypeVisitorStub {
private final GraphQLCodeRegistry.Builder codeRegistry;

CodeRegistryVisitor(GraphQLCodeRegistry.Builder codeRegistry) {
public CodeRegistryVisitor(GraphQLCodeRegistry.Builder codeRegistry) {
this.codeRegistry = codeRegistry;
Introspection.addCodeForIntrospectionTypes(codeRegistry);
}

@Override
Expand All @@ -27,7 +39,7 @@ public TraversalControl visitGraphQLFieldDefinition(GraphQLFieldDefinition node,
FieldCoordinates coordinates = coordinates(parentContainerType, node);
codeRegistry.dataFetcherIfAbsent(coordinates, dataFetcher);
}

return CONTINUE;
}

Expand All @@ -38,7 +50,7 @@ public TraversalControl visitGraphQLInterfaceType(GraphQLInterfaceType node, Tra
codeRegistry.typeResolverIfAbsent(node, typeResolver);
}
assertTrue(codeRegistry.getTypeResolver(node) != null,
() -> String.format("You MUST provide a type resolver for the interface type '%s'",node.getName()));
() -> String.format("You MUST provide a type resolver for the interface type '%s'", node.getName()));
return CONTINUE;
}

Expand Down
125 changes: 76 additions & 49 deletions src/main/java/graphql/schema/GraphQLSchema.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
import graphql.DirectivesUtil;
import graphql.Internal;
import graphql.PublicApi;
import graphql.collect.ImmutableKit;
import graphql.introspection.Introspection;
import graphql.language.SchemaDefinition;
import graphql.language.SchemaExtensionDefinition;
import graphql.schema.impl.GraphQLTypeCollectingVisitor;
import graphql.schema.impl.SchemaUtil;
import graphql.schema.validation.InvalidSchemaException;
import graphql.schema.validation.SchemaValidationError;
import graphql.schema.validation.SchemaValidator;
Expand Down Expand Up @@ -45,79 +48,85 @@
@PublicApi
public class GraphQLSchema {


private final GraphQLObjectType queryType;
private final GraphQLObjectType mutationType;
private final GraphQLObjectType subscriptionType;
private final GraphQLObjectType introspectionSchemaType;
private final ImmutableSet<GraphQLType> additionalTypes;
private final GraphQLFieldDefinition intospectionSchemaField;
private final GraphQLFieldDefinition introspectionSchemaField;
private final GraphQLFieldDefinition introspectionTypeField;
// we don't allow modification of "__typename" - its a scalar
private final GraphQLFieldDefinition __typename = Introspection.TypeNameMetaFieldDef;
private final DirectivesUtil.DirectivesHolder directives;
private final DirectivesUtil.DirectivesHolder schemaDirectives;
private final SchemaDefinition definition;
private final ImmutableList<SchemaExtensionDefinition> extensionDefinitions;
private final String description;

private final GraphQLCodeRegistry codeRegistry;

private final ImmutableMap<String, GraphQLNamedType> typeMap;
private final ImmutableMap<String, ImmutableList<GraphQLObjectType>> interfaceNameToObjectTypes;
private final ImmutableMap<String, ImmutableList<String>> interfaceNameToObjectTypeNames;

private final String description;

/*
* This constructs partial GraphQL schema object which has has the schema (query / mutation / subscription) trees
* in it but it does not have the collected types, code registry nor the type references replaced
*
* But it can be traversed to discover all that and filled out later via another constructor.
*
*/
@Internal
private GraphQLSchema(Builder builder, boolean afterTransform) {
private GraphQLSchema(Builder builder) {
assertNotNull(builder.additionalTypes, () -> "additionalTypes can't be null");
assertNotNull(builder.queryType, () -> "queryType can't be null");
assertNotNull(builder.additionalDirectives, () -> "directives can't be null");
assertNotNull(builder.codeRegistry, () -> "codeRegistry can't be null");


this.queryType = builder.queryType;
this.mutationType = builder.mutationType;
this.subscriptionType = builder.subscriptionType;
this.additionalTypes = ImmutableSet.copyOf(builder.additionalTypes);
this.introspectionSchemaType = builder.introspectionSchemaType;
this.intospectionSchemaField = Introspection.buildSchemaField(builder.introspectionSchemaType);
this.introspectionSchemaField = Introspection.buildSchemaField(builder.introspectionSchemaType);
this.introspectionTypeField = Introspection.buildTypeField(builder.introspectionSchemaType);
this.directives = new DirectivesUtil.DirectivesHolder(builder.additionalDirectives);
this.schemaDirectives = new DirectivesUtil.DirectivesHolder(builder.schemaDirectives);
this.definition = builder.definition;
this.extensionDefinitions = nonNullCopyOf(builder.extensionDefinitions);
this.codeRegistry = builder.codeRegistry;
// sorted by type name
SchemaUtil schemaUtil = new SchemaUtil();
this.typeMap = ImmutableMap.copyOf(schemaUtil.allTypes(this, additionalTypes, afterTransform));
this.interfaceNameToObjectTypes = buildInterfacesToObjectTypes(schemaUtil.groupImplementations(this));
this.interfaceNameToObjectTypeNames = buildInterfacesToObjectName(interfaceNameToObjectTypes);
this.description = builder.description;
}

// This can be removed once we no longer extract legacy code from types such as data fetchers but for now
// we need it to make an efficient copy that does not walk the types twice
this.codeRegistry = null;
this.typeMap = ImmutableKit.emptyMap();
this.interfaceNameToObjectTypes = ImmutableKit.emptyMap();
this.interfaceNameToObjectTypeNames = ImmutableKit.emptyMap();
}

/*
* This constructs a full fledged graphql schema object that has not yet had its type references replaced
* but its otherwise complete
*/
@Internal
private GraphQLSchema(GraphQLSchema otherSchema, GraphQLCodeRegistry codeRegistry) {
this.queryType = otherSchema.queryType;
this.mutationType = otherSchema.mutationType;
this.subscriptionType = otherSchema.subscriptionType;
this.introspectionSchemaType = otherSchema.introspectionSchemaType;
this.additionalTypes = otherSchema.additionalTypes;
this.intospectionSchemaField = otherSchema.intospectionSchemaField;
this.introspectionTypeField = otherSchema.introspectionTypeField;
this.directives = otherSchema.directives;
this.schemaDirectives = otherSchema.schemaDirectives;
this.definition = otherSchema.definition;
this.extensionDefinitions = nonNullCopyOf(otherSchema.extensionDefinitions);
public GraphQLSchema(GraphQLSchema partiallyBuiltSchema,
GraphQLCodeRegistry codeRegistry,
ImmutableMap<String, GraphQLNamedType> typeMap,
ImmutableMap<String, ImmutableList<GraphQLObjectType>> interfaceNameToObjectTypes) {
this.queryType = partiallyBuiltSchema.queryType;
this.mutationType = partiallyBuiltSchema.mutationType;
this.subscriptionType = partiallyBuiltSchema.subscriptionType;
this.additionalTypes = ImmutableSet.copyOf(partiallyBuiltSchema.additionalTypes);
this.introspectionSchemaType = partiallyBuiltSchema.introspectionSchemaType;
this.introspectionSchemaField = Introspection.buildSchemaField(partiallyBuiltSchema.introspectionSchemaType);
this.introspectionTypeField = Introspection.buildTypeField(partiallyBuiltSchema.introspectionSchemaType);
this.directives = partiallyBuiltSchema.directives;
this.schemaDirectives = partiallyBuiltSchema.schemaDirectives;
this.definition = partiallyBuiltSchema.definition;
this.extensionDefinitions = partiallyBuiltSchema.extensionDefinitions;
this.description = partiallyBuiltSchema.description;
this.codeRegistry = codeRegistry;

this.typeMap = otherSchema.typeMap;
this.interfaceNameToObjectTypes = otherSchema.interfaceNameToObjectTypes;
this.interfaceNameToObjectTypeNames = otherSchema.interfaceNameToObjectTypeNames;
this.description = otherSchema.description;
this.typeMap = typeMap;
this.interfaceNameToObjectTypes = interfaceNameToObjectTypes;
interfaceNameToObjectTypeNames = buildInterfacesToObjectName(interfaceNameToObjectTypes);
}

/**
Expand Down Expand Up @@ -155,7 +164,11 @@ private static GraphQLDirective[] schemaDirectivesArray(GraphQLSchema existingSc
return existingSchema.schemaDirectives.getDirectives().toArray(new GraphQLDirective[0]);
}

private ImmutableMap<String, ImmutableList<GraphQLObjectType>> buildInterfacesToObjectTypes(Map<String, List<GraphQLObjectType>> groupImplementations) {
private static List<GraphQLNamedType> getAllTypesAsList(ImmutableMap<String, GraphQLNamedType> typeMap) {
return sortTypes(byNameAsc(), typeMap.values());
}

private static ImmutableMap<String, ImmutableList<GraphQLObjectType>> buildInterfacesToObjectTypes(Map<String, List<GraphQLObjectType>> groupImplementations) {
ImmutableMap.Builder<String, ImmutableList<GraphQLObjectType>> map = ImmutableMap.builder();
for (Map.Entry<String, List<GraphQLObjectType>> e : groupImplementations.entrySet()) {
ImmutableList<GraphQLObjectType> sortedObjectTypes = ImmutableList.copyOf(sortTypes(byNameAsc(), e.getValue()));
Expand All @@ -164,7 +177,7 @@ private ImmutableMap<String, ImmutableList<GraphQLObjectType>> buildInterfacesTo
return map.build();
}

private ImmutableMap<String, ImmutableList<String>> buildInterfacesToObjectName(ImmutableMap<String, ImmutableList<GraphQLObjectType>> byInterface) {
private static ImmutableMap<String, ImmutableList<String>> buildInterfacesToObjectName(ImmutableMap<String, ImmutableList<GraphQLObjectType>> byInterface) {
ImmutableMap.Builder<String, ImmutableList<String>> map = ImmutableMap.builder();
for (Map.Entry<String, ImmutableList<GraphQLObjectType>> e : byInterface.entrySet()) {
ImmutableList<String> objectTypeNames = map(e.getValue(), GraphQLObjectType::getName);
Expand All @@ -181,7 +194,7 @@ public GraphQLCodeRegistry getCodeRegistry() {
* @return the special system field called "__schema"
*/
public GraphQLFieldDefinition getIntrospectionSchemaFieldDefinition() {
return intospectionSchemaField;
return introspectionSchemaField;
}

/**
Expand Down Expand Up @@ -284,7 +297,7 @@ public Map<String, GraphQLNamedType> getTypeMap() {
}

public List<GraphQLNamedType> getAllTypesAsList() {
return sortTypes(byNameAsc(), typeMap.values());
return getAllTypesAsList(typeMap);
}

/**
Expand Down Expand Up @@ -495,18 +508,16 @@ public static class Builder {
private GraphQLObjectType introspectionSchemaType = Introspection.__Schema;
private GraphQLObjectType subscriptionType;
private GraphQLCodeRegistry codeRegistry = GraphQLCodeRegistry.newCodeRegistry().build();
private Set<GraphQLType> additionalTypes = new LinkedHashSet<>();
private SchemaDefinition definition;
private List<SchemaExtensionDefinition> extensionDefinitions;
private String description;

// we default these in
private Set<GraphQLDirective> additionalDirectives = new LinkedHashSet<>(
private final Set<GraphQLDirective> additionalDirectives = new LinkedHashSet<>(
asList(Directives.IncludeDirective, Directives.SkipDirective)
);
private List<GraphQLDirective> schemaDirectives = new ArrayList<>();

private SchemaUtil schemaUtil = new SchemaUtil();
private final Set<GraphQLType> additionalTypes = new LinkedHashSet<>();
private final List<GraphQLDirective> schemaDirectives = new ArrayList<>();

public Builder query(GraphQLObjectType.Builder builder) {
return query(builder.build());
Expand Down Expand Up @@ -673,10 +684,10 @@ public GraphQLSchema build(Set<GraphQLType> additionalTypes, Set& 42E6 lt;GraphQLDirectiv
* @return the built schema
*/
public GraphQLSchema build() {
return buildImpl(false);
return buildImpl();
}

GraphQLSchema buildImpl(boolean afterTransform) {
private GraphQLSchema buildImpl() {
assertNotNull(additionalTypes, () -> "additionalTypes can't be null");
assertNotNull(additionalDirectives, () -> "additionalDirectives can't be null");

Expand All @@ -690,12 +701,28 @@ GraphQLSchema buildImpl(boolean afterTransform) {
additionalDirectives.add(Directives.SpecifiedByDirective);
}

// grab the legacy code things from types
final GraphQLSchema tempSchema = new GraphQLSchema(this, afterTransform);
codeRegistry = codeRegistry.transform(codeRegistryBuilder -> schemaUtil.extractCodeFromTypes(codeRegistryBuilder, tempSchema));
// quick build - no traversing
final GraphQLSchema partiallyBuiltSchema = new GraphQLSchema(this);

GraphQLCodeRegistry.Builder extractedDataFetchers = GraphQLCodeRegistry.newCodeRegistry(codeRegistry);
CodeRegistryVisitor codeRegistryVisitor = new CodeRegistryVisitor(extractedDataFetchers);
GraphQLTypeCollectingVisitor typeCollectingVisitor = new GraphQLTypeCollectingVisitor();
SchemaUtil.visitPartiallySchema(partiallyBuiltSchema, codeRegistryVisitor, typeCollectingVisitor);

codeRegistry = extractedDataFetchers.build();
ImmutableMap<String, GraphQLNamedType> allTypes = typeCollectingVisitor.getResult();
List<GraphQLNamedType> allTypesAsList = getAllTypesAsList(allTypes);

ImmutableMap<String, List<GraphQLObjectType>> groupedImplementations = SchemaUtil.groupInterfaceImplementationsByName(allTypesAsList);
ImmutableMap<String, ImmutableList<GraphQLObjectType>> interfaceNameToObjectTypes = buildInterfacesToObjectTypes(groupedImplementations);

// this is now build however its contained types are still to be mutated by type reference replacement
final GraphQLSchema finalSchema = new GraphQLSchema(partiallyBuiltSchema, codeRegistry, allTypes, interfaceNameToObjectTypes);
SchemaUtil.replaceTypeReferences(finalSchema);
return validateSchema(finalSchema);
}

GraphQLSchema graphQLSchema = new GraphQLSchema(tempSchema, codeRegistry);
schemaUtil.replaceTypeReferences(graphQLSchema);
private GraphQLSchema validateSchema(GraphQLSchema graphQLSchema) {
Collection<SchemaValidationError> errors = new SchemaValidator().validateSchema(graphQLSchema);
if (errors.size() > 0) {
throw new InvalidSchemaException(errors);
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/graphql/schema/SchemaTransformer.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
import static graphql.Assert.assertShouldNeverHappen;
import static graphql.schema.GraphQLSchemaElementAdapter.SCHEMA_ELEMENT_ADAPTER;
import static graphql.schema.SchemaElementChildrenContainer.newSchemaElementChildrenContainer;
import static graphql.schema.StronglyConnectedComponentsTopologicallySorted.getStronglyConnectedComponentsTopologicallySorted;
import static graphql.util.NodeZipper.ModificationType.DELETE;
import static graphql.schema.impl.StronglyConnectedComponentsTopologicallySorted.getStronglyConnectedComponentsTopologicallySorted;
import static graphql.util.NodeZipper.ModificationType.REPLACE;
import static graphql.util.TraversalControl.CONTINUE;
import static java.lang.String.format;
Expand Down Expand Up @@ -544,7 +544,7 @@ public GraphQLSchema rebuildSchema(GraphQLCodeRegistry.Builder codeRegistry) {
.withSchemaDirectives(this.schemaDirectives)
.codeRegistry(codeRegistry.build())
.description(schema.getDescription())
.buildImpl(true);
.build();
}
}
}
Loading
0