Skip to content

Commit 05a6077

Browse files
committed
Refactored code using graphql_server
1 parent 182782c commit 05a6077

File tree

2 files changed

+21
-182
lines changed

2 files changed

+21
-182
lines changed

flask_graphql/graphqlview.py

Lines changed: 20 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,14 @@
11
import json
2-
from promise import Promise
3-
from collections import namedtuple
42

5-
import six
63
from flask import Response, request
74
from flask.views import View
85

9-
from graphql import Source, execute, parse, validate
10-
from graphql.error import format_error as format_graphql_error
11-
from graphql.error import GraphQLError
12-
from graphql.execution import ExecutionResult
136
from graphql.type.schema import GraphQLSchema
14-
from graphql.utils.get_operation_ast import get_operation_ast
7+
from graphql_server import run_http_query, HttpQueryError, default_format_error, load_json_body
158

169
from .render_graphiql import render_graphiql
1710

1811

19-
GraphQLParams = namedtuple('GraphQLParams', 'query,variables,operation_name,id')
20-
GraphQLResponse = namedtuple('GraphQLResponse', 'result,status_code')
21-
22-
23-
class HttpQueryError(Exception):
24-
def __init__(self, status_code, message=None, is_graphql_error=False, headers=None):
25-
self.status_code = status_code
26-
self.message = message
27-
self.is_graphql_error = is_graphql_error
28-
self.headers = headers
29-
super(HttpQueryError, self).__init__(message)
30-
31-
3212
class GraphQLView(View):
3313
schema = None
3414
executor = None
@@ -51,22 +31,18 @@ def __init__(self, **kwargs):
5131

5232
assert isinstance(self.schema, GraphQLSchema), 'A Schema is required to be provided to GraphQLView.'
5333

54-
# Flask
5534
# noinspection PyUnusedLocal
5635
def get_root_value(self):
5736
return self.root_value
5837

59-
# Flask
6038
def get_context(self):
6139
if self.context is not None:
6240
return self.context
6341
return request
6442

65-
# Flask
6643
def get_middleware(self):
6744
return self.middleware
6845

69-
# Flask
7046
def get_executor(self):
7147
return self.executor
7248

@@ -82,57 +58,30 @@ def dispatch_request(self):
8258

8359
try:
8460
request_method = request.method.lower()
85-
if request_method not in ('get', 'post'):
86-
raise HttpQueryError(
87-
405,
88-
'GraphQL only supports GET and POST requests.',
89-
headers={
90-
'Allow': ['GET, POST']
91-
}
92-
)
93-
9461
data = self.parse_body()
95-
is_batch = isinstance(data, list)
62+
if isinstance(data, dict):
63+
data = dict(data, **request.args.to_dict())
9664

97-
show_graphiql = not is_batch and self.should_display_graphiql(data)
65+
show_graphiql = request_method == 'get' and self.should_display_graphiql()
9866
catch = HttpQueryError if show_graphiql else None
99-
only_allow_query = request_method == 'get'
100-
101-
if not is_batch:
102-
if not isinstance(data, dict):
103-
raise HttpQueryError(
104-
400,
105-
'GraphQL params should be a dict. Received {}.'.format(data)
106-
)
107-
data = dict(data, **request.args.to_dict())
108-
data = [data]
109-
elif not self.batch:
110-
raise HttpQueryError(
111-
400,
112-
'Batch GraphQL requests are not enabled.'
113-
)
11467

115-
all_params = [self.get_graphql_params(entry) for entry in data]
68+
pretty = self.pretty or show_graphiql or request.args.get('pretty')
11669

117-
responses = [self.get_response(
70+
result, status_code, all_params = run_http_query(
11871
self.schema,
119-
params,
120-
catch,
121-
only_allow_query,
72+
request_method,
73+
data,
74+
batch_enabled=self.batch,
75+
catch=catch,
76+
77+
# Execute options
12278
root_value=self.get_root_value(),
12379
context_value=self.get_context(),
12480
middleware=self.get_middleware(),
12581
executor=self.get_executor(),
126-
) for params in all_params]
127-
128-
response, status_codes = zip(*responses)
129-
status_code = max(status_codes)
130-
131-
if not is_batch:
132-
response = response[0]
82+
)
13383

134-
pretty = self.pretty or show_graphiql or request.args.get('pretty')
135-
result = self.json_encode(response, pretty)
84+
result = self.json_encode(result, pretty)
13685

