Skip to content

Commit 136ac28

Browse files
feat: Adds UniqueTypeNamesRule
1 parent e459369 commit 136ac28

File tree

3 files changed

+218
-1
lines changed

3 files changed

+218
-1
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
/**
3+
* Unique type names
4+
*
5+
* A GraphQL document is only valid if all defined types have unique names.
6+
*/
7+
func UniqueTypeNamesRule(context: SDLValidationContext) -> Visitor {
8+
var knownTypeNames = [String: Name]()
9+
let schema = context.getSchema()
10+
11+
return Visitor(
12+
enter: { node, _, _, _, _ in
13+
if let definition = node as? ScalarTypeDefinition {
14+
checkTypeName(node: definition)
15+
} else if let definition = node as? ObjectTypeDefinition {
16+
checkTypeName(node: definition)
17+
} else if let definition = node as? InterfaceTypeDefinition {
18+
checkTypeName(node: definition)
19+
} else if let definition = node as? InterfaceTypeDefinition {
20+
checkTypeName(node: definition)
21+
} else if let definition = node as? UnionTypeDefinition {
22+
checkTypeName(node: definition)
23+
} else if let definition = node as? EnumTypeDefinition {
24+
checkTypeName(node: definition)
25+
} else if let definition = node as? InputObjectTypeDefinition {
26+
checkTypeName(node: definition)
27+
}
28+
return .continue
29+
}
30+
)
31+
32+
func checkTypeName(node: TypeDefinition) {
33+
let typeName = node.name.value
34+
35+
if schema?.getType(name: typeName) != nil {
36+
context.report(
37+
error: GraphQLError(
38+
message: "Type \"\(typeName)\" already exists in the schema. It cannot also be defined in this type definition.",
39+
nodes: [node.name]
40+
)
41+
)
42+
return
43+
}
44+
45+
if let knownNameNode = knownTypeNames[typeName] {
46+
context.report(
47+
error: GraphQLError(
48+
message: "There can be only one type named \"\(typeName)\".",
49+
nodes: [knownNameNode, node.name]
50+
)
51+
)
52+
} else {
53+
knownTypeNames[typeName] = node.name
54+
}
55+
}
56+
}

Sources/GraphQL/Validation/SpecifiedRules.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public let specifiedRules: [(ValidationContext) -> Visitor] = [
3939
public let specifiedSDLRules: [SDLValidationRule] = [
4040
LoneSchemaDefinitionRule,
4141
UniqueOperationTypesRule,
42-
// UniqueTypeNamesRule,
42+
UniqueTypeNamesRule,
4343
// UniqueEnumValueNamesRule,
4444
// UniqueFieldDefinitionNamesRule,
4545
// UniqueArgumentDefinitionNamesRule,
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
@testable import GraphQL
2+
import XCTest
3+
4+
class UniqueTypeNamesRuleTests: SDLValidationTestCase {
5+
override func setUp() {
6+
rule = UniqueTypeNamesRule
7+
}
8+
9+
func testNoTypes() throws {
10+
try assertValidationErrors(
11+
"""
12+
directive @test on SCHEMA
13+
""",
14+
[]
15+
)
16+
}
17+
18+
func testOneType() throws {
19+
try assertValidationErrors(
20+
"""
21+
type Foo
22+
""",
23+
[]
24+
)
25+
}
26+
27+
func testManyTypes() throws {
28+
try assertValidationErrors(
29+
"""
30+
type Foo
31+
type Bar
32+
type Baz
33+
""",
34+
[]
35+
)
36+
}
37+
38+
func testTypeAndNonTypeDefinitionsNamedTheSame() throws {
39+
try assertValidationErrors(
40+
"""
41+
query Foo { __typename }
42+
fragment Foo on Query { __typename }
43+
directive @Foo on SCHEMA
44+
45+
type Foo
46+
""",
47+
[]
48+
)
49+
}
50+
51+
func testTypesNamedTheSame() throws {
52+
try assertValidationErrors(
53+
"""
54+
type Foo
55+
56+
scalar Foo
57+
type Foo
58+
interface Foo
59+
union Foo
60+
enum Foo
61+
input Foo
62+
""",
63+
[
64+
GraphQLError(
65+
message: #"There can be only one type named "Foo"."#,
66+
locations: [
67+
.init(line: 1, column: 6),
68+
.init(line: 3, column: 8),
69+
]
70+
),
71+
GraphQLError(
72+
message: #"There can be only one type named "Foo"."#,
73+
locations: [
74+
.init(line: 1, column: 6),
75+
.init(line: 4, column: 6),
76+
]
77+
),
78+
GraphQLError(
79+
message: #"There can be only one type named "Foo"."#,
80+
locations: [
81+
.init(line: 1, column: 6),
82+
.init(line: 5, column: 11),
83+
]
84+
),
85+
GraphQLError(
86+
message: #"There can be only one type named "Foo"."#,
87+
locations: [
88+
.init(line: 1, column: 6),
89+
.init(line: 6, column: 7),
90+
]
91+
),
92+
GraphQLError(
93+
message: #"There can be only one type named "Foo"."#,
94+
locations: [
95+
.init(line: 1, column: 6),
96+
.init(line: 7, column: 6),
97+
]
98+
),
99+
GraphQLError(
100+
message: #"There can be only one type named "Foo"."#,
101+
locations: [
102+
.init(line: 1, column: 6),
103+
.init(line: 8, column: 7),
104+
]
105+
),
106+
]
107+
)
108+
}
109+
110+
func testAddingNewTypeToExistingSchema() throws {
111+
let schema = try buildSchema(source: "type Foo")
112+
try assertValidationErrors("type Bar", schema: schema, [])
113+
}
114+
115+
func testAddingNewTypeToExistingSchemaWithSameNamedDirective() throws {
116+
let schema = try buildSchema(source: "directive @Foo on SCHEMA")
117+
try assertValidationErrors("type Foo", schema: schema, [])
118+
}
119+
120+
func testAddingConflictingTypesToExistingSchema() throws {
121+
let schema = try buildSchema(source: "type Foo")
122+
let sdl = """
123+
scalar Foo
124+
type Foo
125+
interface Foo
126+
union Foo
127+
enum Foo
128+
input Foo
129+
"""
130+
try assertValidationErrors(
131+
sdl,
132+
schema: schema,
133+
[
134+
GraphQLError(
135+
message: #"Type "Foo" already exists in the schema. It cannot also be defined in this type definition."#,
136+
locations: [.init(line: 1, column: 8)]
137+
),
138+
GraphQLError(
139+
message: #"Type "Foo" already exists in the schema. It cannot also be defined in this type definition."#,
140+
locations: [.init(line: 2, column: 6)]
141+
),
142+
GraphQLError(
143+
message: #"Type "Foo" already exists in the schema. It cannot also be defined in this type definition."#,
144+
locations: [.init(line: 3, column: 11)]
145+
),
146+
GraphQLError(
147+
message: #"Type "Foo" already exists in the schema. It cannot also be defined in this type definition."#,
148+
locations: [.init(line: 4, column: 7)]
149+
),
150+
GraphQLError(
151+
message: #"Type "Foo" already exists in the schema. It cannot also be defined in this type definition."#,
152+
locations: [.init(line: 5, column: 6)]
153+
),
154+
GraphQLError(
155+
message: #"Type "Foo" already exists in the schema. It cannot also be defined in this type definition."#,
156+
locations: [.init(line: 6, column: 7)]
157+
),
158+
]
159+
)
160+
}
161+
}

0 commit comments

Comments
 (0)