88import graphql .DirectivesUtil ;
99import graphql .Internal ;
1010import graphql .PublicApi ;
11+ import graphql .collect .ImmutableKit ;
1112import graphql .introspection .Introspection ;
1213import graphql .language .SchemaDefinition ;
1314import graphql .language .SchemaExtensionDefinition ;
15+ import graphql .schema .impl .GraphQLTypeCollectingVisitor ;
16+ import graphql .schema .impl .SchemaUtil ;
1417import graphql .schema .validation .InvalidSchemaException ;
1518import graphql .schema .validation .SchemaValidationError ;
1619import graphql .schema .validation .SchemaValidator ;
4548@ PublicApi
4649public class GraphQLSchema {
4750
48-
4951 private final GraphQLObjectType queryType ;
5052 private final GraphQLObjectType mutationType ;
5153 private final GraphQLObjectType subscriptionType ;
5254 private final GraphQLObjectType introspectionSchemaType ;
5355 private final ImmutableSet <GraphQLType > additionalTypes ;
54- private final GraphQLFieldDefinition intospectionSchemaField ;
56+ private final GraphQLFieldDefinition introspectionSchemaField ;
5557 private final GraphQLFieldDefinition introspectionTypeField ;
5658 // we don't allow modification of "__typename" - its a scalar
5759 private final GraphQLFieldDefinition __typename = Introspection .TypeNameMetaFieldDef ;
5860 private final DirectivesUtil .DirectivesHolder directives ;
5961 private final DirectivesUtil .DirectivesHolder schemaDirectives ;
6062 private final SchemaDefinition definition ;
6163 private final ImmutableList <SchemaExtensionDefinition > extensionDefinitions ;
64+ private final String description ;
6265
6366 private final GraphQLCodeRegistry codeRegistry ;
6467
6568 private final ImmutableMap <String , GraphQLNamedType > typeMap ;
6669 private final ImmutableMap <String , ImmutableList <GraphQLObjectType >> interfaceNameToObjectTypes ;
6770 private final ImmutableMap <String , ImmutableList <String >> interfaceNameToObjectTypeNames ;
6871
69- private final String description ;
70-
72+ /*
73+ * This constructs partial GraphQL schema object which has has the schema (query / mutation / subscription) trees
74+ * in it but it does not have the collected types, code registry nor the type references replaced
75+ *
76+ * But it can be traversed to discover all that and filled out later via another constructor.
77+ *
78+ */
7179 @ Internal
72- private GraphQLSchema (Builder builder , boolean afterTransform ) {
80+ private GraphQLSchema (Builder builder ) {
7381 assertNotNull (builder .additionalTypes , () -> "additionalTypes can't be null" );
7482 assertNotNull (builder .queryType , () -> "queryType can't be null" );
7583 assertNotNull (builder .additionalDirectives , () -> "directives can't be null" );
7684 assertNotNull (builder .codeRegistry , () -> "codeRegistry can't be null" );
7785
78-
7986 this .queryType = builder .queryType ;
8087 this .mutationType = builder .mutationType ;
8188 this .subscriptionType = builder .subscriptionType ;
8289 this .additionalTypes = ImmutableSet .copyOf (builder .additionalTypes );
8390 this .introspectionSchemaType = builder .introspectionSchemaType ;
84- this .intospectionSchemaField = Introspection .buildSchemaField (builder .introspectionSchemaType );
91+ this .introspectionSchemaField = Introspection .buildSchemaField (builder .introspectionSchemaType );
8592 this .introspectionTypeField = Introspection .buildTypeField (builder .introspectionSchemaType );
8693 this .directives = new DirectivesUtil .DirectivesHolder (builder .additionalDirectives );
8794 this .schemaDirectives = new DirectivesUtil .DirectivesHolder (builder .schemaDirectives );
8895 this .definition = builder .definition ;
8996 this .extensionDefinitions = nonNullCopyOf (builder .extensionDefinitions );
90- this .codeRegistry = builder .codeRegistry ;
91- // sorted by type name
92- SchemaUtil schemaUtil = new SchemaUtil ();
93- this .typeMap = ImmutableMap .copyOf (schemaUtil .allTypes (this , additionalTypes , afterTransform ));
94- this .interfaceNameToObjectTypes = buildInterfacesToObjectTypes (schemaUtil .groupImplementations (this ));
95- this .interfaceNameToObjectTypeNames = buildInterfacesToObjectName (interfaceNameToObjectTypes );
9697 this .description = builder .description ;
97- }
9898
99- // This can be removed once we no longer extract legacy code from types such as data fetchers but for now
100- // we need it to make an efficient copy that does not walk the types twice
99+ this .codeRegistry = null ;
100+ this .typeMap = ImmutableKit .emptyMap ();
101+ this .interfaceNameToObjectTypes = ImmutableKit .emptyMap ();
102+ this .interfaceNameToObjectTypeNames = ImmutableKit .emptyMap ();
103+ }
101104
105+ /*
106+ * This constructs a full fledged graphql schema object that has not yet had its type references replaced
107+ * but its otherwise complete
108+ */
102109 @ Internal
103- private GraphQLSchema (GraphQLSchema otherSchema , GraphQLCodeRegistry codeRegistry ) {
104- this .queryType = otherSchema .queryType ;
105- this .mutationType = otherSchema .mutationType ;
106- this .subscriptionType = otherSchema .subscriptionType ;
107- this .introspectionSchemaType = otherSchema .introspectionSchemaType ;
108- this .additionalTypes = otherSchema .additionalTypes ;
109- this .intospectionSchemaField = otherSchema .intospectionSchemaField ;
110- this .introspectionTypeField = otherSchema .introspectionTypeField ;
111- this .directives = otherSchema .directives ;
112- this .schemaDirectives = otherSchema .schemaDirectives ;
113- this .definition = otherSchema .definition ;
114- this .extensionDefinitions = nonNullCopyOf (otherSchema .extensionDefinitions );
110+ public GraphQLSchema (GraphQLSchema partiallyBuiltSchema ,
111+ GraphQLCodeRegistry codeRegistry ,
112+ ImmutableMap <String , GraphQLNamedType > typeMap ,
113+ ImmutableMap <String , ImmutableList <GraphQLObjectType >> interfaceNameToObjectTypes ) {
114+ this .queryType = partiallyBuiltSchema .queryType ;
115+ this .mutationType = partiallyBuiltSchema .mutationType ;
116+ this .subscriptionType = partiallyBuiltSchema .subscriptionType ;
117+ this .additionalTypes = ImmutableSet .copyOf (partiallyBuiltSchema .additionalTypes );
118+ this .introspectionSchemaType = partiallyBuiltSchema .introspectionSchemaType ;
119+ this .introspectionSchemaField = Introspection .buildSchemaField (partiallyBuiltSchema .introspectionSchemaType );
120+ this .introspectionTypeField = Introspection .buildTypeField (partiallyBuiltSchema .introspectionSchemaType );
121+ this .directives = partiallyBuiltSchema .directives ;
122+ this .schemaDirectives = partiallyBuiltSchema .schemaDirectives ;
123+ this .definition = partiallyBuiltSchema .definition ;
124+ this .extensionDefinitions = partiallyBuiltSchema .extensionDefinitions ;
125+ this .description = partiallyBuiltSchema .description ;
115126 this .codeRegistry = codeRegistry ;
116-
117- this .typeMap = otherSchema .typeMap ;
118- this .interfaceNameToObjectTypes = otherSchema .interfaceNameToObjectTypes ;
119- this .interfaceNameToObjectTypeNames = otherSchema .interfaceNameToObjectTypeNames ;
120- this .description = otherSchema .description ;
127+ this .typeMap = typeMap ;
128+ this .interfaceNameToObjectTypes = interfaceNameToObjectTypes ;
129+ interfaceNameToObjectTypeNames = buildInterfacesToObjectName (interfaceNameToObjectTypes );
121130 }
122131
123132 /**
@@ -155,7 +164,11 @@ private static GraphQLDirective[] schemaDirectivesArray(GraphQLSchema existingSc
155164 return existingSchema .schemaDirectives .getDirectives ().toArray (new GraphQLDirective [0 ]);
156165 }
157166
158- private ImmutableMap <String , ImmutableList <GraphQLObjectType >> buildInterfacesToObjectTypes (Map <String , List <GraphQLObjectType >> groupImplementations ) {
167+ private static List <GraphQLNamedType > getAllTypesAsList (ImmutableMap <String , GraphQLNamedType > typeMap ) {
168+ return sortTypes (byNameAsc (), typeMap .values ());
169+ }
170+
171+ private static ImmutableMap <String , ImmutableList <GraphQLObjectType >> buildInterfacesToObjectTypes (Map <String , List <GraphQLObjectType >> groupImplementations ) {
159172 ImmutableMap .Builder <String , ImmutableList <GraphQLObjectType >> map = ImmutableMap .builder ();
160173 for (Map .Entry <String , List <GraphQLObjectType >> e : groupImplementations .entrySet ()) {
161174 ImmutableList <GraphQLObjectType > sortedObjectTypes = ImmutableList .copyOf (sortTypes (byNameAsc (), e .getValue ()));
@@ -164,7 +177,7 @@ private ImmutableMap<String, ImmutableList<GraphQLObjectType>> buildInterfacesTo
164177 return map .build ();
165178 }
166179
167- private ImmutableMap <String , ImmutableList <String >> buildInterfacesToObjectName (ImmutableMap <String , ImmutableList <GraphQLObjectType >> byInterface ) {
180+ private static ImmutableMap <String , ImmutableList <String >> buildInterfacesToObjectName (ImmutableMap <String , ImmutableList <GraphQLObjectType >> byInterface ) {
168181 ImmutableMap .Builder <String , ImmutableList <String >> map = ImmutableMap .builder ();
169182 for (Map .Entry <String , ImmutableList <GraphQLObjectType >> e : byInterface .entrySet ()) {
170183 ImmutableList <String > objectTypeNames = map (e .getValue (), GraphQLObjectType ::getName );
@@ -181,7 +194,7 @@ public GraphQLCodeRegistry getCodeRegistry() {
181194 * @return the special system field called "__schema"
182195 */
183196 public GraphQLFieldDefinition getIntrospectionSchemaFieldDefinition () {
184- return intospectionSchemaField ;
197+ return introspectionSchemaField ;
185198 }
186199
187200 /**
@@ -284,7 +297,7 @@ public Map<String, GraphQLNamedType> getTypeMap() {
284297 }
285298
286299 public List <GraphQLNamedType > getAllTypesAsList () {
287- return sortTypes ( byNameAsc (), typeMap . values () );
300+ return getAllTypesAsList ( typeMap );
288301 }
289302
290303 /**
@@ -495,18 +508,16 @@ public static class Builder {
495508 private GraphQLObjectType introspectionSchemaType = Introspection .__Schema ;
496509 private GraphQLObjectType subscriptionType ;
497510 private GraphQLCodeRegistry codeRegistry = GraphQLCodeRegistry .newCodeRegistry ().build ();
498- private Set <GraphQLType > additionalTypes = new LinkedHashSet <>();
499511 private SchemaDefinition definition ;
500512 private List <SchemaExtensionDefinition > extensionDefinitions ;
501513 private String description ;
502514
503515 // we default these in
504- private Set <GraphQLDirective > additionalDirectives = new LinkedHashSet <>(
516+ private final Set <GraphQLDirective > additionalDirectives = new LinkedHashSet <>(
505517 asList (Directives .IncludeDirective , Directives .SkipDirective )
506518 );
507- private List <GraphQLDirective > schemaDirectives = new ArrayList <>();
508-
509- private SchemaUtil schemaUtil = new SchemaUtil ();
519+ private final Set <GraphQLType > additionalTypes = new LinkedHashSet <>();
520+ private final List <GraphQLDirective > schemaDirectives = new ArrayList <>();
510521
511522 public Builder query (GraphQLObjectType .Builder builder ) {
512523 return query (builder .build ());
@@ -673,10 +684,10 @@ public GraphQLSchema build(Set<GraphQLType> additionalTypes, Set<GraphQLDirectiv
673684 * @return the built schema
674685 */
675686 public GraphQLSchema build () {
676- return buildImpl (false );
687+ return buildImpl ();
677688 }
678689
679- GraphQLSchema buildImpl (boolean afterTransform ) {
690+ private GraphQLSchema buildImpl () {
680691 assertNotNull (additionalTypes , () -> "additionalTypes can't be null" );
681692 assertNotNull (additionalDirectives , () -> "additionalDirectives can't be null" );
682693
@@ -690,12 +701,28 @@ GraphQLSchema buildImpl(boolean afterTransform) {
690701 additionalDirectives .add (Directives .SpecifiedByDirective );
691702 }
692703
693- // grab the legacy code things from types
694- final GraphQLSchema tempSchema = new GraphQLSchema (this , afterTransform );
695- codeRegistry = codeRegistry .transform (codeRegistryBuilder -> schemaUtil .extractCodeFromTypes (codeRegistryBuilder , tempSchema ));
704+ // quick build - no traversing
705+ final GraphQLSchema partiallyBuiltSchema = new GraphQLSchema (this );
706+
707+ GraphQLCodeRegistry .Builder extractedDataFetchers = GraphQLCodeRegistry .newCodeRegistry (codeRegistry );
708+ CodeRegistryVisitor codeRegistryVisitor = new CodeRegistryVisitor (extractedDataFetchers );
709+ GraphQLTypeCollectingVisitor typeCollectingVisitor = new GraphQLTypeCollectingVisitor ();
710+ SchemaUtil .visitPartiallySchema (partiallyBuiltSchema , codeRegistryVisitor , typeCollectingVisitor );
711+
712+ codeRegistry = extractedDataFetchers .build ();
713+ ImmutableMap <String , GraphQLNamedType > allTypes = typeCollectingVisitor .getResult ();
714+ List <GraphQLNamedType > allTypesAsList = getAllTypesAsList (allTypes );
715+
716+ ImmutableMap <String , List <GraphQLObjectType >> groupedImplementations = SchemaUtil .groupInterfaceImplementationsByName (allTypesAsList );
717+ ImmutableMap <String , ImmutableList <GraphQLObjectType >> interfaceNameToObjectTypes = buildInterfacesToObjectTypes (groupedImplementations );
718+
719+ // this is now build however its contained types are still to be mutated by type reference replacement
720+ final GraphQLSchema finalSchema = new GraphQLSchema (partiallyBuiltSchema , codeRegistry , allTypes , interfaceNameToObjectTypes );
721+ SchemaUtil .replaceTypeReferences (finalSchema );
722+ return validateSchema (finalSchema );
723+ }
696724
697- GraphQLSchema graphQLSchema = new GraphQLSchema (tempSchema , codeRegistry );
698- schemaUtil .replaceTypeReferences (graphQLSchema );
725+ private GraphQLSchema validateSchema (GraphQLSchema graphQLSchema ) {
699726 Collection <SchemaValidationError > errors = new SchemaValidator ().validateSchema (graphQLSchema );
700727 if (errors .size () > 0 ) {
701728 throw new InvalidSchemaException (errors );
0 commit comments