Skip to content

Commit 8dd79b2

Browse files
feat: Improve formatting error reporting
1 parent 6054614 commit 8dd79b2

4 files changed

Lines changed: 95 additions & 25 deletions

File tree

Sources/GraphQLTransportWS/Client.swift

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,32 +56,41 @@ public actor Client<InitPayload: Equatable & Codable> {
5656

5757
switch response.type {
5858
case .connectionAck:
59-
guard
60-
let connectionAckResponse = try? decoder.decode(
59+
let connectionAckResponse: ConnectionAckResponse
60+
do {
61+
connectionAckResponse = try decoder.decode(
6162
ConnectionAckResponse.self,
6263
from: message
6364
)
64-
else {
65-
try await messenger.error(.invalidResponseFormat(messageType: .connectionAck))
65+
} catch {
66+
try await messenger.error(.invalidResponseFormat(messageType: .connectionAck, error: error))
6667
return
6768
}
6869
try await onConnectionAck(connectionAckResponse, self)
6970
case .next:
70-
guard let nextResponse = try? decoder.decode(NextResponse.self, from: message) else {
71-
try await messenger.error(.invalidResponseFormat(messageType: .next))
71+
let nextResponse: NextResponse
72+
do {
73+
nextResponse = try decoder.decode(NextResponse.self, from: message)
74+
} catch {
75+
try await messenger.error(.invalidResponseFormat(messageType: .next, error: error))
7276
return
7377
}
7478
try await onNext(nextResponse, self)
7579
case .error:
76-
guard let errorResponse = try? decoder.decode(ErrorResponse.self, from: message) else {
77-
try await messenger.error(.invalidResponseFormat(messageType: .error))
80+
let errorResponse: ErrorResponse
81+
do {
82+
errorResponse = try decoder.decode(ErrorResponse.self, from: message)
83+
} catch {
84+
try await messenger.error(.invalidResponseFormat(messageType: .error, error: error))
7885
return
7986
}
8087
try await onError(errorResponse, self)
8188
case .complete:
82-
guard let completeResponse = try? decoder.decode(CompleteResponse.self, from: message)
83-
else {
84-
try await messenger.error(.invalidResponseFormat(messageType: .complete))
89+
let completeResponse: CompleteResponse
90+
do {
91+
completeResponse = try decoder.decode(CompleteResponse.self, from: message)
92+
} catch {
93+
try await messenger.error(.invalidResponseFormat(messageType: .complete, error: error))
8594
return
8695
}
8796
try await onComplete(completeResponse, self)

Sources/GraphQLTransportWS/GraphqlTransportWSError.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,16 @@ struct GraphQLTransportWSError: Error {
5858
)
5959
}
6060

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

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

Sources/GraphQLTransportWS/Server.swift

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,27 +77,32 @@ where
7777
// handle incoming message
7878
switch request.type {
7979
case .connectionInit:
80-
guard
81-
let connectionInitRequest = try? decoder.decode(
80+
let connectionInitRequest: ConnectionInitRequest<InitPayload>
81+
do {
82+
connectionInitRequest = try decoder.decode(
8283
ConnectionInitRequest<InitPayload>.self,
8384
from: message
8485
)
85-
else {
86-
try await messenger.error(.invalidRequestFormat(messageType: .connectionInit))
86+
} catch {
87+
try await messenger.error(.invalidRequestFormat(messageType: .connectionInit, error: error))
8788
return
8889
}
8990
try await onConnectionInit(connectionInitRequest, messenger)
9091
case .subscribe:
91-
guard let subscribeRequest = try? decoder.decode(SubscribeRequest.self, from: message)
92-
else {
93-
try await messenger.error(.invalidRequestFormat(messageType: .subscribe))
92+
let subscribeRequest: SubscribeRequest
93+
do {
94+
subscribeRequest = try decoder.decode(SubscribeRequest.self, from: message)
95+
} catch {
96+
try await messenger.error(.invalidRequestFormat(messageType: .subscribe, error: error))
9497
return
9598
}
9699
try await onSubscribe(subscribeRequest)
97100
case .complete:
98-
guard let completeRequest = try? decoder.decode(CompleteRequest.self, from: message)
99-
else {
100-
try await messenger.error(.invalidRequestFormat(messageType: .complete))
101+
let completeRequest: CompleteRequest
102+
do {
103+
completeRequest = try decoder.decode(CompleteRequest.self, from: message)
104+
} catch {
105+
try await messenger.error(.invalidRequestFormat(messageType: .complete, error: error))
101106
return
102107
}
103108
try await onOperationComplete(completeRequest)

Tests/GraphQLTransportWSTests/GraphQLTransportWSTests.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,62 @@ struct GraphqlTransportWSTests {
268268
)
269269
}
270270

271+
/// Tests malformed requests include decoder details in the transport error
272+
@Test func malformedRequestIncludesDecodingDetails() async throws {
273+
let api = TestAPI()
274+
let context = TestContext()
275+
let server = Server<TokenInitPayload, Void, AsyncThrowingStream<GraphQLResult, Error>>(
276+
messenger: serverMessenger,
277+
onInit: { _ in },
278+
onExecute: { graphQLRequest, _ in
279+
try await api.execute(
280+
request: graphQLRequest.query,
281+
context: context
282+
)
283+
},
284+
onSubscribe: { graphQLRequest, _ in
285+
try await api.subscribe(
286+
request: graphQLRequest.query,
287+
context: context
288+
).get()
289+
}
290+
)
291+
let (incoming, continuation) = AsyncThrowingStream<Data, any Error>.makeStream()
292+
293+
continuation.yield(Data(#"{"type":"complete"}"#.utf8))
294+
continuation.finish()
295+
296+
try await server.listen(to: incoming)
297+
298+
let error = await #expect(throws: TestMessengerError.self) {
299+
for try await _ in serverMessenger.stream {}
300+
}
301+
#expect(error?.code == 4400)
302+
#expect(error?.message.contains("Request message doesn't match 'complete' JSON format") == true)
303+
#expect(error?.message.contains("keyNotFound") == true)
304+
#expect(error?.message.contains(#""id""#) == true)
305+
}
306+
307+
/// Tests malformed responses include decoder details in the transport error
308+
@Test func malformedResponseIncludesDecodingDetails() async throws {
309+
let messenger = TestMessenger()
310+
let client = Client<TokenInitPayload>(messenger: messenger)
311+
let (incoming, continuation) = AsyncThrowingStream<Data, any Error>.makeStream()
312+
313+
continuation.yield(Data(#"{"type":"next"}"#.utf8))
314+
continuation.finish()
315+
316+
try await client.listen(to: incoming)
317+
318+
let error = await #expect(throws: TestMessengerError.self) {
319+
for try await _ in messenger.stream {}
320+
}
321+
#expect(error?.code == 4400)
322+
#expect(error?.message.contains("Response message doesn't match 'next' JSON format") == true)
323+
#expect(error?.message.contains("keyNotFound") == true)
324+
#expect(error?.message.contains(#""id""#) == true)
325+
}
326+
271327
enum TestError: Error {
272328
case couldBeAnything
273329
}

0 commit comments

Comments
 (0)