44use GraphQL \Error \Error ;
55use GraphQL \Error \FormattedError ;
66use GraphQL \Error \InvariantViolation ;
7- use GraphQL \Error \UserError ;
87use GraphQL \Executor \ExecutionResult ;
98use GraphQL \Executor \Executor ;
109use GraphQL \Executor \Promise \Adapter \SyncPromiseAdapter ;
@@ -36,7 +35,14 @@ class Helper
3635 public function executeOperation (ServerConfig $ config , OperationParams $ op )
3736 {
3837 $ promiseAdapter = $ config ->getPromiseAdapter () ?: Executor::getPromiseAdapter ();
39- $ result = $ this ->promiseToExecuteOperation ($ promiseAdapter , $ config , $ op );
38+
39+ $ errors = $ this ->validateOperationParams ($ op );
40+
41+ if (!empty ($ errors )) {
42+ $ result = $ promiseAdapter ->createFulfilled (new ExecutionResult (null , $ errors ));
43+ } else {
44+ $ result = $ this ->promiseToExecuteOperation ($ promiseAdapter , $ config , $ op );
45+ }
4046
4147 if ($ promiseAdapter instanceof SyncPromiseAdapter) {
4248 $ result = $ promiseAdapter ->wait ($ result );
@@ -59,7 +65,12 @@ public function executeBatch(ServerConfig $config, array $operations)
5965 $ result = [];
6066
6167 foreach ($ operations as $ operation ) {
62- $ result [] = $ this ->promiseToExecuteOperation ($ promiseAdapter , $ config , $ operation );
68+ $ errors = $ this ->validateOperationParams ($ operation );
69+ if (!empty ($ errors )) {
70+ $ result [] = $ promiseAdapter ->createFulfilled (new ExecutionResult (null , $ errors ));
71+ } else {
72+ $ result [] = $ this ->promiseToExecuteOperation ($ promiseAdapter , $ config , $ operation );
73+ }
6374 }
6475
6576 $ result = $ promiseAdapter ->all ($ result );
@@ -79,63 +90,52 @@ public function executeBatch(ServerConfig $config, array $operations)
7990 */
8091 private function promiseToExecuteOperation (PromiseAdapter $ promiseAdapter , ServerConfig $ config , OperationParams $ op )
8192 {
82- $ phpErrors = [];
93+ try {
94+ $ doc = $ op ->queryId ? static ::loadPersistedQuery ($ config , $ op ) : $ op ->query ;
8395
84- $ execute = function () use ($ config , $ op , $ promiseAdapter ) {
85- try {
86- $ doc = $ op ->queryId ? static ::loadPersistedQuery ($ config , $ op ) : $ op ->query ;
96+ if (!$ doc instanceof DocumentNode) {
97+ $ doc = Parser::parse ($ doc );
98+ }
99+ if ($ op ->isReadOnly () && AST ::getOperation ($ doc , $ op ->operation ) !== 'query ' ) {
100+ throw new Error ("GET supports only query operation " );
101+ }
87102
88- if (!$ doc instanceof DocumentNode) {
89- $ doc = Parser::parse ($ doc );
90- }
91- if ($ op ->isReadOnly () && AST ::isMutation ($ op ->operation , $ doc )) {
92- throw new UserError ("Cannot execute mutation in read-only context " );
93- }
103+ $ validationErrors = DocumentValidator::validate (
104+ $ config ->getSchema (),
105+ $ doc ,
106+ $ this ->resolveValidationRules ($ config , $ op )
107+ );
94108
95- $ validationErrors = DocumentValidator::validate (
109+ if (!empty ($ validationErrors )) {
110+ $ result = $ promiseAdapter ->createFulfilled (
111+ new ExecutionResult (null , $ validationErrors )
112+ );
113+ } else {
114+ $ result = Executor::promiseToExecute (
115+ $ promiseAdapter ,
96116 $ config ->getSchema (),
97117 $ doc ,
98- $ this ->resolveValidationRules ($ config , $ op )
99- );
100-
101- if (!empty ($ validationErrors )) {
102- return $ promiseAdapter ->createFulfilled (
103- new ExecutionResult (null , $ validationErrors )
104- );
105- } else {
106- return Executor::promiseToExecute (
107- $ promiseAdapter ,
108- $ config ->getSchema (),
109- $ doc ,
110- $ config ->getRootValue (),
111- $ config ->getContext (),
112- $ op ->variables ,
113- $ op ->operation ,
114- $ config ->getDefaultFieldResolver ()
115- );
116- }
117- } catch (Error $ e ) {
118- return $ promiseAdapter ->createFulfilled (
119- new ExecutionResult (null , [$ e ])
118+ $ config ->getRootValue (),
119+ $ config ->getContext (),
120+ $ op ->variables ,
121+ $ op ->operation ,
122+ $ config ->getDefaultFieldResolver ()
120123 );
121124 }
122- };
123- if ($ config ->getDebug ()) {
124- $ execute = Utils::withErrorHandling ($ execute , $ phpErrors );
125+ } catch (Error $ e ) {
126+ $ result = $ promiseAdapter ->createFulfilled (
127+ new ExecutionResult (null , [$ e ])
128+ );
125129 }
126- $ result = $ execute ();
127130
128- $ applyErrorFormatting = function (ExecutionResult $ result ) use ($ config, $ phpErrors ) {
131+ $ applyErrorFormatting = function (ExecutionResult $ result ) use ($ config ) {
129132 if ($ config ->getDebug ()) {
130133 $ errorFormatter = function ($ e ) {
131134 return FormattedError::createFromException ($ e , true );
132135 };
133136 } else {
134137 $ errorFormatter = $ config ->getErrorFormatter ();
135138 }
136- if (!empty ($ phpErrors )) {
137- $ result ->extensions ['phpErrors ' ] = array_map ($ errorFormatter , $ phpErrors );
138- }
139139 $ result ->setErrorFormatter ($ errorFormatter );
140140 return $ result ;
141141 };
@@ -160,7 +160,7 @@ public function loadPersistedQuery(ServerConfig $config, OperationParams $op)
160160 $ loader = $ config ->getPersistentQueryLoader ();
161161
162162 if (!$ loader ) {
163- throw new UserError ("Persisted queries are not supported by this server " );
163+ throw new Error ("Persisted queries are not supported by this server " );
164164 }
165165
166166 $ source = $ loader ($ op ->queryId , $ op );
@@ -190,10 +190,10 @@ public function resolveValidationRules(ServerConfig $config, OperationParams $pa
190190 $ validationRules = $ validationRules ($ params );
191191
192192 if (!is_array ($ validationRules )) {
193- throw new InvariantViolation (
194- "Validation rules callable must return array of rules , but got: %s " .
193+ throw new InvariantViolation (sprintf (
194+ "Expecting validation rules to be array or callable returning array , but got: %s " ,
195195 Utils::printSafe ($ validationRules )
196- );
196+ )) ;
197197 }
198198 }
199199
@@ -210,19 +210,14 @@ public function resolveValidationRules(ServerConfig $config, OperationParams $pa
210210 *
211211 * @param callable|null $readRawBodyFn
212212 * @return OperationParams|OperationParams[]
213+ * @throws Error
213214 */
214215 public function parseHttpRequest (callable $ readRawBodyFn = null )
215216 {
216217 $ method = isset ($ _SERVER ['REQUEST_METHOD ' ]) ? $ _SERVER ['REQUEST_METHOD ' ] : null ;
217218
218219 if ($ method === 'GET ' ) {
219- $ request = array_change_key_case ($ _GET );
220-
221- if (isset ($ request ['query ' ]) || isset ($ request ['queryid ' ]) || isset ($ request ['documentid ' ])) {
222- $ result = OperationParams::create ($ _GET , true );
223- } else {
224- throw new UserError ('Cannot execute GET request without "query" or "queryId" parameter ' );
225- }
220+ $ result = OperationParams::create ($ _GET , true );
226221 } else if ($ method === 'POST ' ) {
227222 $ contentType = isset ($ _SERVER ['CONTENT_TYPE ' ]) ? $ _SERVER ['CONTENT_TYPE ' ] : null ;
228223
@@ -234,18 +229,18 @@ public function parseHttpRequest(callable $readRawBodyFn = null)
234229 $ body = json_decode ($ rawBody ?: '' , true );
235230
236231 if (json_last_error ()) {
237- throw new UserError ("Could not parse JSON: " . json_last_error_msg ());
232+ throw new Error ("Could not parse JSON: " . json_last_error_msg ());
238233 }
239234 if (!is_array ($ body )) {
240- throw new UserError (
235+ throw new Error (
241236 "GraphQL Server expects JSON object or array, but got " .
242237 Utils::printSafeJson ($ body )
243238 );
244239 }
245240 if (isset ($ body [0 ])) {
246241 $ result = [];
247242 foreach ($ body as $ index => $ entry ) {
248- $ op = OperationParams::create ($ entry, true );
243+ $ op = OperationParams::create ($ entry );
249244 $ result [] = $ op ;
250245 }
251246 } else {
@@ -254,12 +249,12 @@ public function parseHttpRequest(callable $readRawBodyFn = null)
254249 } else if (stripos ($ contentType , 'application/x-www-form-urlencoded ' ) !== false ) {
255250 $ result = OperationParams::create ($ _POST );
256251 } else if (null === $ contentType ) {
257- throw new UserError ('Missing "Content-Type" header ' );
252+ throw new Error ('Missing "Content-Type" header ' );
258253 } else {
259- throw new UserError ("Unexpected content type: " . Utils::printSafeJson ($ contentType ));
254+ throw new Error ("Unexpected content type: " . Utils::printSafeJson ($ contentType ));
260255 }
261256 } else {
262- throw new UserError ('HTTP Method " ' . $ method . '" is not supported ' , 405 );
257+ throw new Error ('HTTP Method " ' . $ method . '" is not supported ' );
263258 }
264259 return $ result ;
265260 }
@@ -276,77 +271,43 @@ public function readRawBody()
276271 * Checks validity of operation params and returns array of errors (empty array when params are valid)
277272 *
278273 * @param OperationParams $params
279- * @return array
274+ * @return Error[]
280275 */
281276 public function validateOperationParams (OperationParams $ params )
282277 {
283278 $ errors = [];
284279 if (!$ params ->query && !$ params ->queryId ) {
285- $ errors [] = 'GraphQL Request must include at least one of those two parameters: "query" or "queryId" ' ;
280+ $ errors [] = new Error ( 'GraphQL Request must include at least one of those two parameters: "query" or "queryId" ' ) ;
286281 }
287282 if ($ params ->query && $ params ->queryId ) {
288- $ errors [] = 'GraphQL Request parameters "query" and "queryId" are mutually exclusive ' ;
283+ $ errors [] = new Error ( 'GraphQL Request parameters "query" and "queryId" are mutually exclusive ' ) ;
289284 }
290285
291286 if ($ params ->query !== null && (!is_string ($ params ->query ) || empty ($ params ->query ))) {
292- $ errors [] = 'GraphQL Request parameter "query" must be string, but got ' .
293- Utils::printSafeJson ($ params ->query );
287+ $ errors [] = new Error (
288+ 'GraphQL Request parameter "query" must be string, but got ' .
289+ Utils::printSafeJson ($ params ->query )
290+ );
294291 }
295292 if ($ params ->queryId !== null && (!is_string ($ params ->queryId ) || empty ($ params ->queryId ))) {
296- $ errors [] = 'GraphQL Request parameter "queryId" must be string, but got ' .
297- Utils::printSafeJson ($ params ->queryId );
293+ $ errors [] = new Error (
294+ 'GraphQL Request parameter "queryId" must be string, but got ' .
295+ Utils::printSafeJson ($ params ->queryId )
296+ );
298297 }
299298
300299 if ($ params ->operation !== null && (!is_string ($ params ->operation ) || empty ($ params ->operation ))) {
301- $ errors [] = 'GraphQL Request parameter "operation" must be string, but got ' .
302- Utils::printSafeJson ($ params ->operation );
300+ $ errors [] = new Error (
301+ 'GraphQL Request parameter "operation" must be string, but got ' .
302+ Utils::printSafeJson ($ params ->operation )
303+ );
303304 }
304305 if ($ params ->variables !== null && (!is_array ($ params ->variables ) || isset ($ params ->variables [0 ]))) {
305- $ errors [] = 'GraphQL Request parameter "variables" must be object, but got ' .
306- Utils::printSafeJson ($ params ->variables );
306+ $ errors [] = new Error (
307+ 'GraphQL Request parameter "variables" must be object or JSON string parsed to object, but got ' .
308+ Utils::printSafeJson ($ params ->getOriginalInput ('variables ' ))
309+ );
307310 }
308311 return $ errors ;
309312 }
310-
311- /**
312- * Assertion to check that parsed body is valid instance of OperationParams (or array of instances)
313- *
314- * @param OperationParams|OperationParams[] $parsedBody
315- * @throws InvariantViolation
316- * @throws UserError
317- */
318- public function assertValidRequest ($ parsedBody )
319- {
320- if (is_array ($ parsedBody )) {
321- foreach ($ parsedBody as $ index => $ entry ) {
322- if (!$ entry instanceof OperationParams) {
323- throw new InvariantViolation (sprintf (
324- 'GraphQL Server: Parsed http request must be an instance of %s or array of such instances, ' .
325- 'but got invalid array where entry at position %d is %s ' ,
326- OperationParams::class,
327- $ index ,
328- Utils::printSafe ($ entry )
329- ));
330- }
331-
332- $ errors = $ this ->validateOperationParams ($ entry );
333-
334- if (!empty ($ errors [0 ])) {
335- $ err = $ index ? "Error in query # $ index: {$ errors [0 ]}" : $ errors [0 ];
336- throw new UserError ($ err );
337- }
338- }
339- } else if ($ parsedBody instanceof OperationParams) {
340- $ errors = $ this ->validateOperationParams ($ parsedBody );
341- if (!empty ($ errors [0 ])) {
342- throw new UserError ($ errors [0 ]);
343- }
344- } else {
345- throw new InvariantViolation (sprintf (
346- 'GraphQL Server: Parsed http request must be an instance of %s or array of such instances, but got %s ' ,
347- OperationParams::class,
348- Utils::printSafe ($ parsedBody )
349- ));
350- }
351- }
352313}
0 commit comments