Skip to content

Commit 69042cd

Browse files
feat: Adds UniqueEnumValueNamesRule
1 parent 136ac28 commit 69042cd

File tree

3 files changed

+274
-1
lines changed

3 files changed

+274
-1
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
2+
/**
3+
* Unique enum value names
4+
*
5+
* A GraphQL enum type is only valid if all its values are uniquely named.
6+
*/
7+
func UniqueEnumValueNamesRule(
8+
context: SDLValidationContext
9+
) -> Visitor {
10+
let schema = context.getSchema()
11+
let existingTypeMap = schema?.typeMap ?? [:]
12+
var knownValueNames = [String: [String: Name]]()
13+
14+
return Visitor(
15+
enter: { node, _, _, _, _ in
16+
if let definition = node as? EnumTypeDefinition {
17+
checkValueUniqueness(node: definition)
18+
} else if let definition = node as? EnumExtensionDefinition {
19+
checkValueUniqueness(node: definition.definition)
20+
}
21+
return .continue
22+
}
23+
)
24+
25+
func checkValueUniqueness(node: EnumTypeDefinition) {
26+
let typeName = node.name.value
27+
var valueNames = knownValueNames[typeName] ?? [:]
28+
let valueNodes = node.values
29+
for valueDef in valueNodes {
30+
let valueName = valueDef.name.value
31+
32+
let existingType = existingTypeMap[typeName]
33+
if
34+
let existingType = existingType as? GraphQLEnumType,
35+
existingType.nameLookup[valueName] != nil
36+
{
37+
context.report(
38+
error: GraphQLError(
39+
message: "Enum value \"\(typeName).\(valueName)\" already exists in the schema. It cannot also be defined in this type extension.",
40+
nodes: [valueDef.name]
41+
)
42+
)
43+
continue
44+
}
45+
46+
if let knownValueName = valueNames[valueName] {
47+
context.report(
48+
error: GraphQLError(
49+
message: "Enum value \"\(typeName).\(valueName)\" can only be defined once.",
50+
nodes: [knownValueName, valueDef.name]
51+
)
52+
)
53+
} else {
54+
valueNames[valueName] = valueDef.name
55+
}
56+
}
57+
knownValueNames[typeName] = valueNames
58+
}
59+
}

