@@ -41,7 +41,7 @@ public final class GraphQLSchema {
4141 public let subscriptionType : GraphQLObjectType ?
4242 public let directives : [ GraphQLDirective ]
4343 public let typeMap : TypeMap
44- public let implementations : [ String : InterfaceImplementations ]
44+ public internal ( set ) var implementations : [ String : InterfaceImplementations ]
4545 private var subTypeMap : [ String : [ String : Bool ] ] = [ : ]
4646
4747 public init (
@@ -70,58 +70,92 @@ public final class GraphQLSchema {
7070 // Provide specified directives (e.g. @include and @skip) by default.
7171 self . directives = directives. isEmpty ? specifiedDirectives : directives
7272
73- // Build type map now to detect any errors within this schema.
74-
75- var typeMap = TypeMap ( )
76-
7773 // To preserve order of user-provided types, we add first to add them to
7874 // the set of "collected" types, so `collectReferencedTypes` ignore them.
75+ var allReferencedTypes = TypeMap ( )
7976 for type in types {
80- typeMap [ type. name] = type
77+ allReferencedTypes [ type. name] = type
8178 }
8279 if !types. isEmpty {
8380 for type in types {
8481 // When we ready to process this type, we remove it from "collected" types
8582 // and then add it together with all dependent types in the correct position.
86- typeMap [ type. name] = nil
87- typeMap = try typeMapReducer ( typeMap: typeMap , type: type)
83+ allReferencedTypes [ type. name] = nil
84+ allReferencedTypes = try typeMapReducer ( typeMap: allReferencedTypes , type: type)
8885 }
8986 }
9087
9188 if let query = queryType {
92- typeMap = try typeMapReducer ( typeMap: typeMap , type: query)
89+ allReferencedTypes = try typeMapReducer ( typeMap: allReferencedTypes , type: query)
9390 }
9491
9592 if let mutation = mutationType {
96- typeMap = try typeMapReducer ( typeMap: typeMap , type: mutation)
93+ allReferencedTypes = try typeMapReducer ( typeMap: allReferencedTypes , type: mutation)
9794 }
9895
9996 if let subscription = subscriptionType {
100- typeMap = try typeMapReducer ( typeMap: typeMap , type: subscription)
97+ allReferencedTypes = try typeMapReducer ( typeMap: allReferencedTypes , type: subscription)
10198 }
10299
103100 for directive in self . directives {
104101 for arg in directive. args {
105- typeMap = try typeMapReducer ( typeMap: typeMap , type: arg. type)
102+ allReferencedTypes = try typeMapReducer ( typeMap: allReferencedTypes , type: arg. type)
106103 }
107104 }
108105
109- typeMap = try typeMapReducer ( typeMap: typeMap, type: __Schema)
106+ allReferencedTypes = try typeMapReducer ( typeMap: allReferencedTypes, type: __Schema)
107+ try replaceTypeReferences ( typeMap: allReferencedTypes)
110108
111- self . typeMap = typeMap
112- try replaceTypeReferences ( typeMap: typeMap )
109+ // Storing the resulting map for reference by the schema.
110+ var typeMap = TypeMap ( )
113111
114112 // Keep track of all implementations by interface name.
115113 implementations = try collectImplementations ( types: Array ( typeMap. values) )
116114
117- // Enforce correct interface implementations.
118- for (_, type) in typeMap {
119- if let object = type as? GraphQLObjectType {
120- for interface in try object. getInterfaces ( ) {
121- try assert ( object: object, implementsInterface: interface, schema: self )
115+ for namedType in allReferencedTypes. values {
116+ let typeName = namedType. name
117+ if typeMap [ typeName] != nil {
118+ throw GraphQLError (
119+ message:
120+ " Schema must contain uniquely named types but contains multiple types named \" \( typeName) \" . "
121+ )
122+ }
123+ typeMap [ typeName] = namedType
124+
125+ if let namedType = namedType as? GraphQLInterfaceType {
126+ // Store implementations by interface.
127+ for iface in try namedType. getInterfaces ( ) {
128+ let implementations = self . implementations [ iface. name] ?? . init(
129+ objects: [ ] ,
130+ interfaces: [ ]
131+ )
132+
133+ var interfaces = implementations. interfaces
134+ interfaces. append ( namedType)
135+ self . implementations [ iface. name] = . init(
136+ objects: implementations. objects,
137+ interfaces: interfaces
138+ )
139+ }
140+ } else if let namedType = namedType as? GraphQLObjectType {
141+ // Store implementations by objects.
142+ for iface in try namedType. getInterfaces ( ) {
143+ let implementations = self . implementations [ iface. name] ?? . init(
144+ objects: [ ] ,
145+ interfaces: [ ]
146+ )
147+
148+ var objects = implementations. objects
149+ objects. append ( namedType)
150+ self . implementations [ iface. name] = . init(
151+ objects: objects,
152+ interfaces: implementations. interfaces
153+ )
122154 }
123155 }
124156 }
157+
158+ self . typeMap = typeMap
125159 }
126160
127161 convenience init ( config: GraphQLSchemaNormalizedConfig ) throws {
@@ -356,75 +390,6 @@ func typeMapReducer(typeMap: TypeMap, type: GraphQLType) throws -> TypeMap {
356390 return typeMap
357391}
358392
359- func assert(
360- object: GraphQLObjectType ,
361- implementsInterface interface: GraphQLInterfaceType ,
362- schema _: GraphQLSchema
363- ) throws {
364- let objectFieldMap = try object. getFields ( )
365- let interfaceFieldMap = try interface. getFields ( )
366-
367- for (fieldName, interfaceField) in interfaceFieldMap {
368- guard let objectField = objectFieldMap [ fieldName] else {
369- throw GraphQLError (
370- message:
371- " \( interface. name) expects field \( fieldName) " +
372- " but \( object. name) does not provide it. "
373- )
374- }
375-
376- // // Assert interface field type is satisfied by object field type, by being
377- // // a valid subtype. (covariant)
378- // guard try isTypeSubTypeOf(schema, objectField.type, interfaceField.type) else {
379- // throw GraphQLError(
380- // message:
381- // "\(interface.name).\(fieldName) expects type \"\(interfaceField.type)\" " +
382- // "but " +
383- // "\(object.name).\(fieldName) provides type \"\(objectField.type)\"."
384- // )
385- // }
386-
387- // Assert each interface field arg is implemented.
388- for interfaceArg in interfaceField. args {
389- let argName = interfaceArg. name
390- guard let objectArg = objectField. args. find ( { $0. name == argName } ) else {
391- throw GraphQLError (
392- message:
393- " \( interface. name) . \( fieldName) expects argument \" \( argName) \" but " +
394- " \( object. name) . \( fieldName) does not provide it. "
395- )
396- }
397-
398- // Assert interface field arg type matches object field arg type.
399- // (invariant)
400- guard isEqualType ( interfaceArg. type, objectArg. type) else {
401- throw GraphQLError (
402- message:
403- " \( interface. name) . \( fieldName) ( \( argName) :) expects type " +
404- " \" \( interfaceArg. type) \" but " +
405- " \( object. name) . \( fieldName) ( \( argName) :) provides type " +
406- " \" \( objectArg. type) \" . "
407- )
408- }
409- }
410-
411- // Assert additional arguments must not be required.
412- for objectArg in objectField. args {
413- let argName = objectArg. name
414- if
415- interfaceField. args. find ( { $0. name == argName } ) == nil ,
416- isRequiredArgument ( objectArg)
417- {
418- throw GraphQLError (
419- message:
420- " \( object. name) . \( fieldName) includes required argument ( \( argName) :) that is missing " +
421- " from the Interface field \( interface. name) . \( fieldName) . "
422- )
423- }
424- }
425- }
426- }
427-
428393func replaceTypeReferences( typeMap: TypeMap ) throws {
429394 for type in typeMap {
430395 if let typeReferenceContainer = type. value as? GraphQLTypeReferenceContainer {
0 commit comments