Skip to content

TypeReference improvements #108

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
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
39 changes: 30 additions & 9 deletions Sources/GraphQL/Type/Schema.swift
Original file line number Diff line number Diff line change
Expand Up @@ -235,16 +235,28 @@ func typeMapReducer(typeMap: TypeMap, type: GraphQLType) throws -> TypeMap {
return typeMap // Should never happen
}

guard typeMap[type.name] == nil || typeMap[type.name] is GraphQLTypeReference else {
guard typeMap[type.name]! == type || type is GraphQLTypeReference else {
throw GraphQLError(
message:
"Schema must contain unique named types but contains multiple " +
"types named \"\(type.name)\"."
)
if let existingType = typeMap[type.name] {
if existingType is GraphQLTypeReference {
if type is GraphQLTypeReference {
// Just short circuit because they're both type references
return typeMap
}
// Otherwise, fall through and override the type reference
} else {
if type is GraphQLTypeReference {
// Just ignore the reference and keep the concrete one
return typeMap
} else if !(existingType == type) {
throw GraphQLError(
message:
"Schema must contain unique named types but contains multiple " +
"types named \"\(type.name)\"."
)
} else {
// Otherwise, it's already been defined so short circuit
return typeMap
}
}

return typeMap
}

typeMap[type.name] = type
Expand Down Expand Up @@ -363,6 +375,15 @@ func replaceTypeReferences(typeMap: TypeMap) throws {
try typeReferenceContainer.replaceTypeReferences(typeMap: typeMap)
}
}

// Check that no type names map to TypeReferences. That is, they have all been resolved to actual types.
for (typeName, graphQLNamedType) in typeMap {
if graphQLNamedType is GraphQLTypeReference {
throw GraphQLError(
message: "Type \"\(typeName)\" was referenced but not defined."
)
}
}
}

func resolveTypeReference(type: GraphQLType, typeMap: TypeMap) throws -> GraphQLType {
Expand Down
52 changes: 52 additions & 0 deletions Tests/GraphQLTests/TypeTests/GraphQLSchemaTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,56 @@ class GraphQLSchemaTests: XCTestCase {
)
}
}

func testAssertSchemaCircularReference() throws {
let object1 = try GraphQLObjectType(
name: "Object1",
fields: [
"object2": GraphQLField(
type: GraphQLTypeReference("Object2")
),
]
)
let object2 = try GraphQLObjectType(
name: "Object2",
fields: [
"object1": GraphQLField(
type: GraphQLTypeReference("Object1")
),
]
)
let query = try GraphQLObjectType(
name: "Query",
fields: [
"object1": GraphQLField(type: GraphQLTypeReference("Object1")),
"object2": GraphQLField(type: GraphQLTypeReference("Object2")),
]
)

let schema = try GraphQLSchema(query: query, types: [object1, object2])
for (_, graphQLNamedType) in schema.typeMap {
XCTAssertFalse(graphQLNamedType is GraphQLTypeReference)
}
}

func testAssertSchemaFailsWhenObjectNotDefined() throws {
let object1 = try GraphQLObjectType(
name: "Object1",
fields: [
"object2": GraphQLField(
type: GraphQLTypeReference("Object2")
),
]
)
let query = try GraphQLObjectType(
name: "Query",
fields: [
"object1": GraphQLField(type: GraphQLTypeReference("Object1")),
]
)

XCTAssertThrowsError(
_ = try GraphQLSchema(query: query, types: [object1])
)
}
}