Skip to content

Commit 73f2b00

Browse files
committed
Added some more validators.
1 parent 76c9251 commit 73f2b00

34 files changed

+1313
-506
lines changed

Sources/GraphQL/Execution/Execute.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,10 @@ func doesFragmentConditionMatch(
638638
}
639639

640640
if let abstractType = conditionalType as? GraphQLAbstractType {
641-
return try exeContext.schema.isPossibleType(abstractType: abstractType, possibleType: type)
641+
return exeContext.schema.isSubType(
642+
abstractType: abstractType,
643+
maybeSubType: type
644+
)
642645
}
643646

644647
return false
@@ -1062,7 +1065,7 @@ func completeAbstractValue(
10621065
)
10631066
}
10641067

1065-
if try !exeContext.schema.isPossibleType(abstractType: returnType, possibleType: objectType) {
1068+
if !exeContext.schema.isSubType(abstractType: returnType, maybeSubType: objectType) {
10661069
throw GraphQLError(
10671070
message:
10681071
"Runtime Object type \"\(objectType.name)\" is not a possible type " +

Sources/GraphQL/Language/AST.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1272,12 +1272,20 @@ public final class InterfaceTypeDefinition {
12721272
public let kind: Kind = .interfaceTypeDefinition
12731273
public let loc: Location?
12741274
public let name: Name
1275+
public let interfaces: [NamedType]
12751276
public let directives: [Directive]
12761277
public let fields: [FieldDefinition]
12771278

1278-
init(loc: Location? = nil, name: Name, directives: [Directive] = [], fields: [FieldDefinition]) {
1279+
init(
1280+
loc: Location? = nil,
1281+
name: Name,
1282+
interfaces: [NamedType] = [],
1283+
directives: [Directive] = [],
1284+
fields: [FieldDefinition]
1285+
) {
12791286
self.loc = loc
12801287
self.name = name
1288+
self.interfaces = interfaces
12811289
self.directives = directives
12821290
self.fields = fields
12831291
}

Sources/GraphQL/Language/Parser.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -803,10 +803,11 @@ func parseInputValueDef(lexer: Lexer) throws -> InputValueDefinition {
803803
* InterfaceTypeDefinition : interface Name Directives? { FieldDefinition+ }
804804
*/
805805
func parseInterfaceTypeDefinition(lexer: Lexer) throws -> InterfaceTypeDefinition {
806-
let start = lexer.token;
807-
try expectKeyword(lexer: lexer, value: "interface");
808-
let name = try parseName(lexer: lexer);
809-
let directives = try parseDirectives(lexer: lexer);
806+
let start = lexer.token
807+
try expectKeyword(lexer: lexer, value: "interface")
808+
let name = try parseName(lexer: lexer)
809+
let interfaces = try parseImplementsInterfaces(lexer: lexer)
810+
let directives = try parseDirectives(lexer: lexer)
810811
let fields = try any(
811812
lexer: lexer,
812813
openKind: .openingBrace,
@@ -816,6 +817,7 @@ func parseInterfaceTypeDefinition(lexer: Lexer) throws -> InterfaceTypeDefinitio
816817
return InterfaceTypeDefinition(
817818
loc: loc(lexer: lexer, startToken: start),
818819
name: name,
820+
interfaces: interfaces,
819821
directives: directives,
820822
fields: fields
821823
)

Sources/GraphQL/Language/Visitor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ let QueryDocumentKeys: [Kind: [String]] = [
3535
.objectTypeDefinition: ["name", "interfaces", "directives", "fields"],
3636
.fieldDefinition: ["name", "arguments", "type", "directives"],
3737
.inputValueDefinition: ["name", "type", "defaultValue", "directives"],
38-
.interfaceTypeDefinition: ["name", "directives", "fields"],
38+
.interfaceTypeDefinition: ["name", "interfaces", "directives", "fields"],
3939
.unionTypeDefinition: ["name", "directives", "types"],
4040
.enumTypeDefinition: ["name", "directives", "values"],
4141
.enumValueDefinition: ["name", "directives"],

Sources/GraphQL/Type/Definition.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,21 +739,26 @@ public final class GraphQLInterfaceType {
739739
public let description: String?
740740
public let resolveType: GraphQLTypeResolve?
741741
public let fields: GraphQLFieldDefinitionMap
742+
public let interfaces: [GraphQLInterfaceType]
742743
public let kind: TypeKind = .interface
743744

744745
public init(
745746
name: String,
746747
description: String? = nil,
748+
interfaces: [GraphQLInterfaceType] = [],
747749
fields: GraphQLFieldMap,
748750
resolveType: GraphQLTypeResolve? = nil
749751
) throws {
750752
try assertValid(name: name)
751753
self.name = name
752754
self.description = description
755+
753756
self.fields = try defineFieldMap(
754757
name: name,
755758
fields: fields
756759
)
760+
761+
self.interfaces = interfaces
757762
self.resolveType = resolveType
758763
}
759764

Sources/GraphQL/Type/Introspection.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,11 +253,16 @@ let __Type: GraphQLObjectType = try! GraphQLObjectType(
253253
"interfaces": GraphQLField(
254254
type: GraphQLList(GraphQLNonNull(GraphQLTypeReference("__Type"))),
255255
resolve: { type, _, _, _ -> [GraphQLInterfaceType]? in
256-
guard let type = type as? GraphQLObjectType else {
257-
return nil
256+
if let type = type as? GraphQLObjectType {
257+
return type.interfaces
258258
}
259259

260-
return type.interfaces
260+
if let type = type as? GraphQLInterfaceType {
261+
return type.interfaces
262+
}
263+
264+
return nil
265+
261266
}
262267
),
263268
"possibleTypes": GraphQLField(
@@ -403,7 +408,7 @@ let __TypeKind = try! GraphQLEnumType(
403408
"INTERFACE": GraphQLEnumValue(
404409
value: Map(TypeKind.interface.rawValue),
405410
description: "Indicates this type is an interface. " +
406-
"`fields` and `possibleTypes` are valid fields."
411+
"`fields`, `interfaces`, and `possibleTypes` are valid fields."
407412
),
408413
"UNION": GraphQLEnumValue(
409414
value: Map(TypeKind.union.rawValue),

Sources/GraphQL/Type/Schema.swift

Lines changed: 99 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ public final class GraphQLSchema {
3131
public let subscriptionType: GraphQLObjectType?
3232
public let directives: [GraphQLDirective]
3333
public let typeMap: TypeMap
34-
public let implementations: [String: [GraphQLObjectType]]
35-
private var possibleTypeMap: [String: [String: Bool]] = [:]
34+
public let implementations: [String: InterfaceImplementations]
35+
private var subTypeMap: [String: [String: Bool]] = [:]
3636

3737
public init(
3838
query: GraphQLObjectType,
@@ -77,22 +77,7 @@ public final class GraphQLSchema {
7777
try replaceTypeReferences(typeMap: typeMap)
7878

7979
// Keep track of all implementations by interface name.
80-
var implementations: [String: [GraphQLObjectType]] = [:]
81-
82-
for (_, type) in typeMap {
83-
if let object = type as? GraphQLObjectType {
84-
for interface in object.interfaces {
85-
if var i = implementations[interface.name] {
86-
i.append(object)
87-
implementations[interface.name] = i
88-
} else {
89-
implementations[interface.name] = [object]
90-
}
91-
}
92-
}
93-
}
94-
95-
self.implementations = implementations.mapValues { $0.sorted { $0.fields.count > $1.fields.count } }
80+
self.implementations = collectImplementations(types: Array(typeMap.values))
9681

9782
// Enforce correct interface implementations.
9883
for (_, type) in typeMap {
@@ -109,48 +94,70 @@ public final class GraphQLSchema {
10994
}
11095

11196
public func getPossibleTypes(abstractType: GraphQLAbstractType) -> [GraphQLObjectType] {
112-
if let union = abstractType as? GraphQLUnionType {
113-
return union.types
97+
if let unionType = abstractType as? GraphQLUnionType {
98+
return unionType.types
11499
}
115-
116-
if let interface = abstractType as? GraphQLInterfaceType {
117-
return implementations[interface.name] ?? []
100+
101+
if let interfaceType = abstractType as? GraphQLInterfaceType {
102+
return getImplementations(interfaceType: interfaceType).objects
118103
}
119-
104+
120105
fatalError("Should be impossible. Only UnionType and InterfaceType should conform to AbstractType")
121106
}
107+
108+
public func getImplementations(
109+
interfaceType: GraphQLInterfaceType
110+
) -> InterfaceImplementations {
111+
implementations[interfaceType.name]!
112+
}
122113

123-
public func isPossibleType(abstractType: GraphQLAbstractType, possibleType: GraphQLObjectType) throws -> Bool {
124-
if possibleTypeMap[abstractType.name] == nil {
125-
let possibleTypes = getPossibleTypes(abstractType: abstractType)
126-
127-
guard !possibleTypes.isEmpty else {
128-
throw GraphQLError(
129-
message:
130-
"Could not find possible implementing types for \(abstractType.name) " +
131-
"in schema. Check that schema.types is defined and is an array of " +
132-
"all possible types in the schema."
133-
)
134-
114+
// @deprecated: use isSubType instead - will be removed in the future.
115+
public func isPossibleType(
116+
abstractType: GraphQLAbstractType,
117+
possibleType: GraphQLObjectType
118+
) throws -> Bool {
119+
isSubType(abstractType: abstractType, maybeSubType: possibleType)
120+
}
121+
122+
public func isSubType(
123+
abstractType: GraphQLAbstractType,
124+
maybeSubType: GraphQLNamedType
125+
) -> Bool {
126+
var map = subTypeMap[abstractType.name]
127+
128+
if map == nil {
129+
map = [:]
130+
131+
if let unionType = abstractType as? GraphQLUnionType {
132+
for type in unionType.types {
133+
map?[type.name] = true
134+
}
135135
}
136-
137-
var map: [String: Bool] = [:]
138-
139-
for type in possibleTypes {
140-
map[type.name] = true
136+
137+
if let interfaceType = abstractType as? GraphQLInterfaceType {
138+
let implementations = getImplementations(interfaceType: interfaceType)
139+
140+
for type in implementations.objects {
141+
map?[type.name] = true
142+
}
143+
144+
for type in implementations.interfaces {
145+
map?[type.name] = true
146+
}
141147
}
142-
143-
possibleTypeMap[abstractType.name] = map
148+
149+
subTypeMap[abstractType.name] = map
144150
}
145151

146-
return possibleTypeMap[abstractType.name]?[possibleType.name] != nil
152+
let isSubType = map?[maybeSubType.name] != nil
153+
return isSubType
147154
}
148155

149-
150156
public func getDirective(name: String) -> GraphQLDirective? {
151157
for directive in directives where directive.name == name {
152158
return directive
153159
}
160+
154161
return nil
155162
}
156163
}
@@ -166,6 +173,51 @@ extension GraphQLSchema : Encodable {
166173

167174
public typealias TypeMap = [String: GraphQLNamedType]
168175

176+
public struct InterfaceImplementations {
177+
public let objects: [GraphQLObjectType]
178+
public let interfaces: [GraphQLInterfaceType]
179+
180+
public init(
181+
objects: [GraphQLObjectType] = [],
182+
interfaces: [GraphQLInterfaceType] = []
183+
) {
184+
self.objects = objects
185+
self.interfaces = interfaces
186+
}
187+
}
188+
189+
func collectImplementations(
190+
types: [GraphQLNamedType]
191+
) -> [String : InterfaceImplementations] {
192+
var implementations: [String: InterfaceImplementations] = [:]
193+
194+
for type in types {
195+
if let type = type as? GraphQLInterfaceType {
196+
if implementations[type.name] == nil {
197+
implementations[type.name] = InterfaceImplementations()
198+
}
199+
200+
// Store implementations by interface.
201+
for iface in type.interfaces {
202+
implementations[iface.name] = InterfaceImplementations(
203+
interfaces: (implementations[iface.name]?.interfaces ?? []) + [type]
204+
)
205+
}
206+
}
207+
208+
if let type = type as? GraphQLObjectType {
209+
// Store implementations by objects.
210+
for iface in type.interfaces {
211+
implementations[iface.name] = InterfaceImplementations(
212+
objects: (implementations[iface.name]?.objects ?? []) + [type]
213+
)
214+
}
215+
}
216+
}
217+
218+
return implementations
219+
}
220+
169221
func typeMapReducer(typeMap: TypeMap, type: GraphQLType) throws -> TypeMap {
170222
var typeMap = typeMap
171223

@@ -197,9 +249,7 @@ func typeMapReducer(typeMap: TypeMap, type: GraphQLType) throws -> TypeMap {
197249

198250
if let type = type as? GraphQLObjectType {
199251
typeMap = try type.interfaces.reduce(typeMap, typeMapReducer)
200-
}
201252

202-
if let type = type as? GraphQLObjectType {
203253
for (_, field) in type.fields {
204254

205255
if !field.args.isEmpty {
@@ -212,6 +262,8 @@ func typeMapReducer(typeMap: TypeMap, type: GraphQLType) throws -> TypeMap {
212262
}
213263

214264
if let type = type as? GraphQLInterfaceType {
265+
typeMap = try type.interfaces.reduce(typeMap, typeMapReducer)
266+
215267
for (_, field) in type.fields {
216268

217269
if !field.args.isEmpty {

0 commit comments

Comments
 (0)