Skip to content

Commit 764f0c7

Browse files
committed
wip
1 parent 8c96fc0 commit 764f0c7

File tree

3 files changed

+129
-3
lines changed

3 files changed

+129
-3
lines changed

sentry_sdk/integrations/stdlib.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import io
12
import json
23
import os
34
import subprocess
@@ -157,7 +158,9 @@ def getresponse(self, *args, **kwargs):
157158
if getattr(self, "_sentrysdk_is_graphql_request", False):
158159
with capture_internal_exceptions():
159160
try:
160-
response_body = json.loads(rv.read())
161+
response_data = rv.read()
162+
response_body = json.loads(response_data)
163+
rv.read = io.BytesIO(response_data).read
161164
except (json.JSONDecodeError, UnicodeDecodeError):
162165
return rv
163166

@@ -204,7 +207,7 @@ def stdlib_processor(
204207

205208
parsed_url = parse_url(url, sanitize=False)
206209
request_info["query_string"] = parsed_url.query
207-
request_info["url"] = url
210+
request_info["url"] = parsed_url.url
208211
request_info["method"] = method
209212
try:
210213
request_info["data"] = json.loads(request_body)

tests/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,12 @@ def do_GET(self): # noqa: N802
584584
self.end_headers()
585585
return
586586

587+
def do_POST(self): # noqa: N802
588+
# Process an HTTP POST request and return a response with an HTTP 200 status.
589+
self.send_response(200)
590+
self.end_headers()
591+
return
592+
587593

588594
def get_free_port():
589595
s = socket.socket(socket.AF_INET, type=socket.SOCK_STREAM)

tests/integrations/stdlib/test_httplib.py

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import json
12
import random
3+
from textwrap import dedent
4+
from urllib.parse import urlencode
25

36
import pytest
47

@@ -16,6 +19,13 @@
1619
# py3
1720
from http.client import HTTPConnection, HTTPSConnection
1821

22+
try:
23+
# py3
24+
from urllib.parse import parse_qsl
25+
except ImportError:
26+
# py2
27+
from urlparse import parse_qsl # type: ignore
28+
1929
try:
2030
from unittest import mock # python 3.3 and above
2131
except ImportError:
@@ -27,7 +37,7 @@
2737
from sentry_sdk.tracing import Transaction
2838
from sentry_sdk.integrations.stdlib import StdlibIntegration
2939

30-
from tests.conftest import create_mock_http_server
40+
from tests.conftest import MockServerRequestHandler, create_mock_http_server
3141

3242
PORT = create_mock_http_server()
3343

@@ -340,3 +350,110 @@ def test_option_trace_propagation_targets(
340350
else:
341351
assert "sentry-trace" not in request_headers
342352
assert "baggage" not in request_headers
353+
354+
355+
def test_graphql_get_client_error_captured(sentry_init, capture_events):
356+
sentry_init(integrations=[StdlibIntegration()])
357+
358+
graphql_response = {
359+
"data": None,
360+
"errors": [
361+
{
362+
"message": "some error",
363+
"locations": [{"line": 2, "column": 3}],
364+
"path": ["user"],
365+
}
366+
],
367+
}
368+
369+
events = capture_events()
370+
371+
params = {"query": "query QueryName {user{name}}"}
372+
373+
def do_GET(self):
374+
self.send_response(200)
375+
self.end_headers()
376+
self.wfile.write(json.dumps(graphql_response).encode())
377+
378+
with mock.patch.object(MockServerRequestHandler, "do_GET", do_GET):
379+
conn = HTTPConnection("localhost:{}".format(PORT))
380+
conn.request("GET", "/graphql?" + urlencode(params))
381+
response = conn.getresponse()
382+
383+
assert response.read() == json.dumps(graphql_response).encode()
384+
385+
(event,) = events
386+
387+
assert event["request"]["url"] == "http://localhost:{}/graphql".format(PORT)
388+
assert event["request"]["method"] == "GET"
389+
assert dict(parse_qsl(event["request"]["query_string"])) == params
390+
assert "data" not in event["request"]
391+
assert (
392+
event["contexts"]["response"]["data"] == json.dumps(graphql_response).encode()
393+
)
394+
395+
assert event["request"]["api_target"] == "graphql"
396+
assert event["fingerprint"] == ["QueryName", "query", 200]
397+
assert (
398+
event["exception"]["values"][0]["value"]
399+
== "GraphQL request failed, name: QueryName, type: query"
400+
)
401+
402+
403+
def test_graphql_post_client_error_captured(sentry_init, capture_events):
404+
sentry_init(integrations=[StdlibIntegration()])
405+
406+
graphql_request = {
407+
"query": dedent(
408+
"""
409+
mutation AddPet ($name: String!) {
410+
addPet(name: $name) {
411+
id
412+
name
413+
}
414+
}
415+
"""
416+
),
417+
"variables": {
418+
"name": "Lucy",
419+
},
420+
}
421+
graphql_response = {
422+
"data": None,
423+
"errors": [
424+
{
425+
"message": "already have too many pets",
426+
"locations": [{"line": 1, "column": 1}],
427+
}
428+
],
429+
}
430+
431+
events = capture_events()
432+
433+
def do_POST(self):
434+
self.send_response(200)
435+
self.end_headers()
436+
self.wfile.write(json.dumps(graphql_response).encode())
437+
438+
with mock.patch.object(MockServerRequestHandler, "do_POST", do_POST):
439+
conn = HTTPConnection("localhost:{}".format(PORT))
440+
conn.request("POST", "/graphql", body=json.dumps(graphql_request).encode())
441+
response = conn.getresponse()
442+
443+
# the response can still be read normally
444+
assert response.read() == json.dumps(graphql_response).encode()
445+
446+
(event,) = events
447+
448+
assert event["request"]["url"] == "http://localhost:{}/graphql".format(PORT)
449+
assert event["request"]["method"] == "POST"
450+
assert event["request"]["query_string"] == ""
451+
assert event["request"]["data"] == graphql_request
452+
assert event["contexts"]["response"]["data"] == graphql_response
453+
454+
assert event["request"]["api_target"] == "graphql"
455+
assert event["fingerprint"] == ["AddPet", "mutation", 200]
456+
assert (
457+
event["exception"]["values"][0]["value"]
458+
== "GraphQL request failed, name: AddPet, type: mutation"
459+
)

0 commit comments

Comments
 (0)