Skip to content

Commit 3a1093a

Browse files
committed
Merge pull request #176 from graphql-python/features/middlewares
Added Middleware
2 parents b431bfe + 10e5424 commit 3a1093a

26 files changed

+238
-213
lines changed

docs/config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ ga = "UA-12613282-7"
1717
"/docs/basic-types/",
1818
"/docs/enums/",
1919
"/docs/relay/",
20+
"/docs/middleware/",
2021
]
2122

2223
[docs.django]

docs/pages/docs/django/debug.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,19 @@ For that, you will need to add the plugin in your graphene schema.
1313

1414
## Installation
1515

16-
For use the Django Debug plugin in Graphene, just import `DjangoDebugPlugin` and add it to the `plugins` argument when you initiate the `Schema`.
16+
For use the Django Debug plugin in Graphene:
17+
* Import `DjangoDebugMiddleware` and add it to the `middleware` argument when you initiate the `Schema`.
18+
* Add the `debug` field into the schema root `Query` with the value `graphene.Field(DjangoDebug, name='__debug')`.
1719

1820

1921
```python
20-
from graphene.contrib.django.debug import DjangoDebugPlugin
22+
from graphene.contrib.django.debug import DjangoDebugMiddleware, DjangoDebug
2123

22-
# ...
23-
schema = graphene.Schema(query=Query, plugins=[DjangoDebugPlugin()])
24+
class Query(graphene.ObjectType):
25+
# ...
26+
debug = graphene.Field(DjangoDebug, name='__debug')
27+
28+
schema = graphene.Schema(query=Query, middlewares=[DjangoDebugMiddleware()])
2429
```
2530

2631
This plugin, will add another field in the `Query` named `__debug`.

docs/pages/docs/middleware.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
---
2+
title: Middleware
3+
description: Walkthrough Middleware
4+
---
5+
6+
# Middleware
7+
8+
You can use _middleware_ to affect the evaluation of fields in your schema.
9+
10+
A middleware is any object that responds to `resolve(*args, next_middleware)`. Inside that method, it should either:
11+
12+
* Send `resolve` to the next middleware to continue the evaluation; or
13+
* Return a value to end the evaluation early.
14+
15+
Middlewares' `resolve` is invoked with several arguments:
16+
17+
* `next` represents the execution chain. Call `next` to continue evalution.
18+
* `root` is the root value object passed throughout the query
19+
* `args` is the hash of arguments passed to the field
20+
* `context` is the context object passed throughout the query
21+
* `info` is the resolver info
22+
23+
Add a middleware to a schema by adding to the `middlewares` list.
24+
25+
26+
### Example: Authorization
27+
28+
This middleware only continues evaluation if the `field_name` is not `'user'`:
29+
30+
```python
31+
class AuthorizationMiddleware(object):
32+
33+
def resolve(self, next, root, args, context, info):
34+
if info.field_name == 'user':
35+
return None
36+
return next(root, args, context, info)
37+
```
38+
39+
Then, add the middleware to your schema:
40+
41+
```python
42+
schema = Schema(middlewares=[AuthorizationMiddleware])
43+
```
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from .plugin import DjangoDebugPlugin
1+
from .middleware import DjangoDebugMiddleware
22
from .types import DjangoDebug
33

4-
__all__ = ['DjangoDebugPlugin', 'DjangoDebug']
4+
__all__ = ['DjangoDebugMiddleware', 'DjangoDebug']
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from promise import Promise
2+
from django.db import connections
3+
4+
from .sql.tracking import unwrap_cursor, wrap_cursor
5+
from .types import DjangoDebug
6+
7+
8+
class DjangoDebugContext(object):
9+
10+
def __init__(self):
11+
self.debug_promise = None
12+
self.promises = []
13+
self.enable_instrumentation()
14+
self.object = DjangoDebug(sql=[])
15+
16+
def get_debug_promise(self):
17+
if not self.debug_promise:
18+
self.debug_promise = Promise.all(self.promises)
19+
return self.debug_promise.then(self.on_resolve_all_promises)
20+
21+
def on_resolve_all_promises(self, values):
22+
self.disable_instrumentation()
23+
return self.object
24+
25+
def add_promise(self, promise):
26+
if self.debug_promise and not self.debug_promise.is_fulfilled:
27+
self.promises.append(promise)
28+
29+
def enable_instrumentation(self):
30+
# This is thread-safe because database connections are thread-local.
31+
for connection in connections.all():
32+
wrap_cursor(connection, self)
33+
34+
def disable_instrumentation(self):
35+
for connection in connections.all():
36+
unwrap_cursor(connection)
37+
38+
39+
class DjangoDebugMiddleware(object):
40+
41+
def resolve(self, next, root, args, context, info):
42+
django_debug = getattr(context, 'django_debug', None)
43+
if not django_debug:
44+
if context is None:
45+
raise Exception('DjangoDebug cannot be executed in None contexts')
46+
try:
47+
context.django_debug = DjangoDebugContext()
48+
except Exception:
49+
raise Exception('DjangoDebug need the context to be writable, context received: {}.'.format(
50+
context.__class__.__name__
51+
))
52+
if info.schema.graphene_schema.T(DjangoDebug) == info.return_type:
53+
return context.django_debug.get_debug_promise()
54+
promise = next(root, args, context, info)
55+
context.django_debug.add_promise(promise)
56+
return promise

graphene/contrib/django/debug/plugin.py

Lines changed: 0 additions & 79 deletions
This file was deleted.

graphene/contrib/django/debug/sql/tracking.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from django.utils import six
99
from django.utils.encoding import force_text
1010

11+
from .types import DjangoDebugSQL, DjangoDebugPostgreSQL
12+
1113

