Skip to content

Commit 2b3ff39

Browse files
Merge pull request #11 from GraphQLSwift/feat/data-centric-view
Uses `Data` inputs and outputs
2 parents e381e88 + 66e5edd commit 2b3ff39

10 files changed

Lines changed: 210 additions & 241 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/GraphQLTransportWS", 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 GraphQLTransportWS
3131
struct WebSocketMessenger: Messenger {
3232
let websocket: WebSocket
3333

34-
func send<S>(_ message: S) 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/GraphQLTransportWS/Client.swift

Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -39,89 +39,102 @@ public actor Client<InitPayload: Equatable & Codable> {
3939
/// Listen and react to the provided async sequence of server messages. This function will block until the stream is completed.
4040
/// - Parameter incoming: The server message sequence that the client should react to.
4141
public func listen<A: AsyncSequence & Sendable>(to incoming: A) async throws
42-
where A.Element == String {
42+
where A.Element == Data {
4343
for try await message in incoming {
44-
// Detect and ignore error responses.
45-
if message.starts(with: "44") {
46-
// TODO: Determine what to do with returned error messages
44+
try await respond(to: message)
45+
}
46+
}
47+
48+
/// Listen and react to the provided async sequence of server messages. This function will block until the stream is completed.
49+
/// - Parameter incoming: The server message sequence that the client should react to.
50+
@available(*, deprecated, message: "Use `Data` sequence instead.")
51+
public func listen<A: AsyncSequence & Sendable>(to incoming: A) async throws
52+
where A.Element == String {
53+
for try await stringMessage in incoming {
54+
guard let message = stringMessage.data(using: .utf8) else {
55+
try await self.error(.invalidEncoding())
4756
return
4857
}
58+
try await respond(to: message)
59+
}
60+
}
61+
62+
private func respond(to message: Data) async throws {
63+
let response: Response
64+
do {
65+
response = try decoder.decode(Response.self, from: message)
66+
} catch {
67+
try await self.error(.noType())
68+
return
69+
}
4970

50-
guard let json = message.data(using: .utf8) else {
51-
try await error(.invalidEncoding())
71+
switch response.type {
72+
case .connectionAck:
73+
guard
74+
let connectionAckResponse = try? decoder.decode(
75+
ConnectionAckResponse.self,
76+
from: message
77+
)
78+
else {
79+
try await error(.invalidResponseFormat(messageType: .connectionAck))
5280
return
5381
}
54-
55-
let response: Response
56-
do {
57-
response = try decoder.decode(Response.self, from: json)
58-
} catch {
59-
try await self.error(.noType())
82+
try await onConnectionAck(connectionAckResponse, self)
83+
case .next:
84+
guard let nextResponse = try? decoder.decode(NextResponse.self, from: message) else {
85+
try await error(.invalidResponseFormat(messageType: .next))
6086
return
6187
}
62-
63-
switch response.type {
64-
case .connectionAck:
65-
guard
66-
let connectionAckResponse = try? decoder.decode(
67-
ConnectionAckResponse.self,
68-
from: json
69-
)
70-
else {
71-
try await error(.invalidResponseFormat(messageType: .connectionAck))
72-
return
73-
}
74-
try await onConnectionAck(connectionAckResponse, self)
75-
case .next:
76-
guard let nextResponse = try? decoder.decode(NextResponse.self, from: json) else {
77-
try await error(.invalidResponseFormat(messageType: .next))
78-
return
79-
}
80-
try await onNext(nextResponse, self)
81-
case .error:
82-
guard let errorResponse = try? decoder.decode(ErrorResponse.self, from: json) else {
83-
try await error(.invalidResponseFormat(messageType: .error))
84-
return
85-
}
86-
try await onError(errorResponse, self)
87-
case .complete:
88-
guard let completeResponse = try? decoder.decode(CompleteResponse.self, from: json)
89-
else {
90-
try await error(.invalidResponseFormat(messageType: .complete))
91-
return
92-
}
93-
try await onComplete(completeResponse, self)
94-
default:
95-
try await error(.invalidType())
88+
try await onNext(nextResponse, self)
89+
case .error:
90+
guard let errorResponse = try? decoder.decode(ErrorResponse.self, from: message) else {
91+
try await error(.invalidResponseFormat(messageType: .error))
92+
return
93+
}
94+
try await onError(errorResponse, self)
95+
case .complete:
96+
guard let completeResponse = try? decoder.decode(CompleteResponse.self, from: message)
97+
else {
98+
try await error(.invalidResponseFormat(messageType: .complete))
99+
return
96100
}
101+
try await onComplete(completeResponse, self)
102+
default:
103+
try await error(.invalidType())
97104
}
98105
}
99106

100107
/// Send a `connection_init` request through the messenger
101108
public func sendConnectionInit(payload: InitPayload) async throws {
102109
try await messenger.send(
103-
ConnectionInitRequest(
104-
payload: payload
105-
).toJSON(encoder)
110+
encoder.encode(
111+
ConnectionInitRequest(
112+
payload: payload
113+
)
114+
)
106115
)
107116
}
108117

109118
/// Send a `subscribe` request through the messenger
110119
public func sendStart(payload: GraphQLRequest, id: String) async throws {
111120
try await messenger.send(
112-
SubscribeRequest(
113-
payload: payload,
114-
id: id
115-
).toJSON(encoder)
121+
encoder.encode(
122+
SubscribeRequest(
123+
payload: payload,
124+
id: id
125+
)
126+
)
116127
)
117128
}
118129

119130
/// Send a `complete` request through the messenger
120131
public func sendStop(id: String) async throws {
121132
try await messenger.send(
122-
CompleteRequest(
123-
id: id
124-
).toJSON(encoder)
133+
encoder.encode(
134+
CompleteRequest(
135+
id: id
136+
)
137+
)
125138
)
126139
}
127140

Sources/GraphQLTransportWS/GraphqlTransportWSError.swift

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,105 +9,86 @@ struct GraphQLTransportWSError: Error {
99
self.code = code
1010
}
1111

12-
static func unauthorized() -> Self {
12+
static func forbidden() -> Self {
1313
return self.init(
14-
"Unauthorized",
15-
code: .unauthorized
14+
"Forbidden",
15+
code: .forbidden
1616
)
1717
}
1818

1919
static func notInitialized() -> Self {
2020
return self.init(
2121
"Connection not initialized",
22-
code: .notInitialized
22+
code: .unauthorized
2323
)
2424
}
2525

2626
static func tooManyInitializations() -> Self {
2727
return self.init(
2828
"Too many initialisation requests",
29-
code: .tooManyInitializations
29+
code: .tooManyRequests
3030
)
3131
}
3232

3333
static func subscriberAlreadyExists(id: String) -> Self {
3434
return self.init(
3535
"Subscriber for \(id) already exists",
36-
code: .subscriberAlreadyExists
36+
code: .conflict
3737
)
3838
}
3939

4040
static func invalidEncoding() -> Self {
4141
return self.init(
4242
"Message was not encoded in UTF8",
43-
code: .invalidEncoding
43+
code: .miscellaneous
4444
)
4545
}
4646

4747
static func noType() -> Self {
4848
return self.init(
4949
"Message has no 'type' field",
50-
code: .noType
50+
code: .miscellaneous
5151
)
5252
}
5353

5454
static func invalidType() -> Self {
5555
return self.init(
5656
"Message 'type' value does not match supported types",
57-
code: .invalidType
57+
code: .miscellaneous
5858
)
5959
}
6060

6161
static func invalidRequestFormat(messageType: RequestMessageType) -> Self {
6262
return self.init(
6363
"Request message doesn't match '\(messageType.type.rawValue)' JSON format",
64-
code: .invalidRequestFormat
64+
code: .miscellaneous
6565
)
6666
}
6767

6868
static func invalidResponseFormat(messageType: ResponseMessageType) -> Self {
6969
return self.init(
7070
"Response message doesn't match '\(messageType.type.rawValue)' JSON format",
71-
code: .invalidResponseFormat
71+
code: .miscellaneous
7272
)
7373
}
7474

7575
static func internalAPIStreamIssue(errors: [GraphQLError]) -> Self {
7676
return self.init(
7777
"API Response did not result in a stream type, contained errors\n \(errors.map { $0.message }.joined(separator: "\n"))",
78-
code: .internalAPIStreamIssue
79-
)
80-
}
81-
82-
static func graphQLError(_ error: Error) -> Self {
83-
return self.init(
84-
"\(error)",
85-
code: .graphQLError
78+
code: .internalServerError
8679
)
8780
}
8881
}
8982

9083
/// Error codes for miscellaneous issues
91-
public enum ErrorCode: Int, CustomStringConvertible, Sendable {
84+
enum ErrorCode: Int, CustomStringConvertible, Sendable {
9285
/// Miscellaneous
9386
case miscellaneous = 4400
94-
95-
// Internal errors
96-
case graphQLError = 4401
97-
case internalAPIStreamIssue = 4402
98-
99-
// Message errors
100-
case invalidEncoding = 4410
101-
case noType = 4411
102-
case invalidType = 4412
103-
case invalidRequestFormat = 4413
104-
case invalidResponseFormat = 4414
105-
106-
// Initialization errors
107-
case unauthorized = 4430
108-
case notInitialized = 4431
109-
case tooManyInitializations = 4432
110-
case subscriberAlreadyExists = 4433
87+
case unauthorized = 4401
88+
case forbidden = 4403
89+
case conflict = 4409
90+
case tooManyRequests = 4429
91+
case internalServerError = 4500
11192

11293
public var description: String {
11394
return "\(rawValue)"

Sources/GraphQLTransportWS/JsonEncodable.swift

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

Sources/GraphQLTransportWS/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/GraphQLTransportWS/Requests.swift

Lines changed: 4 additions & 4 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 = .connectionInit
1212
public let payload: InitPayload
1313

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

3232
/// A websocket `subscribe` request from the client to the server
33-
public struct SubscribeRequest: Equatable, JsonEncodable {
33+
public struct SubscribeRequest: Equatable, Codable {
3434
public let type = RequestMessageType.subscribe
3535
public let payload: GraphQLRequest
3636
public let id: String
@@ -56,7 +56,7 @@ public struct SubscribeRequest: Equatable, JsonEncodable {
5656
}
5757

5858
/// A websocket `complete` request from the client to the server
59-
public struct CompleteRequest: Equatable, JsonEncodable {
59+
public struct CompleteRequest: Equatable, Codable {
6060
public let type = RequestMessageType.complete
6161
public let id: String
6262

0 commit comments

Comments
 (0)