Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Initial work on Traversal Improvements in GraphqlSchema builds
  • Loading branch information
bbakerman committed Jul 15, 2021
commit 70db347e1d1c10f59e8736c6f7d0cc4c9dd004d3
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
Copy Markdown
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
107 changes: 103 additions & 4 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,7 +48,6 @@
@PublicApi
public class GraphQLSchema {


private final GraphQLObjectType queryType;
private final GraphQLObjectType mutationType;
private final GraphQLObjectType subscriptionType;
Expand All @@ -68,6 +70,67 @@ public class GraphQLSchema {

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) {
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.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.description = builder.description;

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
public GraphQLSchema(GraphQLSchema partiallyBuiltSchema,
GraphQLCodeRegistry codeRegistry, ImmutableMap<String, GraphQLNamedType> typeMap,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it should be one line per arg, right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

done

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.intospectionSchemaField = 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 = typeMap;
this.interfaceNameToObjectTypes = interfaceNameToObjectTypes;
interfaceNameToObjectTypeNames = buildInterfacesToObjectName(interfaceNameToObjectTypes);
}

// THIS WILL BE REMOVED
@Internal
private GraphQLSchema(Builder builder, boolean afterTransform) {
assertNotNull(builder.additionalTypes, () -> "additionalTypes can't be null");
Expand Down Expand Up @@ -99,6 +162,7 @@ private GraphQLSchema(Builder builder, boolean afterTransform) {
// 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 WILL BE REMOVED
@Internal
private GraphQLSchema(GraphQLSchema otherSchema, GraphQLCodeRegistry codeRegistry) {
this.queryType = otherSchema.queryType;
Expand All @@ -120,6 +184,7 @@ private GraphQLSchema(GraphQLSchema otherSchema, GraphQLCodeRegistry codeRegistr
this.description = otherSchema.description;
}


/**
* @return a new schema builder
*/
Expand Down Expand Up @@ -155,7 +220,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 +233,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 Down Expand Up @@ -284,7 +353,7 @@ public Map<String, GraphQLNamedType> getTypeMap() {
}

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

/**
Expand Down Expand Up @@ -690,12 +759,42 @@ GraphQLSchema buildImpl(boolean afterTransform) {
additionalDirectives.add(Directives.SpecifiedByDirective);
}

// 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);

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

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

final GraphQLSchema finalSchema = new GraphQLSchema(partiallyBuiltSchema, codeRegistry, allTypes, interfaceNameToObjectTypes);
schemaUtil.replaceTypeReferences(finalSchema);
if (true) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this doesn't look right

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah as discussed direct - this was to show the new code versus the old.

The PR now has this removed and cleaned up

return validateSchema(finalSchema);
}
//
// This is is the old code here. Its not reachable but here
// to show you it. I will clean it up of course along with constructors
// we dont need
//

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

GraphQLSchema graphQLSchema = new GraphQLSchema(tempSchema, codeRegistry);
schemaUtil.replaceTypeReferences(graphQLSchema);
return validateSchema(graphQLSchema);
}

private GraphQLSchema validateSchema(GraphQLSchema graphQLSchema) {
Collection<SchemaValidationError> errors = new SchemaValidator().validateSchema(graphQLSchema);
if (errors.size() > 0) {
throw new InvalidSchemaException(errors);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/graphql/schema/SchemaTransformer.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
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.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
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
package graphql.schema;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

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

package graphql.schema.impl;

import com.google.common.collect.ImmutableMap;
import graphql.AssertException;
import graphql.Internal;
import graphql.schema.GraphQLEnumType;
import graphql.schema.GraphQLFieldDefinition;
import graphql.schema.GraphQLInputObjectType;
import graphql.schema.GraphQLInterfaceType;
import graphql.schema.GraphQLNamedType;
import graphql.schema.GraphQLObjectType;
import graphql.schema.GraphQLScalarType;
import graphql.schema.GraphQLSchemaElement;
import graphql.schema.GraphQLType;
import graphql.schema.GraphQLTypeReference;
import graphql.schema.GraphQLTypeVisitorStub;
import graphql.schema.GraphQLUnionType;
import graphql.util.TraversalControl;
import graphql.util.TraverserContext;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;

import static java.lang.String.format;

Expand Down Expand Up @@ -109,7 +123,7 @@ private void assertTypeUniqueness(GraphQLNamedType type, Map<String, GraphQLName
}
}

public Map<String, GraphQLNamedType> getResult() {
return result;
public ImmutableMap<String, GraphQLNamedType> getResult() {
return ImmutableMap.copyOf(new TreeMap<>(result));
}
}
Loading