Skip to content

Commit dda63e4

Browse files
Merge pull request #108 from NeedleInAJayStack/feature/type-reference-imrovements
TypeReference improvements
2 parents 080cffd + d6a94dd commit dda63e4

File tree

2 files changed

+82
-9
lines changed

2 files changed

+82
-9
lines changed

Sources/GraphQL/Type/Schema.swift

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -235,16 +235,28 @@ func typeMapReducer(typeMap: TypeMap, type: GraphQLType) throws -> TypeMap {
235235
return typeMap // Should never happen
236236
}
237237

238-
guard typeMap[type.name] == nil || typeMap[type.name] is GraphQLTypeReference else {
239-
guard typeMap[type.name]! == type || type is GraphQLTypeReference else {
240-
throw GraphQLError(
241-
message:
242-
"Schema must contain unique named types but contains multiple " +
243-
"types named \"\(type.name)\"."
244-
)
238+
if let existingType = typeMap[type.name] {
239+
if existingType is GraphQLTypeReference {
240+
if type is GraphQLTypeReference {
241+
// Just short circuit because they're both type references
242+
return typeMap
243+
}
244+
// Otherwise, fall through and override the type reference
245+
} else {
246+
if type is GraphQLTypeReference {
247+
// Just ignore the reference and keep the concrete one
248+
return typeMap
249+
} else if !(existingType == type) {
250+
throw GraphQLError(
251+
message:
252+
"Schema must contain unique named types but contains multiple " +
253+
"types named \"\(type.name)\"."
254+
)
255+
} else {
256+
// Otherwise, it's already been defined so short circuit
257+
return typeMap
258+
}
245259
}
246-
247-
return typeMap
248260
}
249261

250262
typeMap[type.name] = type
@@ -363,6 +375,15 @@ func replaceTypeReferences(typeMap: TypeMap) throws {
363375
try typeReferenceContainer.replaceTypeReferences(typeMap: typeMap)
364376
}
365377
}
378+
379+
// Check that no type names map to TypeReferences. That is, they have all been resolved to actual types.
380+
for (typeName, graphQLNamedType) in typeMap {
381+
if graphQLNamedType is GraphQLTypeReference {
382+
throw GraphQLError(
383+
message: "Type \"\(typeName)\" was referenced but not defined."
384+
)
385+
}
386+
}
366387
}
367388

368389
func resolveTypeReference(type: GraphQLType, typeMap: TypeMap) throws -> GraphQLType {

Tests/GraphQLTests/TypeTests/GraphQLSchemaTests.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,56 @@ class GraphQLSchemaTests: XCTestCase {
157157
)
158158
}
159159
}
160+
161+
func testAssertSchemaCircularReference() throws {
162+
let object1 = try GraphQLObjectType(
163+
name: "Object1",
164+
fields: [
165+
"object2": GraphQLField(
166+
type: GraphQLTypeReference("Object2")
167+
),
168+
]
169+
)
170+
let object2 = try GraphQLObjectType(
171+
name: "Object2",
172+
fields: [
173+
"object1": GraphQLField(
174+
type: GraphQLTypeReference("Object1")
175+
),
176+
]
177+
)
178+
let query = try GraphQLObjectType(
179+
name: "Query",
180+
fields: [
181+
"object1": GraphQLField(type: GraphQLTypeReference("Object1")),
182+
"object2": GraphQLField(type: GraphQLTypeReference("Object2")),
183+
]
184+
)
185+
186+
let schema = try GraphQLSchema(query: query, types: [object1, object2])
187+
for (_, graphQLNamedType) in schema.typeMap {
188+
XCTAssertFalse(graphQLNamedType is GraphQLTypeReference)
189+
}
190+
}
191+
192+
func testAssertSchemaFailsWhenObjectNotDefined() throws {
193+
let object1 = try GraphQLObjectType(
194+
name: "Object1",
195+
fields: [
196+
"object2": GraphQLField(
197+
type: GraphQLTypeReference("Object2")
198+
),
199+
]
200+
)
201+
let query = try GraphQLObjectType(
202+
name: "Query",
203+
fields: [
204+
"object1": GraphQLField(type: GraphQLTypeReference("Object1")),
205+
]
206+
)
207+
208+
XCTAssertThrowsError(
209+
_ = try GraphQLSchema(query: query, types: [object1])
210+
)
211+
}
160212
}

0 commit comments

Comments
 (0)