Skip to content

Commit 5fad1f4

Browse files
feature: Adds UniqueFragmentNamesRule
1 parent e26affc commit 5fad1f4

File tree

3 files changed

+160
-1
lines changed

3 files changed

+160
-1
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
/**
3+
* Unique fragment names
4+
*
5+
* A GraphQL document is only valid if all defined fragments have unique names.
6+
*
7+
* See https://spec.graphql.org/draft/#sec-Fragment-Name-Uniqueness
8+
*/
9+
func UniqueFragmentNamesRule(context: ValidationContext) -> Visitor {
10+
var knownFragmentNames = [String: Name]()
11+
return Visitor(
12+
enter: { node, _, _, _, _ in
13+
if let fragment = node as? FragmentDefinition {
14+
let fragmentName = fragment.name
15+
if let knownFragmentName = knownFragmentNames[fragmentName.value] {
16+
context.report(
17+
error: GraphQLError(
18+
message: "There can be only one fragment named \"\(fragmentName.value)\".",
19+
nodes: [knownFragmentName, fragmentName]
20+
)
21+
)
22+
} else {
23+
knownFragmentNames[fragmentName.value] = fragmentName
24+
}
25+
}
26+
return .continue
27+
}
28+
)
29+
}

Sources/GraphQL/Validation/SpecifiedRules.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public let specifiedRules: [(ValidationContext) -> Visitor] = [
1111
VariablesAreInputTypesRule,
1212
ScalarLeafsRule,
1313
FieldsOnCorrectTypeRule,
14-
// UniqueFragmentNamesRule,
14+
UniqueFragmentNamesRule,
1515
// KnownFragmentNamesRule,
1616
NoUnusedFragmentsRule,
1717
PossibleFragmentSpreadsRule,
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
@testable import GraphQL
2+
import XCTest
3+
4+
class UniqueFragmentNamesRuleTests: ValidationTestCase {
5+
override func setUp() {
6+
rule = UniqueFragmentNamesRule
7+
}
8+
9+
func testNoFragments() throws {
10+
try assertValid(
11+
"""
12+
{
13+
field
14+
}
15+
"""
16+
)
17+
}
18+
19+
func testOneFragment() throws {
20+
try assertValid(
21+
"""
22+
{
23+
...fragA
24+
}
25+
26+
fragment fragA on Type {
27+
field
28+
}
29+
"""
30+
)
31+
}
32+
33+
func testManyFragments() throws {
34+
try assertValid(
35+
"""
36+
{
37+
...fragA
38+
...fragB
39+
...fragC
40+
}
41+
fragment fragA on Type {
42+
fieldA
43+
}
44+
fragment fragB on Type {
45+
fieldB
46+
}
47+
fragment fragC on Type {
48+
fieldC
49+
}
50+
"""
51+
)
52+
}
53+
54+
func testInlineFragmentsAreAlwaysUnique() throws {
55+
try assertValid(
56+
"""
57+
{
58+
...on Type {
59+
fieldA
60+
}
61+
...on Type {
62+
fieldB
63+
}
64+
}
65+
"""
66+
)
67+
}
68+
69+
func testFragmentAndOperationNamedTheSame() throws {
70+
try assertValid(
71+
"""
72+
query Foo {
73+
...Foo
74+
}
75+
fragment Foo on Type {
76+
field
77+
}
78+
"""
79+
)
80+
}
81+
82+
func testFragmentsNamedTheSame() throws {
83+
let errors = try assertInvalid(
84+
errorCount: 1,
85+
query:
86+
"""
87+
{
88+
...fragA
89+
}
90+
fragment fragA on Type {
91+
fieldA
92+
}
93+
fragment fragA on Type {
94+
fieldB
95+
}
96+
"""
97+
)
98+
try assertValidationError(
99+
error: errors[0],
100+
locations: [
101+
(line: 4, column: 10),
102+
(line: 7, column: 10),
103+
],
104+
message: "There can be only one fragment named \"fragA\"."
105+
)
106+
}
107+
108+
func testFragmentsNamedTheSameWithoutBeingReferenced() throws {
109+
let errors = try assertInvalid(
110+
errorCount: 1,
111+
query:
112+
"""
113+
fragment fragA on Type {
114+
fieldA
115+
}
116+
fragment fragA on Type {
117+
fieldB
118+
}
119+
"""
120+
)
121+
try assertValidationError(
122+
error: errors[0],
123+
locations: [
124+
(line: 1, column: 10),
125+
(line: 4, column: 10),
126+
],
127+
message: "There can be only one fragment named \"fragA\"."
128+
)
129+
}
130+
}

0 commit comments

Comments
 (0)