11import json
2+ from promise import Promise
23
34import six
45from flask import Response , request
1617
1718
1819class HttpError (Exception ):
19- def __init__ (self , response , message = None , * args , ** kwargs ):
20- self .response = response
21- self .message = message = message or response .description
22- super (HttpError , self ).__init__ (message , * args , ** kwargs )
20+ def __init__ (self , status_code , message = None , is_graphql_error = False , headers = None ):
21+ self .status_code = status_code
22+ self .message = message
23+ self .is_graphql_error = is_graphql_error
24+ self .headers = headers
25+ super (HttpError , self ).__init__ (message )
2326
2427
2528class GraphQLView (View ):
@@ -42,7 +45,6 @@ def __init__(self, **kwargs):
4245 if hasattr (self , key ):
4346 setattr (self , key , value )
4447
45- assert not all ((self .graphiql , self .batch )), 'Use either graphiql or batch processing'
4648 assert isinstance (self .schema , GraphQLSchema ), 'A Schema is required to be provided to GraphQLView.'
4749
4850 # noinspection PyUnusedLocal
@@ -70,27 +72,39 @@ def render_graphiql(self, **kwargs):
7072 def dispatch_request (self ):
7173 try :
7274 if request .method .lower () not in ('get' , 'post' ):
73- raise HttpError (MethodNotAllowed (['GET' , 'POST' ], 'GraphQL only supports GET and POST requests.' ))
75+ raise HttpError (
76+ 405 ,
77+ 'GraphQL only supports GET and POST requests.' ,
78+ headers = {
79+ 'Allow' : ['GET, POST' ]
80+ }
81+ )
7482
7583 data = self .parse_body (request )
7684
7785 show_graphiql = self .graphiql and self .can_display_graphiql (data )
7886
79- if isinstance (data , list ):
87+ is_batch = isinstance (data , list )
88+ if is_batch :
8089 if not self .batch or show_graphiql :
81- raise HttpError (BadRequest ('Batch requests are not allowed.' ))
82-
83- responses = [self .get_response (request , entry ) for entry in data ]
84- response , status_codes = zip (* responses )
85- status_code = max (status_codes )
90+ raise HttpError (
91+ 400 ,
92+ 'Batch requests are not allowed.'
93+ )
8694 else :
87- response , status_code = self .get_response (request , data , show_graphiql )
95+ data = [data ]
96+ responses = [self .get_response (request , entry , show_graphiql ) for entry in data ]
97+ response , status_codes = zip (* responses )
98+ status_code = max (status_codes )
99+
100+ if not is_batch :
101+ response = response [0 ]
88102
89103 pretty = self .pretty or show_graphiql or request .args .get ('pretty' )
90104 result = self .json_encode (response , pretty )
91105
92106 if show_graphiql :
93- query , variables , operation_name , id = self .get_graphql_params (request , data )
107+ query , variables , operation_name , id = self .get_graphql_params (request , data [ 0 ] )
94108 return self .render_graphiql (
95109 query = query ,
96110 variables = variables ,
@@ -109,22 +123,23 @@ def dispatch_request(self):
109123 self .json_encode ({
110124 'errors' : [self .format_error (e )]
111125 }),
112- status = e .response . code ,
113- headers = { 'Allow' : [ 'GET, POST' ]} ,
126+ status = e .status_code ,
127+ headers = e . headers ,
114128 content_type = 'application/json'
115129 )
116130
117131 def get_response (self , request , data , show_graphiql = False ):
118132 query , variables , operation_name , id = self .get_graphql_params (request , data )
119-
120133 execution_result = self .execute_graphql_request (
121134 data ,
122135 query ,
123136 variables ,
124137 operation_name ,
125138 show_graphiql
126139 )
140+ return self .format_execution_result (execution_result , id )
127141
142+ def format_execution_result (self , execution_result , id ):
128143 status_code = 200
129144 if execution_result :
130145 response = {}
@@ -164,7 +179,10 @@ def parse_body(self, request):
164179 try :
165180 return json .loads (request .data .decode ('utf8' ))
166181 except :
167- raise HttpError (BadRequest ('POST body sent invalid JSON.' ))
182+ raise HttpError (
183+ 400 ,
184+ 'POST body sent invalid JSON.'
185+ )
168186
169187 elif content_type == 'application/x-www-form-urlencoded' :
170188 return request .form
@@ -181,7 +199,7 @@ def execute_graphql_request(self, data, query, variables, operation_name, show_g
181199 if not query :
182200 if show_graphiql :
183201 return None
184- raise HttpError (BadRequest ( 'Must provide query string.' ) )
202+ raise HttpError (400 , 'Must provide query string.' )
185203
186204 try :
187205 source = Source (query , name = 'GraphQL request' )
@@ -200,9 +218,13 @@ def execute_graphql_request(self, data, query, variables, operation_name, show_g
200218 if operation_ast and operation_ast .operation != 'query' :
201219 if show_graphiql :
202220 return None
203- raise HttpError (MethodNotAllowed (
204- ['POST' ], 'Can only perform a {} operation from a POST request.' .format (operation_ast .operation )
205- ))
221+ raise HttpError (
222+ 405 ,
223+ 'Can only perform a {} operation from a POST request.' .format (operation_ast .operation ),
224+ headers = {
225+ 'Allow' : ['POST' ],
226+ }
227+ )
206228
207229 try :
208230 return self .execute (
@@ -234,13 +256,13 @@ def request_wants_html(cls, request):
234256 def get_graphql_params (request , data ):
235257 query = request .args .get ('query' ) or data .get ('query' )
236258 variables = request .args .get ('variables' ) or data .get ('variables' )
237- id = request . args . get ( 'id' ) or data .get ('id' )
259+ id = data .get ('id' )
238260
239261 if variables and isinstance (variables , six .text_type ):
240262 try :
241263 variables = json .loads (variables )
242264 except :
243- raise HttpError (BadRequest ( 'Variables are invalid JSON.' ) )
265+ raise HttpError (400 , 'Variables are invalid JSON.' )
244266
245267 operation_name = request .args .get ('operationName' ) or data .get ('operationName' )
246268
0 commit comments