Skip to content

Commit 79c7ca0

Browse files
feature: Adds LoneAnonymousOperationRule
1 parent c2b52db commit 79c7ca0

File tree

3 files changed

+153
-1
lines changed

3 files changed

+153
-1
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
/**
3+
* Lone anonymous operation
4+
*
5+
* A GraphQL document is only valid if when it contains an anonymous operation
6+
* (the query short-hand) that it contains only that one operation definition.
7+
*
8+
* See https://spec.graphql.org/draft/#sec-Lone-Anonymous-Operation
9+
*/
10+
func LoneAnonymousOperationRule(context: ValidationContext) -> Visitor {
11+
var operationCount = 0
12+
return Visitor(
13+
enter: { node, _, _, _, _ in
14+
if let document = node as? Document {
15+
operationCount = document.definitions.filter { $0 is OperationDefinition }.count
16+
return .continue
17+
}
18+
if let operation = node as? OperationDefinition {
19+
if operation.name == nil, operationCount > 1 {
20+
context.report(
21+
error: GraphQLError(
22+
message: "This anonymous operation must be the only defined operation.",
23+
nodes: [operation]
24+
)
25+
)
26+
}
27+
return .continue
28+
}
29+
return .continue
30+
}
31+
)
32+
}

Sources/GraphQL/Validation/SpecifiedRules.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
public let specifiedRules: [(ValidationContext) -> Visitor] = [
55
// ExecutableDefinitionsRule,
66
UniqueOperationNamesRule,
7-
// LoneAnonymousOperationRule,
7+
LoneAnonymousOperationRule,
88
// SingleFieldSubscriptionsRule,
99
// KnownTypeNamesRule,
1010
// FragmentsOnCompositeTypesRule,
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
@testable import GraphQL
2+
import XCTest
3+
4+
class LoneAnonymousOperationRuleTests: ValidationTestCase {
5+
override func setUp() {
6+
rule = LoneAnonymousOperationRule
7+
}
8+
9+
func testNoOperations() throws {
10+
try assertValid(
11+
"""
12+
fragment fragA on Type {
13+
field
14+
}
15+
"""
16+
)
17+
}
18+
19+
func testOneAnonOperation() throws {
20+
try assertValid(
21+
"""
22+
{
23+
field
24+
}
25+
"""
26+
)
27+
}
28+
29+
func testMultipleNamedOperations() throws {
30+
try assertValid(
31+
"""
32+
query Foo {
33+
field
34+
}
35+
36+
query Bar {
37+
field
38+
}
39+
"""
40+
)
41+
}
42+
43+
func testAnonOperationWithFragment() throws {
44+
try assertValid(
45+
"""
46+
{
47+
...Foo
48+
}
49+
fragment Foo on Type {
50+
field
51+
}
52+
"""
53+
)
54+
}
55+
56+
func testMultipleAnonOperations() throws {
57+
let errors = try assertInvalid(
58+
errorCount: 2,
59+
query:
60+
"""
61+
{
62+
fieldA
63+
}
64+
{
65+
fieldB
66+
}
67+
"""
68+
)
69+
try assertValidationError(
70+
error: errors[0],
71+
locations: [(line: 1, column: 1)],
72+
message: "This anonymous operation must be the only defined operation."
73+
)
74+
try assertValidationError(
75+
error: errors[1],
76+
locations: [(line: 4, column: 1)],
77+
message: "This anonymous operation must be the only defined operation."
78+
)
79+
}
80+
81+
func testAnonOperationWithAMutation() throws {
82+
let errors = try assertInvalid(
83+
errorCount: 1,
84+
query:
85+
"""
86+
{
87+
fieldA
88+
}
89+
mutation Foo {
90+
fieldB
91+
}
92+
"""
93+
)
94+
try assertValidationError(
95+
error: errors[0],
96+
locations: [(line: 1, column: 1)],
97+
message: "This anonymous operation must be the only defined operation."
98+
)
99+
}
100+
101+
func testAnonOperationWithASubscription() throws {
102+
let errors = try assertInvalid(
103+
errorCount: 1,
104+
query:
105+
"""
106+
{
107+
fieldA
108+
}
109+
subscription Foo {
110+
fieldB
111+
}
112+
"""
113+
)
114+
try assertValidationError(
115+
error: errors[0],
116+
locations: [(line: 1, column: 1)],
117+
message: "This anonymous operation must be the only defined operation."
118+
)
119+
}
120+
}

0 commit comments

Comments
 (0)