2016-09-18 02:29:00 +03:00
|
|
|
from django.db import connections
|
|
|
|
|
2023-05-09 23:52:55 +03:00
|
|
|
from asgiref.sync import sync_to_async
|
|
|
|
import inspect
|
2016-09-18 02:29:00 +03:00
|
|
|
from .sql.tracking import unwrap_cursor, wrap_cursor
|
2021-03-02 21:45:46 +03:00
|
|
|
from .exception.formating import wrap_exception
|
2016-09-18 02:29:00 +03:00
|
|
|
from .types import DjangoDebug
|
2023-05-10 21:17:30 +03:00
|
|
|
from graphql.type.definition import GraphQLNonNull
|
|
|
|
|
|
|
|
from django.db.models import QuerySet
|
2016-09-18 02:29:00 +03:00
|
|
|
|
|
|
|
|
2022-10-19 17:10:30 +03:00
|
|
|
class DjangoDebugContext:
|
2016-09-18 02:29:00 +03:00
|
|
|
def __init__(self):
|
2023-02-13 19:19:38 +03:00
|
|
|
self.debug_result = None
|
|
|
|
self.results = []
|
2021-03-02 21:45:46 +03:00
|
|
|
self.object = DjangoDebug(sql=[], exceptions=[])
|
2016-09-18 02:29:00 +03:00
|
|
|
self.enable_instrumentation()
|
|
|
|
|
2023-02-13 19:19:38 +03:00
|
|
|
def get_debug_result(self):
|
|
|
|
if not self.debug_result:
|
|
|
|
self.debug_result = self.results
|
|
|
|
self.results = []
|
|
|
|
return self.on_resolve_all_results()
|
2016-09-18 02:29:00 +03:00
|
|
|
|
2021-03-02 21:45:46 +03:00
|
|
|
def on_resolve_error(self, value):
|
|
|
|
if hasattr(self, "object"):
|
|
|
|
self.object.exceptions.append(wrap_exception(value))
|
2023-02-13 19:19:38 +03:00
|
|
|
return value
|
2021-03-02 21:45:46 +03:00
|
|
|
|
2023-02-13 19:19:38 +03:00
|
|
|
def on_resolve_all_results(self):
|
|
|
|
if self.results:
|
|
|
|
self.debug_result = None
|
|
|
|
return self.get_debug_result()
|
2016-09-18 02:29:00 +03:00
|
|
|
self.disable_instrumentation()
|
|
|
|
return self.object
|
|
|
|
|
2023-02-13 19:19:38 +03:00
|
|
|
def add_result(self, result):
|
|
|
|
if self.debug_result:
|
|
|
|
self.results.append(result)
|
2016-09-18 02:29:00 +03:00
|
|
|
|
|
|
|
def enable_instrumentation(self):
|
|
|
|
# This is thread-safe because database connections are thread-local.
|
|
|
|
for connection in connections.all():
|
|
|
|
wrap_cursor(connection, self)
|
|
|
|
|
|
|
|
def disable_instrumentation(self):
|
|
|
|
for connection in connections.all():
|
|
|
|
unwrap_cursor(connection)
|
|
|
|
|
|
|
|
|
2022-10-19 17:10:30 +03:00
|
|
|
class DjangoDebugMiddleware:
|
2017-07-28 19:43:27 +03:00
|
|
|
def resolve(self, next, root, info, **args):
|
|
|
|
context = info.context
|
2018-07-20 02:51:33 +03:00
|
|
|
django_debug = getattr(context, "django_debug", None)
|
2016-09-18 02:29:00 +03:00
|
|
|
if not django_debug:
|
|
|
|
if context is None:
|
2018-07-20 02:51:33 +03:00
|
|
|
raise Exception("DjangoDebug cannot be executed in None contexts")
|
2016-09-18 02:29:00 +03:00
|
|
|
try:
|
|
|
|
context.django_debug = DjangoDebugContext()
|
|
|
|
except Exception:
|
2018-07-20 02:51:33 +03:00
|
|
|
raise Exception(
|
|
|
|
"DjangoDebug need the context to be writable, context received: {}.".format(
|
|
|
|
context.__class__.__name__
|
|
|
|
)
|
|
|
|
)
|
|
|
|
if info.schema.get_type("DjangoDebug") == info.return_type:
|
2023-02-13 19:19:38 +03:00
|
|
|
return context.django_debug.get_debug_result()
|
2021-03-02 21:45:46 +03:00
|
|
|
try:
|
2023-02-13 19:19:38 +03:00
|
|
|
result = next(root, info, **args)
|
2021-03-02 21:45:46 +03:00
|
|
|
except Exception as e:
|
|
|
|
return context.django_debug.on_resolve_error(e)
|
2023-02-13 19:19:38 +03:00
|
|
|
context.django_debug.add_result(result)
|
|
|
|
return result
|
2023-05-09 23:52:55 +03:00
|
|
|
|
|
|
|
|
|
|
|
class DjangoSyncRequiredMiddleware:
|
|
|
|
def resolve(self, next, root, info, **args):
|
|
|
|
parent_type = info.parent_type
|
2023-05-10 21:17:30 +03:00
|
|
|
return_type = info.return_type
|
|
|
|
|
|
|
|
if isinstance(parent_type, GraphQLNonNull):
|
|
|
|
parent_type = parent_type.of_type
|
|
|
|
if isinstance(return_type, GraphQLNonNull):
|
|
|
|
return_type = return_type.of_type
|
2023-05-09 23:52:55 +03:00
|
|
|
|
|
|
|
## Anytime the parent is a DjangoObject type
|
|
|
|
# and we're resolving a sync field, we need to wrap it in a sync_to_async
|
|
|
|
if hasattr(parent_type, "graphene_type") and hasattr(
|
|
|
|
parent_type.graphene_type._meta, "model"
|
|
|
|
):
|
2023-05-10 00:58:15 +03:00
|
|
|
if not inspect.iscoroutinefunction(next) and not inspect.isasyncgenfunction(
|
|
|
|
next
|
|
|
|
):
|
2023-05-09 23:52:55 +03:00
|
|
|
return sync_to_async(next)(root, info, **args)
|
|
|
|
|
|
|
|
## In addition, if we're resolving to a DjangoObject type
|
|
|
|
# we likely need to wrap it in a sync_to_async as well
|
2023-05-10 21:17:30 +03:00
|
|
|
if hasattr(return_type, "graphene_type") and hasattr(
|
|
|
|
return_type.graphene_type._meta, "model"
|
2023-05-09 23:52:55 +03:00
|
|
|
):
|
2023-05-10 00:58:15 +03:00
|
|
|
if not inspect.iscoroutinefunction(next) and not inspect.isasyncgenfunction(
|
|
|
|
next
|
|
|
|
):
|
2023-05-09 23:52:55 +03:00
|
|
|
return sync_to_async(next)(root, info, **args)
|
|
|
|
|
2023-05-10 21:17:30 +03:00
|
|
|
if info.parent_type.name == "Mutation":
|
|
|
|
if not inspect.iscoroutinefunction(next) and not inspect.isasyncgenfunction(
|
|
|
|
next
|
|
|
|
):
|
|
|
|
return sync_to_async(next)(root, info, **args)
|
|
|
|
|
2023-05-09 23:52:55 +03:00
|
|
|
return next(root, info, **args)
|