From 57bf42a2da60c2535ad591c8ad86e43b5efd8a41 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 10 Feb 2025 10:56:14 +0100 Subject: [PATCH 1/8] test From 081df7ea12c91521de832cb15b6a823d05d26436 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Mon, 10 Feb 2025 16:29:33 +0100 Subject: [PATCH 2/8] wip --- sentry_sdk/integrations/strawberry.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/sentry_sdk/integrations/strawberry.py b/sentry_sdk/integrations/strawberry.py index d27e0eaf1c..e591373d69 100644 --- a/sentry_sdk/integrations/strawberry.py +++ b/sentry_sdk/integrations/strawberry.py @@ -322,6 +322,20 @@ def _sentry_patched_execute_sync(*args, **kwargs): strawberry_schema.execute_sync = _sentry_patched_execute_sync +def _patch_execute_new(): + old_execute_sync = strawberry_schema.Schema.execute_sync + old_execute_async = strawberry_schema.Schema.execute + + def _sentry_patched_execute_sync(self, query, variable_values, context_value, root_value, operation_name, *args, **kwargs): + # type: (strawberry_schema.Schema, Optional[str], Optional[dict[str, Any]], Optional[Any], Optional[Any], Optional[str]) -> ExecutionResult + pass + + def _sentry_patched_execute_async(...): + pass + + strawberry_schema.Schema.execute_sync = _sentry_patched_execute_sync + strawberry_schema.Schema.execute = _sentry_patched_execute_async + def _patch_views(): # type: () -> None old_async_view_handle_errors = async_base_view.AsyncBaseHTTPView._handle_errors From 83565eb3d0817d61905d7200d194c249df20855b Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 11 Feb 2025 12:07:20 +0100 Subject: [PATCH 3/8] fix(strawberry): Patch Schema.execute instead of execute --- sentry_sdk/integrations/strawberry.py | 113 ++++++++++++++++---------- 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/sentry_sdk/integrations/strawberry.py b/sentry_sdk/integrations/strawberry.py index e591373d69..336a3862fb 100644 --- a/sentry_sdk/integrations/strawberry.py +++ b/sentry_sdk/integrations/strawberry.py @@ -47,10 +47,10 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from typing import Any, Callable, Generator, List, Optional, Union + from typing import Any, Callable, Generator, List, Optional from graphql import GraphQLError, GraphQLResolveInfo # type: ignore from strawberry.http import GraphQLHTTPResponse - from strawberry.types import ExecutionContext, ExecutionResult, SubscriptionExecutionResult # type: ignore + from strawberry.types import ExecutionContext, ExecutionResult # type: ignore from sentry_sdk._types import Event, EventProcessor @@ -78,7 +78,7 @@ def setup_once(): _check_minimum_version(StrawberryIntegration, version, "strawberry-graphql") _patch_schema_init() - _patch_execute() + _patch_schema_execute() _patch_views() @@ -287,55 +287,80 @@ def resolve(self, _next, root, info, *args, **kwargs): return _next(root, info, *args, **kwargs) -def _patch_execute(): - # type: () -> None - old_execute_async = strawberry_schema.execute - old_execute_sync = strawberry_schema.execute_sync +def _patch_schema_execute(): + old_execute_sync = strawberry_schema.Schema.execute_sync + old_execute_async = strawberry_schema.Schema.execute - async def _sentry_patched_execute_async(*args, **kwargs): - # type: (Any, Any) -> Union[ExecutionResult, SubscriptionExecutionResult] - result = await old_execute_async(*args, **kwargs) + def _sentry_patched_execute_sync( + self, + query, + variable_values, + context_value, + root_value, + operation_name, + *args, + **kwargs, + ): + # type: (strawberry_schema.Schema, Optional[str], Optional[dict[str, Any]], Optional[Any], Optional[Any], Optional[str], Any, Any) -> ExecutionResult + result = old_execute_sync( + self, + query, + variable_values, + context_value, + root_value, + operation_name, + *args, + **kwargs, + ) if sentry_sdk.get_client().get_integration(StrawberryIntegration) is None: return result - if "execution_context" in kwargs: - scope = sentry_sdk.get_isolation_scope() - event_processor = _make_request_event_processor(kwargs["execution_context"]) - scope.add_event_processor(event_processor) - - return result - - @ensure_integration_enabled(StrawberryIntegration, old_execute_sync) - def _sentry_patched_execute_sync(*args, **kwargs): - # type: (Any, Any) -> ExecutionResult - result = old_execute_sync(*args, **kwargs) - - if "execution_context" in kwargs: - scope = sentry_sdk.get_isolation_scope() - event_processor = _make_request_event_processor(kwargs["execution_context"]) - scope.add_event_processor(event_processor) + scope = sentry_sdk.get_isolation_scope() + event_processor = _make_request_event_processor( + query, variable_values, operation_name + ) + scope.add_event_processor(event_processor) return result - strawberry_schema.execute = _sentry_patched_execute_async - strawberry_schema.execute_sync = _sentry_patched_execute_sync - + async def _sentry_patched_execute_async( + self, + query, + variable_values, + context_value, + root_value, + operation_name, + *args, + **kwargs, + ): + # type: (strawberry_schema.Schema, Optional[str], Optional[dict[str, Any]], Optional[Any], Optional[Any], Optional[str], Any, Any) -> ExecutionResult + result = await old_execute_async( + self, + query, + variable_values, + context_value, + root_value, + operation_name, + *args, + **kwargs, + ) -def _patch_execute_new(): - old_execute_sync = strawberry_schema.Schema.execute_sync - old_execute_async = strawberry_schema.Schema.execute + if sentry_sdk.get_client().get_integration(StrawberryIntegration) is None: + return result - def _sentry_patched_execute_sync(self, query, variable_values, context_value, root_value, operation_name, *args, **kwargs): - # type: (strawberry_schema.Schema, Optional[str], Optional[dict[str, Any]], Optional[Any], Optional[Any], Optional[str]) -> ExecutionResult - pass + scope = sentry_sdk.get_isolation_scope() + event_processor = _make_request_event_processor( + query, variable_values, operation_name + ) + scope.add_event_processor(event_processor) - def _sentry_patched_execute_async(...): - pass + return result strawberry_schema.Schema.execute_sync = _sentry_patched_execute_sync strawberry_schema.Schema.execute = _sentry_patched_execute_async + def _patch_views(): # type: () -> None old_async_view_handle_errors = async_base_view.AsyncBaseHTTPView._handle_errors @@ -381,8 +406,8 @@ def _sentry_patched_handle_errors(self, errors, response_data): ) -def _make_request_event_processor(execution_context): - # type: (ExecutionContext) -> EventProcessor +def _make_request_event_processor(query, variables=None, operation_name=None): + # type: (Optional[str], Optional[dict[str, Any]], Optional[str]) -> EventProcessor def inner(event, hint): # type: (Event, dict[str, Any]) -> Event @@ -392,12 +417,12 @@ def inner(event, hint): request_data["api_target"] = "graphql" if not request_data.get("data"): - data = {"query": execution_context.query} + data = {"query": query} - if execution_context.variables: - data["variables"] = execution_context.variables - if execution_context.operation_name: - data["operationName"] = execution_context.operation_name + if variables: + data["variables"] = variables + if operation_name: + data["operationName"] = operation_name request_data["data"] = data From 4d397d141151f454773890ea137123eb6c1ec62e Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 11 Feb 2025 14:48:30 +0100 Subject: [PATCH 4/8] simplify --- sentry_sdk/integrations/strawberry.py | 97 ++++----------------------- 1 file changed, 12 insertions(+), 85 deletions(-) diff --git a/sentry_sdk/integrations/strawberry.py b/sentry_sdk/integrations/strawberry.py index 336a3862fb..768a59d19d 100644 --- a/sentry_sdk/integrations/strawberry.py +++ b/sentry_sdk/integrations/strawberry.py @@ -27,7 +27,6 @@ raise DidNotEnable("strawberry-graphql integration requires Python 3.8 or newer") try: - import strawberry.schema.schema as strawberry_schema # type: ignore from strawberry import Schema from strawberry.extensions import SchemaExtension # type: ignore from strawberry.extensions.tracing.utils import should_skip_tracing as strawberry_should_skip_tracing # type: ignore @@ -50,7 +49,7 @@ from typing import Any, Callable, Generator, List, Optional from graphql import GraphQLError, GraphQLResolveInfo # type: ignore from strawberry.http import GraphQLHTTPResponse - from strawberry.types import ExecutionContext, ExecutionResult # type: ignore + from strawberry.types import ExecutionContext # type: ignore from sentry_sdk._types import Event, EventProcessor @@ -78,7 +77,6 @@ def setup_once(): _check_minimum_version(StrawberryIntegration, version, "strawberry-graphql") _patch_schema_init() - _patch_schema_execute() _patch_views() @@ -180,6 +178,10 @@ def on_operation(self): }, ) + scope = sentry_sdk.get_isolation_scope() + event_processor = _make_request_event_processor(self.execution_context) + scope.add_event_processor(event_processor) + span = sentry_sdk.get_current_span() if span: self.graphql_span = span.start_child( @@ -287,80 +289,6 @@ def resolve(self, _next, root, info, *args, **kwargs): return _next(root, info, *args, **kwargs) -def _patch_schema_execute(): - old_execute_sync = strawberry_schema.Schema.execute_sync - old_execute_async = strawberry_schema.Schema.execute - - def _sentry_patched_execute_sync( - self, - query, - variable_values, - context_value, - root_value, - operation_name, - *args, - **kwargs, - ): - # type: (strawberry_schema.Schema, Optional[str], Optional[dict[str, Any]], Optional[Any], Optional[Any], Optional[str], Any, Any) -> ExecutionResult - result = old_execute_sync( - self, - query, - variable_values, - context_value, - root_value, - operation_name, - *args, - **kwargs, - ) - - if sentry_sdk.get_client().get_integration(StrawberryIntegration) is None: - return result - - scope = sentry_sdk.get_isolation_scope() - event_processor = _make_request_event_processor( - query, variable_values, operation_name - ) - scope.add_event_processor(event_processor) - - return result - - async def _sentry_patched_execute_async( - self, - query, - variable_values, - context_value, - root_value, - operation_name, - *args, - **kwargs, - ): - # type: (strawberry_schema.Schema, Optional[str], Optional[dict[str, Any]], Optional[Any], Optional[Any], Optional[str], Any, Any) -> ExecutionResult - result = await old_execute_async( - self, - query, - variable_values, - context_value, - root_value, - operation_name, - *args, - **kwargs, - ) - - if sentry_sdk.get_client().get_integration(StrawberryIntegration) is None: - return result - - scope = sentry_sdk.get_isolation_scope() - event_processor = _make_request_event_processor( - query, variable_values, operation_name - ) - scope.add_event_processor(event_processor) - - return result - - strawberry_schema.Schema.execute_sync = _sentry_patched_execute_sync - strawberry_schema.Schema.execute = _sentry_patched_execute_async - - def _patch_views(): # type: () -> None old_async_view_handle_errors = async_base_view.AsyncBaseHTTPView._handle_errors @@ -406,8 +334,8 @@ def _sentry_patched_handle_errors(self, errors, response_data): ) -def _make_request_event_processor(query, variables=None, operation_name=None): - # type: (Optional[str], Optional[dict[str, Any]], Optional[str]) -> EventProcessor +def _make_request_event_processor(execution_context): + # type: (ExecutionContext) -> EventProcessor def inner(event, hint): # type: (Event, dict[str, Any]) -> Event @@ -417,12 +345,11 @@ def inner(event, hint): request_data["api_target"] = "graphql" if not request_data.get("data"): - data = {"query": query} - - if variables: - data["variables"] = variables - if operation_name: - data["operationName"] = operation_name + data = {"query": execution_context.query} + if execution_context.variables: + data["variables"] = execution_context.variables + if execution_context.operation_name: + data["operationName"] = execution_context.operation_name request_data["data"] = data From 2051f2331fe10486378fdb36c9a06216126a7523 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 11 Feb 2025 14:56:04 +0100 Subject: [PATCH 5/8] add strawberry to req-linting --- requirements-linting.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-linting.txt b/requirements-linting.txt index 4227acc26a..014e177793 100644 --- a/requirements-linting.txt +++ b/requirements-linting.txt @@ -19,3 +19,4 @@ openfeature-sdk launchdarkly-server-sdk UnleashClient typer +strawberry-graphql From b1e657609121249d4779dd573c0119195b42e2cb Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 11 Feb 2025 15:04:13 +0100 Subject: [PATCH 6/8] some type fixes --- sentry_sdk/integrations/strawberry.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/sentry_sdk/integrations/strawberry.py b/sentry_sdk/integrations/strawberry.py index 768a59d19d..a876fc6a55 100644 --- a/sentry_sdk/integrations/strawberry.py +++ b/sentry_sdk/integrations/strawberry.py @@ -28,14 +28,16 @@ try: from strawberry import Schema - from strawberry.extensions import SchemaExtension # type: ignore - from strawberry.extensions.tracing.utils import should_skip_tracing as strawberry_should_skip_tracing # type: ignore - from strawberry.http import async_base_view, sync_base_view # type: ignore + from strawberry.extensions import SchemaExtension + from strawberry.extensions.tracing.utils import ( + should_skip_tracing as strawberry_should_skip_tracing, + ) + from strawberry.http import async_base_view, sync_base_view except ImportError: raise DidNotEnable("strawberry-graphql is not installed") try: - from strawberry.extensions.tracing import ( # type: ignore + from strawberry.extensions.tracing import ( SentryTracingExtension as StrawberrySentryAsyncExtension, SentryTracingExtensionSync as StrawberrySentrySyncExtension, ) @@ -47,9 +49,9 @@ if TYPE_CHECKING: from typing import Any, Callable, Generator, List, Optional - from graphql import GraphQLError, GraphQLResolveInfo # type: ignore + from graphql import GraphQLError, GraphQLResolveInfo from strawberry.http import GraphQLHTTPResponse - from strawberry.types import ExecutionContext # type: ignore + from strawberry.types import ExecutionContext from sentry_sdk._types import Event, EventProcessor @@ -122,10 +124,10 @@ def _sentry_patched_schema_init(self, *args, **kwargs): return old_schema_init(self, *args, **kwargs) - Schema.__init__ = _sentry_patched_schema_init + Schema.__init__ = _sentry_patched_schema_init # type: ignore[method-assign] -class SentryAsyncExtension(SchemaExtension): # type: ignore +class SentryAsyncExtension(SchemaExtension): def __init__( self, *, @@ -326,10 +328,10 @@ def _sentry_patched_handle_errors(self, errors, response_data): ) sentry_sdk.capture_event(event, hint=hint) - async_base_view.AsyncBaseHTTPView._handle_errors = ( + async_base_view.AsyncBaseHTTPView._handle_errors = ( # type: ignore[method-assign] _sentry_patched_async_view_handle_errors ) - sync_base_view.SyncBaseHTTPView._handle_errors = ( + sync_base_view.SyncBaseHTTPView._handle_errors = ( # type: ignore[method-assign] _sentry_patched_sync_view_handle_errors ) From 22d5839c9e65e337afdb675500c17a11aca795d8 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 11 Feb 2025 15:20:19 +0100 Subject: [PATCH 7/8] more type fixes --- sentry_sdk/integrations/strawberry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/integrations/strawberry.py b/sentry_sdk/integrations/strawberry.py index a876fc6a55..f12019cd60 100644 --- a/sentry_sdk/integrations/strawberry.py +++ b/sentry_sdk/integrations/strawberry.py @@ -140,7 +140,7 @@ def __init__( @cached_property def _resource_name(self): # type: () -> str - query_hash = self.hash_query(self.execution_context.query) + query_hash = self.hash_query(self.execution_context.query) # type: ignore if self.execution_context.operation_name: return "{}:{}".format(self.execution_context.operation_name, query_hash) @@ -347,7 +347,7 @@ def inner(event, hint): request_data["api_target"] = "graphql" if not request_data.get("data"): - data = {"query": execution_context.query} + data = {"query": execution_context.query} # type: dict[str, Any] if execution_context.variables: data["variables"] = execution_context.variables if execution_context.operation_name: From d577e4bb3447dc04319f9c0b9d9290556ecdc79e Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 11 Feb 2025 15:24:02 +0100 Subject: [PATCH 8/8] unused type: ignores --- sentry_sdk/integrations/ariadne.py | 2 +- sentry_sdk/integrations/gql.py | 7 ++++++- sentry_sdk/integrations/graphene.py | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sentry_sdk/integrations/ariadne.py b/sentry_sdk/integrations/ariadne.py index 0336140441..1a95bc0145 100644 --- a/sentry_sdk/integrations/ariadne.py +++ b/sentry_sdk/integrations/ariadne.py @@ -25,7 +25,7 @@ if TYPE_CHECKING: from typing import Any, Dict, List, Optional from ariadne.types import GraphQLError, GraphQLResult, GraphQLSchema, QueryParser # type: ignore - from graphql.language.ast import DocumentNode # type: ignore + from graphql.language.ast import DocumentNode from sentry_sdk._types import Event, EventProcessor diff --git a/sentry_sdk/integrations/gql.py b/sentry_sdk/integrations/gql.py index d5341d2cf6..5f4436f5b2 100644 --- a/sentry_sdk/integrations/gql.py +++ b/sentry_sdk/integrations/gql.py @@ -10,7 +10,12 @@ try: import gql # type: ignore[import-not-found] - from graphql import print_ast, get_operation_ast, DocumentNode, VariableDefinitionNode # type: ignore[import-not-found] + from graphql import ( + print_ast, + get_operation_ast, + DocumentNode, + VariableDefinitionNode, + ) from gql.transport import Transport, AsyncTransport # type: ignore[import-not-found] from gql.transport.exceptions import TransportQueryError # type: ignore[import-not-found] except ImportError: diff --git a/sentry_sdk/integrations/graphene.py b/sentry_sdk/integrations/graphene.py index 198aea50d2..00a8d155d4 100644 --- a/sentry_sdk/integrations/graphene.py +++ b/sentry_sdk/integrations/graphene.py @@ -22,8 +22,8 @@ from collections.abc import Generator from typing import Any, Dict, Union from graphene.language.source import Source # type: ignore - from graphql.execution import ExecutionResult # type: ignore - from graphql.type import GraphQLSchema # type: ignore + from graphql.execution import ExecutionResult + from graphql.type import GraphQLSchema from sentry_sdk._types import Event