Skip to content

Commit 767a2f6

Browse files
Merge pull request #105 from alexsteinerde/custom-validation-rules
Schema validation rules
2 parents d8f83ee + 11e7e09 commit 767a2f6

File tree

4 files changed

+88
-10
lines changed

4 files changed

+88
-10
lines changed

Sources/Graphiti/API/API.swift

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,17 @@ public extension API {
1414
context: ContextType,
1515
on eventLoopGroup: EventLoopGroup,
1616
variables: [String: Map] = [:],
17-
operationName: String? = nil
17+
operationName: String? = nil,
18+
validationRules: [(ValidationContext) -> Visitor] = []
1819
) -> EventLoopFuture<GraphQLResult> {
1920
return schema.execute(
2021
request: request,
2122
resolver: resolver,
2223
context: context,
2324
eventLoopGroup: eventLoopGroup,
2425
variables: variables,
25-
operationName: operationName
26+
operationName: operationName,
27+
validationRules: validationRules
2628
)
2729
}
2830

@@ -31,15 +33,17 @@ public extension API {
3133
context: ContextType,
3234
on eventLoopGroup: EventLoopGroup,
3335
variables: [String: Map] = [:],
34-
operationName: String? = nil
36+
operationName: String? = nil,
37+
validationRules: [(ValidationContext) -> Visitor] = []
3538
) -> EventLoopFuture<SubscriptionResult> {
3639
return schema.subscribe(
3740
request: request,
3841
resolver: resolver,
3942
context: context,
4043
eventLoopGroup: eventLoopGroup,
4144
variables: variables,
42-
operationName: operationName
45+
operationName: operationName,
46+
validationRules: validationRules
4347
)
4448
}
4549
}
@@ -53,15 +57,17 @@ public extension API {
5357
context: ContextType,
5458
on eventLoopGroup: EventLoopGroup,
5559
variables: [String: Map] = [:],
56-
operationName: String? = nil
60+
operationName: String? = nil,
61+
validationRules: [(ValidationContext) -> Visitor] = []
5762
) async throws -> GraphQLResult {
5863
return try await schema.execute(
5964
request: request,
6065
resolver: resolver,
6166
context: context,
6267
eventLoopGroup: eventLoopGroup,
6368
variables: variables,
64-
operationName: operationName
69+
operationName: operationName,
70+
validationRules: validationRules
6571
).get()
6672
}
6773

@@ -71,15 +77,17 @@ public extension API {
7177
context: ContextType,
7278
on eventLoopGroup: EventLoopGroup,
7379
variables: [String: Map] = [:],
74-
operationName: String? = nil
80+
operationName: String? = nil,
81+
validationRules: [(ValidationContext) -> Visitor] = []
7582
) async throws -> SubscriptionResult {
7683
return try await schema.subscribe(
7784
request: request,
7885
resolver: resolver,
7986
context: context,
8087
eventLoopGroup: eventLoopGroup,
8188
variables: variables,
82-
operationName: operationName
89+
operationName: operationName,
90+
validationRules: validationRules
8391
).get()
8492
}
8593
}

Sources/Graphiti/Schema/Schema.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,12 @@ public extension Schema {
6969
context: Context,
7070
eventLoopGroup: EventLoopGroup,
7171
variables: [String: Map] = [:],
72-
operationName: String? = nil
72+
operationName: String? = nil,
73+
validationRules: [(ValidationContext) -> Visitor] = []
7374
) -> EventLoopFuture<GraphQLResult> {
7475
do {
7576
return try graphql(
77+
validationRules: GraphQL.specifiedRules + validationRules,
7678
schema: schema,
7779
request: request,
7880
rootValue: resolver,
@@ -92,10 +94,12 @@ public extension Schema {
9294
context: Context,
9395
eventLoopGroup: EventLoopGroup,
9496
variables: [String: Map] = [:],
95-
operationName: String? = nil
97+
operationName: String? = nil,
98+
validationRules: [(ValidationContext) -> Visitor] = []
9699
) -> EventLoopFuture<SubscriptionResult> {
97100
do {
98101
return try graphqlSubscribe(
102+
validationRules: GraphQL.specifiedRules + validationRules,
99103
schema: schema,
100104
request: request,
101105
rootValue: resolver,
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import GraphQL
2+
3+
public func NoIntrospectionRule(context: ValidationContext) -> Visitor {
4+
return Visitor(enter: { node, _, _, _, _ in
5+
if let field = node as? GraphQL.Field, ["__schema", "__type"].contains(field.name.value) {
6+
context.report(error: .init(message: "GraphQL introspection is not allowed, but the query contained __schema or __type", nodes: [node]))
7+
}
8+
return .continue
9+
})
10+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import Foundation
2+
@testable import Graphiti
3+
import GraphQL
4+
import NIO
5+
import XCTest
6+
7+
class ValidationRulesTests: XCTestCase {
8+
// Test registering custom validation rules
9+
func testRegisteringCustomValidationRule() throws {
10+
struct TestResolver {
11+
var helloWorld: String { "Hellow World" }
12+
}
13+
14+
let testSchema = try Schema<TestResolver, NoContext> {
15+
Query {
16+
Field("helloWorld", at: \.helloWorld)
17+
}
18+
}
19+
let api = TestAPI<TestResolver, NoContext>(
20+
resolver: TestResolver(),
21+
schema: testSchema
22+
)
23+
24+
let group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount)
25+
defer { try? group.syncShutdownGracefully() }
26+
27+
XCTAssertEqual(
28+
try api.execute(
29+
request: """
30+
query {
31+
__type(name: "Query") {
32+
name
33+
description
34+
}
35+
}
36+
""",
37+
context: NoContext(),
38+
on: group,
39+
validationRules: [NoIntrospectionRule]
40+
).wait(),
41+
GraphQLResult(errors: [
42+
.init(message: "GraphQL introspection is not allowed, but the query contained __schema or __type", locations: [.init(line: 2, column: 3)])
43+
])
44+
)
45+
}
46+
}
47+
48+
private class TestAPI<Resolver, ContextType>: API {
49+
public let resolver: Resolver
50+
public let schema: Schema<Resolver, ContextType>
51+
52+
init(resolver: Resolver, schema: Schema<Resolver, ContextType>) {
53+
self.resolver = resolver
54+
self.schema = schema
55+
}
56+
}

0 commit comments

Comments
 (0)