Skip to content

Commit 0e41379

Browse files
feat: Adds KnownArgumentNamesOnDirectivesRule
1 parent 1476abd commit 0e41379

File tree

3 files changed

+182
-1
lines changed

3 files changed

+182
-1
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
func KnownArgumentNamesOnDirectivesRule(
2+
context: SDLorNormalValidationContext
3+
) -> Visitor {
4+
var directiveArgs = [String: [String]]()
5+
6+
let schema = context.getSchema()
7+
let definedDirectives = schema?.directives ?? specifiedDirectives
8+
for directive in definedDirectives {
9+
directiveArgs[directive.name] = directive.args.map(\.name)
10+
}
11+
12+
let astDefinitions = context.ast.definitions
13+
for def in astDefinitions {
14+
if let def = def as? DirectiveDefinition {
15+
let argsNodes = def.arguments
16+
directiveArgs[def.name.value] = argsNodes.map(\.name.value)
17+
}
18+
}
19+
20+
return Visitor(
21+
enter: { node, _, _, _, _ in
22+
if let directiveNode = node as? Directive {
23+
let directiveName = directiveNode.name.value
24+
let knownArgs = directiveArgs[directiveName]
25+
26+
if let knownArgs = knownArgs {
27+
for argNode in directiveNode.arguments {
28+
let argName = argNode.name.value
29+
if !knownArgs.contains(argName) {
30+
let suggestions = suggestionList(input: argName, options: knownArgs)
31+
context.report(
32+
error: GraphQLError(
33+
message: "Unknown argument \"\(argName)\" on directive \"@\(directiveName)\"." +
34+
didYouMean(suggestions: suggestions),
35+
nodes: [argNode]
36+
)
37+
)
38+
}
39+
}
40+
}
41+
}
42+
return .continue
43+
}
44+
)
45+
}

Sources/GraphQL/Validation/SpecifiedRules.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public let specifiedSDLRules: [SDLValidationRule] = [
4848
KnownDirectivesRule,
4949
UniqueDirectivesPerLocationRule,
5050
PossibleTypeExtensionsRule,
51-
// KnownArgumentNamesOnDirectivesRule,
51+
KnownArgumentNamesOnDirectivesRule,
5252
UniqueArgumentNamesRule,
5353
UniqueInputFieldNamesRule,
5454
// ProvidedRequiredArgumentsOnDirectivesRule,
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
@testable import GraphQL
2+
import XCTest
3+
4+
class KnownArgumentNamesOnDirectivesRuleTests: SDLValidationTestCase {
5+
override func setUp() {
6+
rule = KnownArgumentNamesOnDirectivesRule
7+
}
8+
9+
func testKnownArgOnDirectiveDefinedInsideSDL() throws {
10+
try assertValidationErrors(
11+
"""
12+
type Query {
13+
foo: String @test(arg: "")
14+
}
15+
16+
directive @test(arg: String) on FIELD_DEFINITION
17+
""",
18+
[]
19+
)
20+
}
21+
22+
func testUnknownArgOnDirectiveDefinedInsideSDL() throws {
23+
try assertValidationErrors(
24+
"""
25+
type Query {
26+
foo: String @test(unknown: "")
27+
}
28+
29+
directive @test(arg: String) on FIELD_DEFINITION
30+
""",
31+
[
32+
GraphQLError(
33+
message: #"Unknown argument "unknown" on directive "@test"."#,
34+
locations: [.init(line: 2, column: 21)]
35+
),
36+
]
37+
)
38+
}
39+
40+
func testMisspelledArgNameIsReportedOnDirectiveDefinedInsideSDL() throws {
41+
try assertValidationErrors(
42+
"""
43+
type Query {
44+
foo: String @test(agr: "")
45+
}
46+
47+
directive @test(arg: String) on FIELD_DEFINITION
48+
""",
49+
[
50+
GraphQLError(
51+
message: #"Unknown argument "agr" on directive "@test". Did you mean "arg"?"#,
52+
locations: [.init(line: 2, column: 21)]
53+
),
54+
]
55+
)
56+
}
57+
58+
func testUnknownArgOnStandardDirective() throws {
59+
try assertValidationErrors(
60+
"""
61+
type Query {
62+
foo: String @deprecated(unknown: "")
63+
}
64+
""",
65+
[
66+
GraphQLError(
67+
message: #"Unknown argument "unknown" on directive "@deprecated"."#,
68+
locations: [.init(line: 2, column: 27)]
69+
),
70+
]
71+
)
72+
}
73+
74+
func testUnknownArgOnOverriddenStandardDirective() throws {
75+
try assertValidationErrors(
76+
"""
77+
type Query {
78+
foo: String @deprecated(reason: "")
79+
}
80+
directive @deprecated(arg: String) on FIELD
81+
""",
82+
[
83+
GraphQLError(
84+
message: #"Unknown argument "reason" on directive "@deprecated"."#,
85+
locations: [.init(line: 2, column: 27)]
86+
),
87+
]
88+
)
89+
}
90+
91+
func testUnknownArgOnDirectiveDefinedInSchemaExtension() throws {
92+
let schema = try buildSchema(source: """
93+
type Query {
94+
foo: String
95+
}
96+
""")
97+
let sdl = """
98+
directive @test(arg: String) on OBJECT
99+
100+
extend type Query @test(unknown: "")
101+
"""
102+
try assertValidationErrors(
103+
sdl,
104+
schema: schema,
105+
[
106+
GraphQLError(
107+
message: #"Unknown argument "unknown" on directive "@test"."#,
108+
locations: [.init(line: 3, column: 26)]
109+
),
110+
]
111+
)
112+
}
113+
114+
func testUnknownArgOnDirectiveUsedInSchemaExtension() throws {
115+
let schema = try buildSchema(source: """
116+
directive @test(arg: String) on OBJECT
117+
118+
type Query {
119+
foo: String
120+
}
121+
""")
122+
let sdl = """
123+
extend type Query @test(unknown: "")
124+
"""
125+
try assertValidationErrors(
126+
sdl,
127+
schema: schema,
128+
[
129+
GraphQLError(
130+
message: #"Unknown argument "unknown" on directive "@test"."#,
131+
locations: [.init(line: 1, column: 25)]
132+
),
133+
]
134+
)
135+
}
136+
}

0 commit comments

Comments
 (0)