Skip to content

Commit bab2bbd

Browse files
committed
Implement POST/pretty/error conditional tests.
1 parent 1f79846 commit bab2bbd

File tree

5 files changed

+304
-47
lines changed

5 files changed

+304
-47
lines changed

graphql_django_view/__init__.py

+7-12
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,20 @@ def dispatch(self, request, *args, **kwargs):
5353

5454
return HttpResponse(
5555
status=status_code,
56-
content=self.json_encode(response),
56+
content=self.json_encode(request, response),
5757
content_type='application/json'
5858
)
5959

6060
except HttpError as e:
6161
response = e.response
6262
response['Content-Type'] = 'application/json'
63-
response.content = self.json_encode({
64-
'errors': [{'message': six.text_type(e.message)}]
63+
response.content = self.json_encode(request, {
64+
'errors': [self.format_error(e)]
6565
})
6666
return response
6767

68-
def json_encode(self, d):
69-
if not self.pretty:
68+
def json_encode(self, request, d):
69+
if not self.pretty and not request.GET.get('pretty'):
7070
return json.dumps(d, separators=(',', ':'))
7171

7272
return json.dumps(d, sort_keys=True,
@@ -81,7 +81,7 @@ def parse_body(self, request):
8181

8282
elif content_type == 'application/json':
8383
try:
84-
request_json = json.load(request.body)
84+
request_json = json.loads(request.body.decode())
8585
assert isinstance(request_json, dict)
8686
return request_json
8787
except:
@@ -148,12 +148,7 @@ def format_error(error):
148148
if isinstance(error, GraphQLError):
149149
return format_graphql_error(error)
150150

151-
elif isinstance(error, Exception):
152-
return {
153-
'message': six.text_type(error)
154-
}
155-
156-
return error
151+
return {'message': six.text_type(error)}
157152

158153
@staticmethod
159154
def get_content_type(request):

tests/schema.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from graphql.core.type.definition import GraphQLArgument, GraphQLField, GraphQLNonNull, GraphQLObjectType
2+
from graphql.core.type.scalars import GraphQLString
3+
from graphql.core.type.schema import GraphQLSchema
4+
5+
6+
def resolve_raises(o, a, i):
7+
raise Exception("Throws!")
8+
9+
10+
QueryRootType = GraphQLObjectType(
11+
name='QueryRoot',
12+
fields={
13+
'thrower': GraphQLField(GraphQLNonNull(GraphQLString), resolver=resolve_raises),
14+
'test': GraphQLField(
15+
type=GraphQLString,
16+
args={
17+
'who': GraphQLArgument(GraphQLString)
18+
},
19+
resolver=lambda obj, args, info: 'Hello %s' % (args.get('who') or 'World')
20+
)
21+
}
22+
)
23+
24+
MutationRootType = GraphQLObjectType(
25+
name='MutationRoot',
26+
fields={
27+
'writeTest': GraphQLField(
28+
type=QueryRootType,
29+
resolver=lambda *_: QueryRootType
30+
)
31+
}
32+
)
33+
34+
Schema = GraphQLSchema(QueryRootType, MutationRootType)

tests/test_http.py

+255
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import pytest
12
import json
23

34
try:
@@ -19,6 +20,9 @@ def response_json(response):
1920
return json.loads(response.content.decode())
2021

2122

23+
j = lambda **kwargs: json.dumps(kwargs)
24+
25+
2226
def test_allows_get_with_query_param(client):
2327
response = client.get(url_string(query='{test}'))
2428

@@ -148,3 +152,254 @@ def test_allows_mutation_to_exist_within_a_get(client):
148152
assert response_json(response) == {
149153
'data': {'test': "Hello World"}
150154
}
155+
156+
157+
def test_allows_post_with_json_encoding(client):
158+
response = client.post(url_string(), j(query='{test}'), 'application/json')
159+
160+
assert response.status_code == 200
161+
assert response_json(response) == {
162+
'data': {'test': "Hello World"}
163+
}
164+
165+
166+
def test_allows_sending_a_mutation_via_post(client):
167+
response = client.post(url_string(), j(query='mutation TestMutation { writeTest { test } }'), 'application/json')
168+
169+
assert response.status_code == 200
170+
assert response_json(response) == {
171+
'data': {'writeTest': {'test': 'Hello World'}}
172+
}
173+
174+
175+
def test_allows_post_with_url_encoding(client):
176+
response = client.post(url_string(), urlencode(dict(query='{test}')), 'application/x-www-form-urlencoded')
177+
178+
assert response.status_code == 200
179+
assert response_json(response) == {
180+
'data': {'test': "Hello World"}
181+
}
182+
183+
184+
def test_supports_post_json_query_with_string_variables(client):
185+
response = client.post(url_string(), j(
186+
query='query helloWho($who: String){ test(who: $who) }',
187+
variables=json.dumps({'who': "Dolly"})
188+
), 'application/json')
189+
190+
assert response.status_code == 200
191+
assert response_json(response) == {
192+
'data': {'test': "Hello Dolly"}
193+
}
194+
195+
196+
def test_supports_post_json_query_with_json_variables(client):
197+
response = client.post(url_string(), j(
198+
query='query helloWho($who: String){ test(who: $who) }',
199+
variables={'who': "Dolly"}
200+
), 'application/json')
201+
202+
assert response.status_code == 200
203+
assert response_json(response) == {
204+
'data': {'test': "Hello Dolly"}
205+
}
206+
207+
208+
def test_supports_post_url_encoded_query_with_string_variables(client):
209+
response = client.post(url_string(), urlencode(dict(
210+
query='query helloWho($who: String){ test(who: $who) }',
211+
variables=json.dumps({'who': "Dolly"})
212+
)), 'application/x-www-form-urlencoded')
213+
214+
assert response.status_code == 200
215+
assert response_json(response) == {
216+
'data': {'test': "Hello Dolly"}
217+
}
218+
219+
220+
def test_supports_post_json_quey_with_get_variable_values(client):
221+
response = client.post(url_string(
222+
variables=json.dumps({'who': "Dolly"})
223+
), j(
224+
query='query helloWho($who: String){ test(who: $who) }',
225+
), 'application/json')
226+
227+
assert response.status_code == 200
228+
assert response_json(response) == {
229+
'data': {'test': "Hello Dolly"}
230+
}
231+
232+
233+
def test_post_url_encoded_query_with_get_variable_values(client):
234+
response = client.post(url_string(
235+
variables=json.dumps({'who': "Dolly"})
236+
), urlencode(dict(
237+
query='query helloWho($who: String){ test(who: $who) }',
238+
)), 'application/x-www-form-urlencoded')
239+
240+
assert response.status_code == 200
241+
assert response_json(response) == {
242+
'data': {'test': "Hello Dolly"}
243+
}
244+
245+
246+
def test_supports_post_raw_text_query_with_get_variable_values(client):
247+
response = client.post(url_string(
248+
variables=json.dumps({'who': "Dolly"})
249+
),
250+
'query helloWho($who: String){ test(who: $who) }',
251+
'application/graphql'
252+
)
253+
254+
assert response.status_code == 200
255+
assert response_json(response) == {
256+
'data': {'test': "Hello Dolly"}
257+
}
258+
259+
260+
def test_allows_post_with_operation_name(client):
261+
response = client.post(url_string(), j(
262+
query='''
263+
query helloYou { test(who: "You"), ...shared }
264+
query helloWorld { test(who: "World"), ...shared }
265+
query helloDolly { test(who: "Dolly"), ...shared }
266+
fragment shared on QueryRoot {
267+
shared: test(who: "Everyone")
268+
}
269+
''',
270+
operationName='helloWorld'
271+
), 'application/json')
272+
273+
assert response.status_code == 200
274+
assert response_json(response) == {
275+
'data': {
276+
'test': 'Hello World',
277+
'shared': 'Hello Everyone'
278+
}
279+
}
280+
281+
282+
def test_allows_post_with_get_operation_name(client):
283+
response = client.post(url_string(
284+
operationName='helloWorld'
285+
), '''
286+
query helloYou { test(who: "You"), ...shared }
287+
query helloWorld { test(who: "World"), ...shared }
288+
query helloDolly { test(who: "Dolly"), ...shared }
289+
fragment shared on QueryRoot {
290+
shared: test(who: "Everyone")
291+
}
292+
''',
293+
'application/graphql')
294+
295+
assert response.status_code == 200
296+
assert response_json(response) == {
297+
'data': {
298+
'test': 'Hello World',
299+
'shared': 'Hello Everyone'
300+
}
301+
}
302+
303+
304+
@pytest.mark.urls('tests.urls_pretty')
305+
def test_supports_pretty_printing(client):
306+
response = client.get(url_string(query='{test}'))
307+
308+
assert response.content.decode() == (
309+
'{\n'
310+
' "data": {\n'
311+
' "test": "Hello World"\n'
312+
' }\n'
313+
'}'
314+
)
315+
316+
317+
def test_supports_pretty_printing_by_request(client):
318+
response = client.get(url_string(query='{test}', pretty='1'))
319+
320+
assert response.content.decode() == (
321+
'{\n'
322+
' "data": {\n'
323+
' "test": "Hello World"\n'
324+
' }\n'
325+
'}'
326+
)
327+
328+
329+
def test_handles_field_errors_caught_by_graphql(client):
330+
response = client.get(url_string(query='{thrower}'))
331+
assert response.status_code == 200
332+
assert response_json(response) == {
333+
'data': None,
334+
'errors': [{'locations': [{'column': 2, 'line': 1}], 'message': 'Throws!'}]
335+
}
336+
337+
338+
def test_handles_syntax_errors_caught_by_graphql(client):
339+
response = client.get(url_string(query='syntaxerror'))
340+
assert response.status_code == 400
341+
assert response_json(response) == {
342+
'errors': [{'locations': [{'column': 1, 'line': 1}],
343+
'message': 'Syntax Error GraphQL request (1:1) '
344+
'Unexpected Name "syntaxerror"\n\n1: syntaxerror\n ^\n'}]
345+
}
346+
347+
348+
def test_handles_errors_caused_by_a_lack_of_query(client):
349+
response = client.get(url_string())
350+
351+
assert response.status_code == 400
352+
assert response_json(response) == {
353+
'errors': [{'message': 'Must provide query string.'}]
354+
}
355+
356+
357+
def test_handles_invalid_json_bodies(client):
358+
response = client.post(url_string(), '[]', 'application/json')
359+
360+
assert response.status_code == 400
361+
assert response_json(response) == {
362+
'errors': [{'message': 'POST body sent invalid JSON.'}]
363+
}
364+
365+
366+
def test_handles_incomplete_json_bodies(client):
367+
response = client.post(url_string(), '{"query":', 'application/json')
368+
369+
assert response.status_code == 400
370+
assert response_json(response) == {
371+
'errors': [{'message': 'POST body sent invalid JSON.'}]
372+
}
373+
374+
375+
def test_handles_plain_post_text(client):
376+
response = client.post(url_string(
377+
variables=json.dumps({'who': "Dolly"})
378+
),
379+
'query helloWho($who: String){ test(who: $who) }',
380+
'text/plain'
381+
)
382+
assert response.status_code == 400
383+
assert response_json(response) == {
384+
'errors': [{'message': 'Must provide query string.'}]
385+
}
386+
387+
388+
def test_handles_poorly_formed_variables(client):
389+
response = client.get(url_string(
390+
query='query helloWho($who: String){ test(who: $who) }',
391+
variables='who:You'
392+
))
393+
assert response.status_code == 400
394+
assert response_json(response) == {
395+
'errors': [{'message': 'Variables are invalid JSON.'}]
396+
}
397+
398+
399+
def test_handles_unsupported_http_methods(client):
400+
response = client.put(url_string(query='{test}'))
401+
assert response.status_code == 405
402+
assert response['Allow'] == 'GET, POST'
403+
assert response_json(response) == {
404+
'errors': [{'message': 'GraphQL only supports GET and POST requests.'}]
405+
}

tests/urls.py

+1-35
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,6 @@
11
from django.conf.urls import url
2-
from graphql.core.type.definition import GraphQLArgument, GraphQLField, GraphQLNonNull, GraphQLObjectType
3-
from graphql.core.type.scalars import GraphQLString
4-
from graphql.core.type.schema import GraphQLSchema
5-
62
from graphql_django_view import GraphQLView
7-
8-
9-
def resolve_raises(o, a, i):
10-
raise Exception("Throws!")
11-
12-
13-
QueryRootType = GraphQLObjectType(
14-
name='QueryRoot',
15-
fields={
16-
'thrower': GraphQLField(GraphQLNonNull(GraphQLString), resolver=resolve_raises),
17-
'test': GraphQLField(
18-
type=GraphQLString,
19-
args={
20-
'who': GraphQLArgument(GraphQLString)
21-
},
22-
resolver=lambda obj, args, info: 'Hello %s' % (args.get('who') or 'World')
23-
)
24-
}
25-
)
26-
27-
MutationRootType = GraphQLObjectType(
28-
name='MutationRoot',
29-
fields={
30-
'writeTest': GraphQLField(
31-
type=QueryRootType,
32-
resolver=lambda *_: QueryRootType
33-
)
34-
}
35-
)
36-
37-
Schema = GraphQLSchema(QueryRootType)
3+
from .schema import Schema
384

395
urlpatterns = [
406
url(r'^graphql', GraphQLView.as_view(schema=Schema)),

0 commit comments

Comments
 (0)