Skip to content

Commit 4d90bf1

Browse files
Merge pull request #10 from GraphQLSwift/feat/data-centric-view
Uses `Data` inputs and outputs
2 parents 93e8e79 + 5f5f416 commit 4d90bf1

10 files changed

Lines changed: 229 additions & 242 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ To use this package, include it in your `Package.swift` dependencies:
2020
.package(url: "https://github.com/GraphQLSwift/GraphQLWS", from: "<version>"),
2121
```
2222

23-
Then create a class to implement the `Messenger` protocol. Here's an example using
23+
Then create a concrete type that conforms to the `Messenger` protocol. Here's an example using
2424
[`WebSocketKit`](https://github.com/vapor/websocket-kit):
2525

2626
```swift
@@ -31,12 +31,12 @@ import GraphQLWS
3131
struct WebSocketMessenger: Messenger {
3232
let websocket: WebSocket
3333

34-
func send<S>(_ message: S) async throws where S: Collection, S.Element == Character async throws {
35-
try await websocket.send(message)
34+
func send(_ message: Data) async throws {
35+
try await websocket.send(String(decoding: message, as: UTF8.self))
3636
}
3737

3838
func error(_ message: String, code: Int) async throws {
39-
try await websocket.send("\(code): \(message)")
39+
try await websocket.close(code: code)
4040
}
4141

4242
func close() async throws {
@@ -73,9 +73,9 @@ routes.webSocket(
7373
)
7474
}
7575
)
76-
let incoming = AsyncStream<String> { continuation in
76+
let incoming = AsyncStream<Data> { continuation in
7777
websocket.onText { _, message in
78-
continuation.yield(message)
78+
continuation.yield(Data(message.utf8))
7979
}
8080
}
8181
try await server.listen(to: incoming)

Sources/GraphQLWS/Client.swift

Lines changed: 94 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -51,118 +51,133 @@ public actor Client<InitPayload: Equatable & Codable> {
5151
/// Listen and react to the provided async sequence of server messages. This function will block until the stream is completed.
5252
/// - Parameter incoming: The server message sequence that the client should react to.
5353
public func listen<A: AsyncSequence & Sendable>(to incoming: A) async throws
54-
where A.Element == String {
54+
where A.Element == Data {
5555
for try await message in incoming {
56-
// Detect and ignore error responses.
57-
if message.starts(with: "44") {
58-
// TODO: Determine what to do with returned error messages
56+
try await respond(to: message)
57+
}
58+
}
59+
60+
/// Listen and react to the provided async sequence of server messages. This function will block until the stream is completed.
61+
/// - Parameter incoming: The server message sequence that the client should react to.
62+
@available(*, deprecated, message: "Use `Data` sequence instead.")
63+
public func listen<A: AsyncSequence & Sendable>(to incoming: A) async throws
64+
where A.Element == String {
65+
for try await stringMessage in incoming {
66+
guard let message = stringMessage.data(using: .utf8) else {
67+
try await self.error(.invalidEncoding())
5968
return
6069
}
70+
try await respond(to: message)
71+
}
72+
}
6173

62-
guard let json = message.data(using: .utf8) else {
63-
try await error(.invalidEncoding())
74+
private func respond(to message: Data) async throws {
75+
let response: Response
76+
do {
77+
response = try decoder.decode(Response.self, from: message)
78+
} catch {
79+
try await self.error(.noType())
80+
return
81+
}
82+
83+
switch response.type {
84+
case .GQL_CONNECTION_ERROR:
85+
guard
86+
let connectionErrorResponse = try? decoder.decode(
87+
ConnectionErrorResponse.self,
88+
from: message
89+
)
90+
else {
91+
try await error(.invalidResponseFormat(messageType: .GQL_CONNECTION_ERROR))
6492
return
6593
}
66-
67-
let response: Response
68-
do {
69-
response = try decoder.decode(Response.self, from: json)
70-
} catch {
71-
try await self.error(.noType())
94+
try await onConnectionError(connectionErrorResponse, self)
95+
case .GQL_CONNECTION_ACK:
96+
guard
97+
let connectionAckResponse = try? decoder.decode(
98+
ConnectionAckResponse.self,
99+
from: message
100+
)
101+
else {
102+
try await error(.invalidResponseFormat(messageType: .GQL_CONNECTION_ERROR))
72103
return
73104
}
74-
75-
switch response.type {
76-
case .GQL_CONNECTION_ERROR:
77-
guard
78-
let connectionErrorResponse = try? decoder.decode(
79-
ConnectionErrorResponse.self,
80-
from: json
81-
)
82-
else {
83-
try await error(.invalidResponseFormat(messageType: .GQL_CONNECTION_ERROR))
84-
return
85-
}
86-
try await onConnectionError(connectionErrorResponse, self)
87-
case .GQL_CONNECTION_ACK:
88-
guard
89-
let connectionAckResponse = try? decoder.decode(
90-
ConnectionAckResponse.self,
91-
from: json
92-
)
93-
else {
94-
try await error(.invalidResponseFormat(messageType: .GQL_CONNECTION_ERROR))
95-
return
96-
}
97-
try await onConnectionAck(connectionAckResponse, self)
98-
case .GQL_CONNECTION_KEEP_ALIVE:
99-
guard
100-
let connectionKeepAliveResponse = try? decoder.decode(
101-
ConnectionKeepAliveResponse.self,
102-
from: json
103-
)
104-
else {
105-
try await error(.invalidResponseFormat(messageType: .GQL_CONNECTION_KEEP_ALIVE))
106-
return
107-
}
108-
try await onConnectionKeepAlive(connectionKeepAliveResponse, self)
109-
case .GQL_DATA:
110-
guard let nextResponse = try? decoder.decode(DataResponse.self, from: json) else {
111-
try await error(.invalidResponseFormat(messageType: .GQL_DATA))
112-
return
113-
}
114-
try await onData(nextResponse, self)
115-
case .GQL_ERROR:
116-
guard let errorResponse = try? decoder.decode(ErrorResponse.self, from: json) else {
117-
try await error(.invalidResponseFormat(messageType: .GQL_ERROR))
118-
return
119-
}
120-
try await onError(errorResponse, self)
121-
case .GQL_COMPLETE:
122-
guard let completeResponse = try? decoder.decode(CompleteResponse.self, from: json)
123-
else {
124-
try await error(.invalidResponseFormat(messageType: .GQL_COMPLETE))
125-
return
126-
}
127-
try await onComplete(completeResponse, self)
128-
default:
129-
try await error(.invalidType())
105+
try await onConnectionAck(connectionAckResponse, self)
106+
case .GQL_CONNECTION_KEEP_ALIVE:
107+
guard
108+
let connectionKeepAliveResponse = try? decoder.decode(
109+
ConnectionKeepAliveResponse.self,
110+
from: message
111+
)
112+
else {
113+
try await error(.invalidResponseFormat(messageType: .GQL_CONNECTION_KEEP_ALIVE))
114+
return
115+
}
116+
try await onConnectionKeepAlive(connectionKeepAliveResponse, self)
117+
case .GQL_DATA:
118+
guard let nextResponse = try? decoder.decode(DataResponse.self, from: message) else {
119+
try await error(.invalidResponseFormat(messageType: .GQL_DATA))
120+
return
121+
}
122+
try await onData(nextResponse, self)
123+
case .GQL_ERROR:
124+
guard let errorResponse = try? decoder.decode(ErrorResponse.self, from: message) else {
125+
try await error(.invalidResponseFormat(messageType: .GQL_ERROR))
126+
return
127+
}
128+
try await onError(errorResponse, self)
129+
case .GQL_COMPLETE:
130+
guard let completeResponse = try? decoder.decode(CompleteResponse.self, from: message)
131+
else {
132+
try await error(.invalidResponseFormat(messageType: .GQL_COMPLETE))
133+
return
130134
}
135+
try await onComplete(completeResponse, self)
136+
default:
137+
try await error(.invalidType())
131138
}
132139
}
133140

134141
/// Send a `connection_init` request through the messenger
135142
public func sendConnectionInit(payload: InitPayload) async throws {
136143
try await messenger.send(
137-
ConnectionInitRequest(
138-
payload: payload
139-
).toJSON(encoder)
144+
encoder.encode(
145+
ConnectionInitRequest(
146+
payload: payload
147+
)
148+
)
140149
)
141150
}
142151

143152
/// Send a `start` request through the messenger
144153
public func sendStart(payload: GraphQLRequest, id: String) async throws {
145154
try await messenger.send(
146-
StartRequest(
147-
payload: payload,
148-
id: id
149-
).toJSON(encoder)
155+
encoder.encode(
156+
StartRequest(
157+
payload: payload,
158+
id: id
159+
)
160+
)
150161
)
151162
}
152163

153164
/// Send a `stop` request through the messenger
154165
public func sendStop(id: String) async throws {
155166
try await messenger.send(
156-
StopRequest(
157-
id: id
158-
).toJSON(encoder)
167+
encoder.encode(
168+
StopRequest(
169+
id: id
170+
)
171+
)
159172
)
160173
}
161174

162175
/// Send a `connection_terminate` request through the messenger
163176
public func sendConnectionTerminate() async throws {
164177
try await messenger.send(
165-
ConnectionTerminateRequest().toJSON(encoder)
178+
encoder.encode(
179+
ConnectionTerminateRequest()
180+
)
166181
)
167182
}
168183

Sources/GraphQLWS/GraphQLWSError.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ struct GraphQLWSError: Error {
8888
}
8989

9090
/// Error codes for miscellaneous issues
91-
public enum ErrorCode: Int, CustomStringConvertible, Sendable {
91+
enum ErrorCode: Int, CustomStringConvertible, Sendable {
9292
/// Miscellaneous
9393
case miscellaneous = 4400
9494

Sources/GraphQLWS/JsonEncodable.swift

Lines changed: 0 additions & 23 deletions
This file was deleted.

Sources/GraphQLWS/Messenger.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Foundation
44
public protocol Messenger: Sendable {
55
/// Send a message through this messenger
66
/// - Parameter message: The message to send
7-
func send<S: Sendable & Collection>(_ message: S) async throws where S.Element == Character
7+
func send(_ message: Data) async throws
88

99
/// Close the messenger
1010
func close() async throws

Sources/GraphQLWS/Requests.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import Foundation
22
import GraphQL
33

44
/// A general request. This object's type is used to triage to other, more specific request objects.
5-
public struct Request: Equatable, JsonEncodable {
5+
public struct Request: Equatable, Codable {
66
public let type: RequestMessageType
77
}
88

99
/// A websocket `connection_init` request from the client to the server
10-
public struct ConnectionInitRequest<InitPayload: Codable & Equatable>: Equatable, JsonEncodable {
10+
public struct ConnectionInitRequest<InitPayload: Codable & Equatable>: Equatable, Codable {
1111
public let type: RequestMessageType = .GQL_CONNECTION_INIT
1212
public let payload: InitPayload
1313

@@ -31,7 +31,7 @@ public struct ConnectionInitRequest<InitPayload: Codable & Equatable>: Equatable
3131
}
3232

3333
/// A websocket `start` request from the client to the server
34-
public struct StartRequest: Equatable, JsonEncodable {
34+
public struct StartRequest: Equatable, Codable {
3535
public let type: RequestMessageType = .GQL_START
3636
public let payload: GraphQLRequest
3737
public let id: String
@@ -57,7 +57,7 @@ public struct StartRequest: Equatable, JsonEncodable {
5757
}
5858

5959
/// A websocket `stop` request from the client to the server
60-
public struct StopRequest: Equatable, JsonEncodable {
60+
public struct StopRequest: Equatable, Codable {
6161
public let type: RequestMessageType = .GQL_STOP
6262
public let id: String
6363

@@ -81,7 +81,7 @@ public struct StopRequest: Equatable, JsonEncodable {
8181
}
8282

8383
/// A websocket `connection_terminate` request from the client to the server
84-
public struct ConnectionTerminateRequest: Equatable, JsonEncodable {
84+
public struct ConnectionTerminateRequest: Equatable, Codable {
8585
public let type: RequestMessageType = .GQL_CONNECTION_TERMINATE
8686

8787
public init() {}

0 commit comments

Comments
 (0)