Sources/GraphQL/Validation/SpecifiedRules.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public let specifiedSDLRules: [SDLValidationRule] = [
4040
LoneSchemaDefinitionRule,
4141
UniqueOperationTypesRule,
4242
UniqueTypeNamesRule,
43-
// UniqueEnumValueNamesRule,
43+
UniqueEnumValueNamesRule,
4444
// UniqueFieldDefinitionNamesRule,
4545
// UniqueArgumentDefinitionNamesRule,
4646
// UniqueDirectiveNamesRule,
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
@testable import GraphQL
2+
import XCTest
3+
4+
class UniqueEnumValueNamesRuleTests: SDLValidationTestCase {
5+
override func setUp() {
6+
rule = UniqueEnumValueNamesRule
7+
}
8+
9+
func testNoValues() throws {
10+
try assertValidationErrors(
11+
"""
12+
enum SomeEnum
13+
""",
14+
[]
15+
)
16+
}
17+
18+
func testOneValue() throws {
19+
try assertValidationErrors(
20+
"""
21+
enum SomeEnum {
22+
FOO
23+
}
24+
""",
25+
[]
26+
)
27+
}
28+
29+
func testMultipleValues() throws {
30+
try assertValidationErrors(
31+
"""
32+
enum SomeEnum {
33+
FOO
34+
BAR
35+
}
36+
""",
37+
[]
38+
)
39+
}
40+
41+
func testDuplicateValuesInsideTheSameEnumDefinition() throws {
42+
try assertValidationErrors(
43+
"""
44+
enum SomeEnum {
45+
FOO
46+
BAR
47+
FOO
48+
}
49+
""",
50+
[
51+
GraphQLError(
52+
message: #"Enum value "SomeEnum.FOO" can only be defined once."#,
53+
locations: [
54+
.init(line: 2, column: 3),
55+
.init(line: 4, column: 3),
56+
]
57+
),
58+
]
59+
)
60+
}
61+
62+
func testExtendEnumWithNewValue() throws {
63+
try assertValidationErrors(
64+
"""
65+
enum SomeEnum {
66+
FOO
67+
}
68+
extend enum SomeEnum {
69+
BAR
70+
}
71+
extend enum SomeEnum {
72+
BAZ
73+
}
74+
""",
75+
[]
76+
)
77+
}
78+
79+
func testExtendEnumWithDuplicateValue() throws {
80+
try assertValidationErrors(
81+
"""
82+
extend enum SomeEnum {
83+
FOO
84+
}
85+
enum SomeEnum {
86+
FOO
87+
}
88+
""",
89+
[
90+
GraphQLError(
91+
message: #"Enum value "SomeEnum.FOO" can only be defined once."#,
92+
locations: [
93+
.init(line: 2, column: 3),
94+
.init(line: 5, column: 3),
95+
]
96+
),
97+
]
98+
)
99+
}
100+
101+
func testDuplicateValueInsideExtension() throws {
102+
try assertValidationErrors(
103+
"""
104+
enum SomeEnum
105+
extend enum SomeEnum {
106+
FOO
107+
BAR
108+
FOO
109+
}
110+
""",
111+
[
112+
GraphQLError(
113+
message: #"Enum value "SomeEnum.FOO" can only be defined once."#,
114+
locations: [
115+
.init(line: 3, column: 3),
116+
.init(line: 5, column: 3),
117+
]
118+
),
119+
]
120+
)
121+
}
122+
123+
func testDuplicateValueInsideDifferentExtension() throws {
124+
try assertValidationErrors(
125+
"""
126+
enum SomeEnum
127+
extend enum SomeEnum {
128+
FOO
129+
}
130+
extend enum SomeEnum {
131+
FOO
132+
}
133+
""",
134+
[
135+
GraphQLError(
136+
message: #"Enum value "SomeEnum.FOO" can only be defined once."#,
137+
locations: [
138+
.init(line: 3, column: 3),
139+
.init(line: 6, column: 3),
140+
]
141+
),
142+
]
143+
)
144+
}
145+
146+
func testAddingNewValueToTheTypeInsideExistingSchema() throws {
147+
let schema = try buildSchema(source: "enum SomeEnum")
148+
let sdl = """
149+
extend enum SomeEnum {
150+
FOO
151+
}
152+
"""
153+
try assertValidationErrors(sdl, schema: schema, [])
154+
}
155+
156+
func testAddingConflictingValueToExistingSchemaTwice() throws {
157+
let schema = try buildSchema(source: """
158+
enum SomeEnum {
159+
FOO
160+
}
161+
""")
162+
let sdl = """
163+
extend enum SomeEnum {
164+
FOO
165+
}
166+
extend enum SomeEnum {
167+
FOO
168+
}
169+
"""
170+
try assertValidationErrors(
171+
sdl,
172+
schema: schema,
173+
[
174+
GraphQLError(
175+
message: #"Enum value "SomeEnum.FOO" already exists in the schema. It cannot also be defined in this type extension."#,
176+
locations: [
177+
.init(line: 2, column: 3),
178+
]
179+
),
180+
GraphQLError(
181+
message: #"Enum value "SomeEnum.FOO" already exists in the schema. It cannot also be defined in this type extension."#,
182+
locations: [
183+
.init(line: 5, column: 3),
184+
]
185+
),
186+
]
187+
)
188+
}
189+
190+
func testAddingEnumValuesToExistingSchemaTwice() throws {
191+
let schema = try buildSchema(source: "enum SomeEnum")
192+
let sdl = """
193+
extend enum SomeEnum {
194+
FOO
195+
}
196+
extend enum SomeEnum {
197+
FOO
198+
}
199+
"""
200+
try assertValidationErrors(
201+
sdl,
202+
schema: schema,
203+
[
204+
GraphQLError(
205+
message: #"Enum value "SomeEnum.FOO" can only be defined once."#,
206+
locations: [
207+
.init(line: 2, column: 3),
208+
.init(line: 5, column: 3),
209+
]
210+
),
211+
]
212+
)
213+
}
214+
}

0 commit comments

Comments
 (0)