13786
if show_graphiql:
13887
return self.render_graphiql(
@@ -149,49 +98,13 @@ def dispatch_request(self):
14998
except HttpQueryError as e:
15099
return Response(
151100
self.json_encode({
152-
'errors': [self.format_error(e)]
101+
'errors': [default_format_error(e)]
153102
}),
154103
status=e.status_code,
155104
headers=e.headers,
156105
content_type='application/json'
157106
)
158107

159-
def get_response(self, schema, params, catch=None, only_allow_query=False, **kwargs):
160-
try:
161-
execution_result = self.execute_graphql_request(
162-
schema,
163-
params,
164-
only_allow_query,
165-
**kwargs
166-
)
167-
except catch:
168-
return GraphQLResponse(None, 400)
169-
170-
return self.format_execution_result(execution_result, params.id, self.format_error)
171-
172-
@staticmethod
173-
def format_execution_result(execution_result, id, format_error):
174-
status_code = 200
175-
if execution_result:
176-
response = {}
177-
178-
if execution_result.errors:
179-
response['errors'] = [format_error(e) for e in execution_result.errors]
180-
181-
if execution_result.invalid:
182-
status_code = 400
183-
else:
184-
status_code = 200
185-
response['data'] = execution_result.data
186-
187-
if id:
188-
response['id'] = id
189-
190-
else:
191-
response = None
192-
193-
return GraphQLResponse(response, status_code)
194-
195108
# Flask
196109
# noinspection PyBroadException
197110
def parse_body(self):
@@ -202,61 +115,14 @@ def parse_body(self):
202115
return {'query': request.data.decode()}
203116

204117
elif content_type == 'application/json':
205-
try:
206-
return json.loads(request.data.decode('utf8'))
207-
except:
208-
raise HttpQueryError(
209-
400,
210-
'POST body sent invalid JSON.'
211-
)
212-
213-
elif content_type == 'application/x-www-form-urlencoded':
214-
return request.form.to_dict()
118+
return load_json_body(request.data.decode('utf8'))
215119

216-
elif content_type == 'multipart/form-data':
120+
elif content_type == 'application/x-www-form-urlencoded' \
121+
or content_type == 'multipart/form-data':
217122
return request.form.to_dict()
218123

219124
return {}
220125

221-
@staticmethod
222-
def execute_graphql_request(schema, params, only_allow_query=False, **kwargs):
223-
if not params.query:
224-
raise HttpQueryError(400, 'Must provide query string.')
225-
226-
try:
227-
source = Source(params.query, name='GraphQL request')
228-
ast = parse(source)
229-
validation_errors = validate(schema, ast)
230-
if validation_errors:
231-
return ExecutionResult(
232-
errors=validation_errors,
233-
invalid=True,
234-
)
235-
except Exception as e:
236-
return ExecutionResult(errors=[e], invalid=True)
237-
238-
if only_allow_query:
239-
operation_ast = get_operation_ast(ast, params.operation_name)
240-
if operation_ast and operation_ast.operation != 'query':
241-
raise HttpQueryError(
242-
405,
243-
'Can only perform a {} operation from a POST request.'.format(operation_ast.operation),
244-
headers={
245-
'Allow': ['POST'],
246-
}
247-
)
248-
249-
try:
250-
return execute(
251-
schema,
252-
ast,
253-
operation_name=params.operation_name,
254-
variable_values=params.variables,
255-
**kwargs
256-
)
257-
except Exception as e:
258-
return ExecutionResult(errors=[e], invalid=True)
259-
260126
@staticmethod
261127
def json_encode(data, pretty=False):
262128
if not pretty:
@@ -268,42 +134,15 @@ def json_encode(data, pretty=False):
268134
separators=(',', ': ')
269135
)
270136

271-
# Flask
272-
def should_display_graphiql(self, data):
273-
if not self.graphiql or 'raw' in data:
137+
def should_display_graphiql(self):
138+
if not self.graphiql or 'raw' in request.args:
274139
return False
275140

276141
return self.request_wants_html()
277142

278-
# Flask
279143
def request_wants_html(self):
280144
best = request.accept_mimetypes \
281145
.best_match(['application/json', 'text/html'])
282146
return best == 'text/html' and \
283147
request.accept_mimetypes[best] > \
284148
request.accept_mimetypes['application/json']
285-
286-
@staticmethod
287-
def get_variables(variables):
288-
if variables and isinstance(variables, six.text_type):
289-
try:
290-
return json.loads(variables)
291-
except:
292-
raise HttpQueryError(400, 'Variables are invalid JSON.')
293-
return variables
294-
295-
@classmethod
296-
def get_graphql_params(cls, data):
297-
query = data.get('query')
298-
variables = cls.get_variables(data.get('variables'))
299-
id = data.get('id')
300-
operation_name = data.get('operationName')
301-
302-
return GraphQLParams(query, variables, operation_name, id)
303-
304-
@staticmethod
305-
def format_error(error):
306-
if isinstance(error, GraphQLError):
307-
return format_graphql_error(error)
308-
309-
return {'message': six.text_type(error)}

tests/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from flask import Flask
22
from flask_graphql import GraphQLView
3-
from .schema import Schema
3+
from schema import Schema
44

55

66
def create_app(path='/graphql', **kwargs):

0 commit comments

Comments
 (0)