Skip to content

Commit 2d6dfa8

Browse files
author
Garrett Moseke
committed
feat: update auth to allow eventloopfuture resolution
1 parent 72be825 commit 2d6dfa8

File tree

2 files changed

+52
-13
lines changed

2 files changed

+52
-13
lines changed

Sources/GraphQLWS/Server.swift

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public class Server<InitPayload: Equatable & Codable> {
1515

1616
let onExecute: (GraphQLRequest) -> EventLoopFuture<GraphQLResult>
1717
let onSubscribe: (GraphQLRequest) -> EventLoopFuture<SubscriptionResult>
18+
var auth: (InitPayload) throws -> EventLoopFuture<Void>
1819

19-
var auth: (InitPayload) throws -> Void = { _ in }
2020
var onExit: () -> Void = { }
2121
var onMessage: (String) -> Void = { _ in }
2222
var onOperationComplete: (String) -> Void = { _ in }
@@ -34,14 +34,17 @@ public class Server<InitPayload: Equatable & Codable> {
3434
/// - messenger: The messenger to bind the server to.
3535
/// - onExecute: Callback run during `start` resolution for non-streaming queries. Typically this is `API.execute`.
3636
/// - onSubscribe: Callback run during `start` resolution for streaming queries. Typically this is `API.subscribe`.
37+
/// - eventLoop: EventLoop on which to perform server operations.
3738
public init(
3839
messenger: Messenger,
3940
onExecute: @escaping (GraphQLRequest) -> EventLoopFuture<GraphQLResult>,
40-
onSubscribe: @escaping (GraphQLRequest) -> EventLoopFuture<SubscriptionResult>
41+
onSubscribe: @escaping (GraphQLRequest) -> EventLoopFuture<SubscriptionResult>,
42+
eventLoop: EventLoop
4143
) {
4244
self.messenger = messenger
4345
self.onExecute = onExecute
4446
self.onSubscribe = onSubscribe
47+
self.auth = { _ in eventLoop.makeSucceededVoidFuture() }
4548

4649
messenger.onReceive { message in
4750
guard let messenger = self.messenger else { return }
@@ -100,10 +103,10 @@ public class Server<InitPayload: Equatable & Codable> {
100103
}
101104
}
102105

103-
/// Define the callback run during `connection_init` resolution that allows authorization using the `payload`.
104-
/// Throw from this closure to indicate that authorization has failed.
106+
/// Define a custom callback run during `connection_init` resolution that allows authorization using the `payload`.
107+
/// Throw or fail the future from this closure to indicate that authorization has failed.
105108
/// - Parameter callback: The callback to assign
106-
public func auth(_ callback: @escaping (InitPayload) throws -> Void) {
109+
public func auth(_ callback: @escaping (InitPayload) throws -> EventLoopFuture<Void>) {
107110
self.auth = callback
108111
}
109112

@@ -138,14 +141,20 @@ public class Server<InitPayload: Equatable & Codable> {
138141
}
139142

140143
do {
141-
try self.auth(connectionInitRequest.payload)
144+
let authResult = try self.auth(connectionInitRequest.payload)
145+
authResult.whenSuccess {
146+
self.initialized = true
147+
self.sendConnectionAck()
148+
}
149+
authResult.whenFailure { error in
150+
self.error(.unauthorized())
151+
return
152+
}
142153
}
143154
catch {
144155
self.error(.unauthorized())
145156
return
146157
}
147-
initialized = true
148-
self.sendConnectionAck()
149158
// TODO: Should we send the `ka` message?
150159
}
151160

Tests/GraphQLWSTests/GraphQLWSTests.swift

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class GraphqlWsTests: XCTestCase {
1212
var clientMessenger: TestMessenger!
1313
var serverMessenger: TestMessenger!
1414
var server: Server<TokenInitPayload>!
15+
var eventLoop: EventLoop!
1516

1617
override func setUp() {
1718
// Point the client and server at each other
@@ -20,7 +21,7 @@ class GraphqlWsTests: XCTestCase {
2021
clientMessenger.other = serverMessenger
2122
serverMessenger.other = clientMessenger
2223

23-
let eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: 1).next()
24+
eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: 1).next()
2425
let api = TestAPI()
2526
let context = TestContext()
2627

@@ -30,16 +31,17 @@ class GraphqlWsTests: XCTestCase {
3031
api.execute(
3132
request: graphQLRequest.query,
3233
context: context,
33-
on: eventLoop
34+
on: self.eventLoop
3435
)
3536
},
3637
onSubscribe: { graphQLRequest in
3738
api.subscribe(
3839
request: graphQLRequest.query,
3940
context: context,
40-
on: eventLoop
41+
on: self.eventLoop
4142
)
42-
}
43+
},
44+
eventLoop: self.eventLoop
4345
)
4446
}
4547

@@ -73,7 +75,7 @@ class GraphqlWsTests: XCTestCase {
7375
}
7476

7577
/// Tests that throwing in the authorization callback forces an unauthorized error
76-
func testAuth() throws {
78+
func testAuthWithThrow() throws {
7779
server.auth { payload in
7880
throw TestError.couldBeAnything
7981
}
@@ -100,6 +102,34 @@ class GraphqlWsTests: XCTestCase {
100102
)
101103
}
102104

105+
/// Tests that failing a future in the authorization callback forces an unauthorized error
106+
func testAuthWithFailedFuture() throws {
107+
server.auth { payload in
108+
self.eventLoop.makeFailedFuture(TestError.couldBeAnything)
109+
}
110+
111+
var messages = [String]()
112+
let completeExpectation = XCTestExpectation()
113+
114+
let client = Client<TokenInitPayload>(messenger: clientMessenger)
115+
client.onMessage { message, _ in
116+
messages.append(message)
117+
completeExpectation.fulfill()
118+
}
119+
120+
client.sendConnectionInit(
121+
payload: TokenInitPayload(
122+
authToken: ""
123+
)
124+
)
125+
126+
wait(for: [completeExpectation], timeout: 2)
127+
XCTAssertEqual(
128+
messages,
129+
["\(ErrorCode.unauthorized): Unauthorized"]
130+
)
131+
}
132+
103133
/// Test single op message flow works as expected
104134
func testSingleOp() throws {
105135
let id = UUID().description

0 commit comments

Comments
 (0)