Skip to content

Commit b67bf99

Browse files
feat: Adds UniqueDirectiveNamesRule
1 parent 27e4657 commit b67bf99

File tree

3 files changed

+148
-1
lines changed

3 files changed

+148
-1
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
/**
3+
* Unique directive names
4+
*
5+
* A GraphQL document is only valid if all defined directives have unique names.
6+
*/
7+
func UniqueDirectiveNamesRule(
8+
context: SDLValidationContext
9+
) -> Visitor {
10+
var knownDirectiveNames = [String: Name]()
11+
let schema = context.getSchema()
12+
13+
return Visitor(
14+
enter: { node, _, _, _, _ in
15+
if let node = node as? DirectiveDefinition {
16+
let directiveName = node.name.value
17+
if schema?.getDirective(name: directiveName) != nil {
18+
context.report(
19+
error: GraphQLError(
20+
message: "Directive \"@\(directiveName)\" already exists in the schema. It cannot be redefined.",
21+
nodes: [node.name]
22+
)
23+
)
24+
return .continue
25+
}
26+
if let knownName = knownDirectiveNames[directiveName] {
27+
context.report(
28+
error: GraphQLError(
29+
message: "There can be only one directive named \"@\(directiveName)\".",
30+
nodes: [knownName, node.name]
31+
)
32+
)
33+
} else {
34+
knownDirectiveNames[directiveName] = node.name
35+
}
36+
}
37+
return .continue
38+
}
39+
)
40+
}

Sources/GraphQL/Validation/SpecifiedRules.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public let specifiedSDLRules: [SDLValidationRule] = [
4343
UniqueEnumValueNamesRule,
4444
UniqueFieldDefinitionNamesRule,
4545
UniqueArgumentDefinitionNamesRule,
46-
// UniqueDirectiveNamesRule,
46+
UniqueDirectiveNamesRule,
4747
KnownTypeNamesRule,
4848
KnownDirectivesRule,
4949
UniqueDirectivesPerLocationRule,
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
@testable import GraphQL
2+
import XCTest
3+
4+
class UniqueDirectiveNamesRuleTests: SDLValidationTestCase {
5+
override func setUp() {
6+
rule = UniqueDirectiveNamesRule
7+
}
8+
9+
func testNoDirective() throws {
10+
try assertValidationErrors(
11+
"""
12+
type Foo
13+
""",
14+
[]
15+
)
16+
}
17+
18+
func testOneDirective() throws {
19+
try assertValidationErrors(
20+
"""
21+
directive @foo on SCHEMA
22+
""",
23+
[]
24+
)
25+
}
26+
27+
func testManyDirectives() throws {
28+
try assertValidationErrors(
29+
"""
30+
directive @foo on SCHEMA
31+
directive @bar on SCHEMA
32+
directive @baz on SCHEMA
33+
""",
34+
[]
35+
)
36+
}
37+
38+
func testDirectiveAndNonDirectiveDefinitionsNamedTheSame() throws {
39+
try assertValidationErrors(
40+
"""
41+
query foo { __typename }
42+
fragment foo on foo { __typename }
43+
type foo
44+
45+
directive @foo on SCHEMA
46+
""",
47+
[]
48+
)
49+
}
50+
51+
func testDirectivesNamedTheSame() throws {
52+
try assertValidationErrors(
53+
"""
54+
directive @foo on SCHEMA
55+
56+
directive @foo on SCHEMA
57+
""",
58+
[
59+
GraphQLError(
60+
message: #"There can be only one directive named "@foo"."#,
61+
locations: [
62+
.init(line: 1, column: 12),
63+
.init(line: 3, column: 12),
64+
]
65+
),
66+
]
67+
)
68+
}
69+
70+
func testAddingNewDirectiveToExistingSchema() throws {
71+
let schema = try buildSchema(source: "directive @foo on SCHEMA")
72+
try assertValidationErrors("directive @bar on SCHEMA", schema: schema, [])
73+
}
74+
75+
func testAddingNewDirectiveWithStandardNameToExistingSchema() throws {
76+
let schema = try buildSchema(source: "type foo")
77+
try assertValidationErrors(
78+
"directive @skip on SCHEMA",
79+
schema: schema,
80+
[
81+
GraphQLError(
82+
message: #"Directive "@skip" already exists in the schema. It cannot be redefined."#,
83+
locations: [.init(line: 1, column: 12)]
84+
),
85+
]
86+
)
87+
}
88+
89+
func testAddingNewDirectiveToExistingSchemaWithSameNamedType() throws {
90+
let schema = try buildSchema(source: "type foo")
91+
try assertValidationErrors("directive @foo on SCHEMA", schema: schema, [])
92+
}
93+
94+
func testAddingConflictingDirectiveToExistingSchema() throws {
95+
let schema = try buildSchema(source: "directive @foo on SCHEMA")
96+
try assertValidationErrors(
97+
"directive @foo on SCHEMA",
98+
schema: schema,
99+
[
100+
GraphQLError(
101+
message: #"Directive "@foo" already exists in the schema. It cannot be redefined."#,
102+
locations: [.init(line: 1, column: 12)]
103+
),
104+
]
105+
)
106+
}
107+
}

0 commit comments

Comments
 (0)