Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions sentry_sdk/data_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@

# ``http_bodies`` defaults to this (collect everything the
# platform supports); an empty list is the explicit opt-out.
# response bodyies are not included here because we don't
# currently capture them (as of Jul 7 2026)
# ``incoming_response`` is not included here because we don't
# currently capture incoming response bodies.
_ALL_HTTP_BODY_TYPES = [
"incoming_request",
"outgoing_request",
"outgoing_response",
]

# Default number of source lines captured above and below a stack frame.
Expand Down
11 changes: 10 additions & 1 deletion sentry_sdk/integrations/ariadne.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,17 @@ def _make_response_event_processor(response: "Dict[str, Any]") -> "EventProcesso
"""Add response data to the event's response context."""

def inner(event: "Event", hint: "dict[str, Any]") -> "Event":
client_options = sentry_sdk.get_client().options
with capture_internal_exceptions():
if should_send_default_pii() and response.get("errors"):
if has_data_collection_enabled(client_options):
collect_response = (
"outgoing_response"
in client_options["data_collection"]["http_bodies"]
)
else:
collect_response = should_send_default_pii()

if collect_response and response.get("errors"):
contexts = event.setdefault("contexts", {})
contexts["response"] = {
"data": response,
Expand Down
11 changes: 10 additions & 1 deletion sentry_sdk/integrations/strawberry.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,8 +532,17 @@ def _make_response_event_processor(
response_data: "GraphQLHTTPResponse",
) -> "EventProcessor":
def inner(event: "Event", hint: "dict[str, Any]") -> "Event":
client_options = sentry_sdk.get_client().options
with capture_internal_exceptions():
if should_send_default_pii():
if has_data_collection_enabled(client_options):
collect_response = (
"outgoing_response"
in client_options["data_collection"]["http_bodies"]
)
else:
collect_response = should_send_default_pii()

if collect_response:
contexts = event.setdefault("contexts", {})
contexts["response"] = {"data": response_data}

Expand Down
32 changes: 28 additions & 4 deletions tests/integrations/ariadne/test_ariadne.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,10 +411,8 @@ def test_request_data_collection(
assert ("query" in event["request"]["data"]) == expect_query
assert ("variables" in event["request"]["data"]) == expect_variables

# Response body capture is intentionally tied to send_default_pii only.
assert ("response" in event["contexts"]) == bool(
init_kwargs.get("send_default_pii")
)
# ``http_bodies`` defaults to collecting the outgoing response.
assert "response" in event["contexts"]


def test_request_data_collection_body_out_of_bounds_still_collects_variables(
Expand Down Expand Up @@ -443,3 +441,29 @@ def test_request_data_collection_body_out_of_bounds_still_collects_variables(

assert "query" not in event["request"]["data"]
assert event["request"]["data"]["variables"] == {"name": "some name"}


@pytest.mark.parametrize(
"http_bodies,expect_response",
[
pytest.param(None, True, id="http_bodies_default"),
pytest.param(["outgoing_response"], True, id="outgoing_response"),
pytest.param(["incoming_request"], False, id="incoming_request_only"),
pytest.param([], False, id="http_bodies_off"),
],
)
def test_response_data_collection(
sentry_init, capture_events, graphql_client, http_bodies, expect_response
):
data_collection = {} if http_bodies is None else {"http_bodies": http_bodies}
_init_all_integrations(
sentry_init, _experiments={"data_collection": data_collection}
)
events = capture_events()

graphql_client().post("/graphql", json={"query": "query ErrorQuery {error}"})

assert len(events) == 1
(event,) = events

assert ("response" in event["contexts"]) == expect_response
44 changes: 44 additions & 0 deletions tests/integrations/strawberry/test_strawberry.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,50 @@ def test_event_processor_data_collection(
assert "api_target" not in error_event.get("request", {})


@pytest.mark.parametrize(
"http_bodies,expect_response",
[
pytest.param(None, True, id="http_bodies_default"),
pytest.param(["outgoing_response"], True, id="outgoing_response"),
pytest.param(["incoming_request"], False, id="incoming_request_only"),
pytest.param([], False, id="http_bodies_off"),
],
)
@parameterize_strawberry_test
def test_response_data_collection(
request,
sentry_init,
capture_events,
client_factory,
async_execution,
framework_integrations,
http_bodies,
expect_response,
):
data_collection = {} if http_bodies is None else {"http_bodies": http_bodies}
sentry_init(
integrations=[StrawberryIntegration(async_execution=async_execution)]
+ framework_integrations,
_experiments={"data_collection": data_collection},
)
events = capture_events()

schema = strawberry.Schema(Query)

client_factory = request.getfixturevalue(client_factory)
client = client_factory(schema)

client.post(
"/graphql",
json={"query": "query ErrorQuery { error }", "operationName": "ErrorQuery"},
)

assert len(events) == 1
(error_event,) = events

assert ("response" in error_event["contexts"]) == expect_response


@pytest.mark.parametrize(
"data_collection,expect_query,expect_variables",
[
Expand Down
Loading