Skip to content

Commit d3be916

Browse files
authored
Fix errors raising TransportProtocolError with the graphql-ws protocol (#299)
1 parent 0084b95 commit d3be916

File tree

2 files changed

+23
-36
lines changed

2 files changed

+23
-36
lines changed

gql/transport/websockets.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ def _parse_answer_graphqlws(
272272
- instead of a unidirectional keep-alive (ka) message from server to client,
273273
there is now the possibility to send bidirectional ping/pong messages
274274
- connection_ack has an optional payload
275+
- the 'error' answer type returns a list of errors instead of a single error
275276
"""
276277

277278
answer_type: str = ""
@@ -288,11 +289,11 @@ def _parse_answer_graphqlws(
288289

289290
payload = json_answer.get("payload")
290291

291-
if not isinstance(payload, dict):
292-
raise ValueError("payload is not a dict")
293-
294292
if answer_type == "next":
295293

294+
if not isinstance(payload, dict):
295+
raise ValueError("payload is not a dict")
296+
296297
if "errors" not in payload and "data" not in payload:
297298
raise ValueError(
298299
"payload does not contain 'data' or 'errors' fields"
@@ -309,8 +310,11 @@ def _parse_answer_graphqlws(
309310

310311
elif answer_type == "error":
311312

313+
if not isinstance(payload, list):
314+
raise ValueError("payload is not a list")
315+
312316
raise TransportQueryError(
313-
str(payload), query_id=answer_id, errors=[payload]
317+
str(payload[0]), query_id=answer_id, errors=payload
314318
)
315319

316320
elif answer_type in ["ping", "pong", "connection_ack"]:

tests/test_graphqlws_exceptions.py

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import asyncio
2-
import json
3-
import types
42
from typing import List
53

64
import pytest
@@ -125,49 +123,29 @@ async def test_graphqlws_server_does_not_send_ack(
125123
pass
126124

127125

128-
invalid_payload_server_answer = (
129-
'{"type":"error","id":"1","payload":{"message":"Must provide document"}}'
126+
invalid_query_server_answer = (
127+
'{"id":"1","type":"error","payload":[{"message":"Cannot query field '
128+
'\\"helo\\" on type \\"Query\\". Did you mean \\"hello\\"?",'
129+
'"locations":[{"line":2,"column":3}]}]}'
130130
)
131131

132132

133-
async def server_invalid_payload(ws, path):
133+
async def server_invalid_query(ws, path):
134134
await WebSocketServerHelper.send_connection_ack(ws)
135135
result = await ws.recv()
136136
print(f"Server received: {result}")
137-
await ws.send(invalid_payload_server_answer)
137+
await ws.send(invalid_query_server_answer)
138138
await WebSocketServerHelper.wait_connection_terminate(ws)
139139
await ws.wait_closed()
140140

141141

142142
@pytest.mark.asyncio
143-
@pytest.mark.parametrize("graphqlws_server", [server_invalid_payload], indirect=True)
144-
@pytest.mark.parametrize("query_str", [invalid_query_str])
145-
async def test_graphqlws_sending_invalid_payload(
146-
event_loop, client_and_graphqlws_server, query_str
147-
):
143+
@pytest.mark.parametrize("graphqlws_server", [server_invalid_query], indirect=True)
144+
async def test_graphqlws_sending_invalid_query(event_loop, client_and_graphqlws_server):
148145

149146
session, server = client_and_graphqlws_server
150147

151-
# Monkey patching the _send_query method to send an invalid payload
152-
153-
async def monkey_patch_send_query(
154-
self, document, variable_values=None, operation_name=None,
155-
) -> int:
156-
query_id = self.next_query_id
157-
self.next_query_id += 1
158-
159-
query_str = json.dumps(
160-
{"id": str(query_id), "type": "subscribe", "payload": "BLAHBLAH"}
161-
)
162-
163-
await self._send(query_str)
164-
return query_id
165-
166-
session.transport._send_query = types.MethodType(
167-
monkey_patch_send_query, session.transport
168-
)
169-
170-
query = gql(query_str)
148+
query = gql("{helo}")
171149

172150
with pytest.raises(TransportQueryError) as exc_info:
173151
await session.execute(query)
@@ -178,7 +156,10 @@ async def monkey_patch_send_query(
178156

179157
error = exception.errors[0]
180158

181-
assert error["message"] == "Must provide document"
159+
assert (
160+
error["message"]
161+
== 'Cannot query field "helo" on type "Query". Did you mean "hello"?'
162+
)
182163

183164

184165
not_json_answer = ["BLAHBLAH"]
@@ -188,6 +169,7 @@ async def monkey_patch_send_query(
188169
missing_id_answer_3 = ['{"type": "complete"}']
189170
data_without_payload = ['{"type": "next", "id":"1"}']
190171
error_without_payload = ['{"type": "error", "id":"1"}']
172+
error_with_payload_not_a_list = ['{"type": "error", "id":"1", "payload": "NOT A LIST"}']
191173
payload_is_not_a_dict = ['{"type": "next", "id":"1", "payload": "BLAH"}']
192174
empty_payload = ['{"type": "next", "id":"1", "payload": {}}']
193175
sending_bytes = [b"\x01\x02\x03"]
@@ -205,6 +187,7 @@ async def monkey_patch_send_query(
205187
data_without_payload,
206188
error_without_payload,
207189
payload_is_not_a_dict,
190+
error_with_payload_not_a_list,
208191
empty_payload,
209192
sending_bytes,
210193
],

0 commit comments

Comments
 (0)