1214
class SQLQueryTriggered(Exception):
1315
"""Thrown when template panel triggers a query"""
@@ -139,9 +141,11 @@ def _record(self, method, sql, params):
139141
'iso_level': iso_level,
140142
'encoding': conn.encoding,
141143
})
142-
144+
_sql = DjangoDebugPostgreSQL(**params)
145+
else:
146+
_sql = DjangoDebugSQL(**params)
143147
# We keep `sql` to maintain backwards compatibility
144-
self.logger.record(**params)
148+
self.logger.object.sql.append(_sql)
145149

146150
def callproc(self, procname, params=()):
147151
return self._record(self.cursor.callproc, procname, params)

graphene/contrib/django/debug/sql/types.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from .....core import Boolean, Float, ObjectType, String
22

33

4-
class DjangoDebugSQL(ObjectType):
4+
class DjangoDebugBaseSQL(ObjectType):
55
vendor = String()
66
alias = String()
77
sql = String()
@@ -13,6 +13,12 @@ class DjangoDebugSQL(ObjectType):
1313
is_slow = Boolean()
1414
is_select = Boolean()
1515

16+
17+
class DjangoDebugSQL(DjangoDebugBaseSQL):
18+
pass
19+
20+
21+
class DjangoDebugPostgreSQL(DjangoDebugBaseSQL):
1622
trans_id = String()
1723
trans_status = String()
1824
iso_level = String()

graphene/contrib/django/debug/tests/test_query.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@
55
from graphene.contrib.django.utils import DJANGO_FILTER_INSTALLED
66

77
from ...tests.models import Reporter
8-
from ..plugin import DjangoDebugPlugin
8+
from ..middleware import DjangoDebugMiddleware
9+
from ..types import DjangoDebug
10+
11+
12+
class context(object):
13+
pass
914

1015
# from examples.starwars_django.models import Character
1116

@@ -25,6 +30,7 @@ class Meta:
2530

2631
class Query(graphene.ObjectType):
2732
reporter = graphene.Field(ReporterType)
33+
debug = graphene.Field(DjangoDebug, name='__debug')
2834

2935
def resolve_reporter(self, *args, **kwargs):
3036
return Reporter.objects.first()
@@ -51,8 +57,8 @@ def resolve_reporter(self, *args, **kwargs):
5157
}]
5258
}
5359
}
54-
schema = graphene.Schema(query=Query, plugins=[DjangoDebugPlugin()])
55-
result = schema.execute(query)
60+
schema = graphene.Schema(query=Query, middlewares=[DjangoDebugMiddleware()])
61+
result = schema.execute(query, context_value=context())
5662
assert not result.errors
5763
assert result.data == expected
5864

@@ -70,6 +76,7 @@ class Meta:
7076

7177
class Query(graphene.ObjectType):
7278
all_reporters = ReporterType.List()
79+
debug = graphene.Field(DjangoDebug, name='__debug')
7380

7481
def resolve_all_reporters(self, *args, **kwargs):
7582
return Reporter.objects.all()
@@ -98,8 +105,8 @@ def resolve_all_reporters(self, *args, **kwargs):
98105
}]
99106
}
100107
}
101-
schema = graphene.Schema(query=Query, plugins=[DjangoDebugPlugin()])
102-
result = schema.execute(query)
108+
schema = graphene.Schema(query=Query, middlewares=[DjangoDebugMiddleware()])
109+
result = schema.execute(query, context_value=context())
103110
assert not result.errors
104111
assert result.data == expected
105112

@@ -117,6 +124,7 @@ class Meta:
117124

118125
class Query(graphene.ObjectType):
119126
all_reporters = DjangoConnectionField(ReporterType)
127+
debug = graphene.Field(DjangoDebug, name='__debug')
120128

121129
def resolve_all_reporters(self, *args, **kwargs):
122130
return Reporter.objects.all()
@@ -146,8 +154,8 @@ def resolve_all_reporters(self, *args, **kwargs):
146154
}]
147155
},
148156
}
149-
schema = graphene.Schema(query=Query, plugins=[DjangoDebugPlugin()])
150-
result = schema.execute(query)
157+
schema = graphene.Schema(query=Query, middlewares=[DjangoDebugMiddleware()])
158+
result = schema.execute(query, context_value=context())
151159
assert not result.errors
152160
assert result.data['allReporters'] == expected['allReporters']
153161
assert 'COUNT' in result.data['__debug']['sql'][0]['rawSql']
@@ -172,6 +180,7 @@ class Meta:
172180

173181
class Query(graphene.ObjectType):
174182
all_reporters = DjangoFilterConnectionField(ReporterType)
183+
debug = graphene.Field(DjangoDebug, name='__debug')
175184

176185
def resolve_all_reporters(self, *args, **kwargs):
177186
return Reporter.objects.all()
@@ -201,8 +210,8 @@ def resolve_all_reporters(self, *args, **kwargs):
201210
}]
202211
},
203212
}
204-
schema = graphene.Schema(query=Query, plugins=[DjangoDebugPlugin()])
205-
result = schema.execute(query)
213+
schema = graphene.Schema(query=Query, middlewares=[DjangoDebugMiddleware()])
214+
result = schema.execute(query, context_value=context())
206215
assert not result.errors
207216
assert result.data['allReporters'] == expected['allReporters']
208217
assert 'COUNT' in result.data['__debug']['sql'][0]['rawSql']
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from ....core.classtypes.objecttype import ObjectType
22
from ....core.types import Field
3-
from .sql.types import DjangoDebugSQL
3+
from .sql.types import DjangoDebugBaseSQL
44

55

66
class DjangoDebug(ObjectType):
7-
sql = Field(DjangoDebugSQL.List())
7+
sql = Field(DjangoDebugBaseSQL.List())

0 commit comments

Comments
 (0)