11<?php
22namespace GraphQL \Server ;
33
4+ use GraphQL \Error \Error ;
45use GraphQL \Error \FormattedError ;
56use GraphQL \Error \InvariantViolation ;
67use GraphQL \Error \UserError ;
78use GraphQL \Executor \ExecutionResult ;
9+ use GraphQL \Executor \Executor ;
10+ use GraphQL \Executor \Promise \Adapter \SyncPromiseAdapter ;
811use GraphQL \Executor \Promise \Promise ;
9- use GraphQL \GraphQL ;
12+ use GraphQL \Executor \ Promise \ PromiseAdapter ;
1013use GraphQL \Language \AST \DocumentNode ;
1114use GraphQL \Language \Parser ;
1215use GraphQL \Utils \AST ;
1316use GraphQL \Utils \Utils ;
17+ use GraphQL \Validator \DocumentValidator ;
1418
1519/**
1620 * Class Helper
2125class Helper
2226{
2327 /**
24- * Executes GraphQL operation with given server configuration and returns execution result (or promise)
28+ * Executes GraphQL operation with given server configuration and returns execution result
29+ * (or promise when promise adapter is different from SyncPromiseAdapter)
2530 *
2631 * @param ServerConfig $config
2732 * @param OperationParams $op
2833 *
2934 * @return ExecutionResult|Promise
3035 */
3136 public function executeOperation (ServerConfig $ config , OperationParams $ op )
37+ {
38+ $ promiseAdapter = $ config ->getPromiseAdapter () ?: Executor::getPromiseAdapter ();
39+ $ result = $ this ->promiseToExecuteOperation ($ promiseAdapter , $ config , $ op );
40+
41+ if ($ promiseAdapter instanceof SyncPromiseAdapter) {
42+ $ result = $ promiseAdapter ->wait ($ result );
43+ }
44+
45+ return $ result ;
46+ }
47+
48+ /**
49+ * Executes batched GraphQL operations with shared promise queue
50+ * (thus, effectively batching deferreds|promises of all queries at once)
51+ *
52+ * @param ServerConfig $config
53+ * @param OperationParams[] $operations
54+ * @return ExecutionResult[]|Promise
55+ */
56+ public function executeBatch (ServerConfig $ config , array $ operations )
57+ {
58+ $ promiseAdapter = $ config ->getPromiseAdapter () ?: Executor::getPromiseAdapter ();
59+ $ result = [];
60+
61+ foreach ($ operations as $ operation ) {
62+ $ result [] = $ this ->promiseToExecuteOperation ($ promiseAdapter , $ config , $ operation );
63+ }
64+
65+ $ result = $ promiseAdapter ->all ($ result );
66+
67+ // Wait for promised results when using sync promises
68+ if ($ promiseAdapter instanceof SyncPromiseAdapter) {
69+ $ result = $ promiseAdapter ->wait ($ result );
70+ }
71+ return $ result ;
72+ }
73+
74+ /**
75+ * @param PromiseAdapter $promiseAdapter
76+ * @param ServerConfig $config
77+ * @param OperationParams $op
78+ * @return Promise
79+ */
80+ private function promiseToExecuteOperation (PromiseAdapter $ promiseAdapter , ServerConfig $ config , OperationParams $ op )
3281 {
3382 $ phpErrors = [];
34- $ execute = function () use ($ config , $ op ) {
35- $ doc = $ op ->queryId ? static ::loadPersistedQuery ($ config , $ op ) : $ op ->query ;
3683
37- if (!$ doc instanceof DocumentNode) {
38- $ doc = Parser::parse ($ doc );
39- }
40- if ($ op ->isReadOnly () && AST ::isMutation ($ op ->operation , $ doc )) {
41- throw new UserError ("Cannot execute mutation in read-only context " );
42- }
84+ $ execute = function () use ($ config , $ op , $ promiseAdapter ) {
85+ try {
86+ $ doc = $ op ->queryId ? static ::loadPersistedQuery ($ config , $ op ) : $ op ->query ;
87+
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+ }
4394
44- return GraphQL::executeAndReturnResult (
45- $ config ->getSchema (),
46- $ doc ,
47- $ config ->getRootValue (),
48- $ config ->getContext (),
49- $ op ->variables ,
50- $ op ->operation ,
51- $ config ->getDefaultFieldResolver (),
52- static ::resolveValidationRules ($ config , $ op ),
53- $ config ->getPromiseAdapter ()
54- );
95+ $ validationErrors = DocumentValidator::validate (
96+ $ config ->getSchema (),
97+ $ 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 ])
120+ );
121+ }
55122 };
56123 if ($ config ->getDebug ()) {
57124 $ execute = Utils::withErrorHandling ($ execute , $ phpErrors );
58125 }
59126 $ result = $ execute ();
60127
61- $ applyErrorFormatting = function (ExecutionResult $ result ) use ($ config , $ phpErrors ) {
128+ $ applyErrorFormatting = function (ExecutionResult $ result ) use ($ config , $ phpErrors ) {
62129 if ($ config ->getDebug ()) {
63130 $ errorFormatter = function ($ e ) {
64131 return FormattedError::createFromException ($ e , true );
@@ -73,15 +140,15 @@ public function executeOperation(ServerConfig $config, OperationParams $op)
73140 return $ result ;
74141 };
75142
76- return $ result instanceof Promise ?
77- $ result ->then ($ applyErrorFormatting ) :
78- $ applyErrorFormatting ($ result );
143+ return $ result ->then ($ applyErrorFormatting );
79144 }
80145
81146 /**
82147 * @param ServerConfig $config
83148 * @param OperationParams $op
84- * @return string|DocumentNode
149+ * @return mixed
150+ * @throws Error
151+ * @throws InvariantViolation
85152 */
86153 public function loadPersistedQuery (ServerConfig $ config , OperationParams $ op )
87154 {
0 commit comments