|
4 | 4 | namespace TheCodingMachine\Graphqlite\Bundle\Controller; |
5 | 5 |
|
6 | 6 |
|
| 7 | +use function array_map; |
| 8 | +use GraphQL\Error\ClientAware; |
7 | 9 | use GraphQL\Error\Debug; |
| 10 | +use GraphQL\Error\Error; |
8 | 11 | use GraphQL\Executor\ExecutionResult; |
9 | 12 | use GraphQL\Executor\Promise\Promise; |
10 | 13 | use GraphQL\Server\StandardServer; |
11 | 14 | use GraphQL\Upload\UploadMiddleware; |
| 15 | +use function in_array; |
12 | 16 | use function json_decode; |
13 | 17 | use Psr\Http\Message\ServerRequestInterface; |
14 | 18 | use RuntimeException; |
@@ -80,26 +84,67 @@ public function handleRequest(Request $request): Response |
80 | 84 | $uploadMiddleware = new UploadMiddleware(); |
81 | 85 | $psr7Request = $uploadMiddleware->processRequest($psr7Request); |
82 | 86 |
|
83 | | - $result = $this->handlePsr7Request($psr7Request); |
84 | | - |
85 | | - return new JsonResponse($result); |
| 87 | + return $this->handlePsr7Request($psr7Request); |
86 | 88 | } |
87 | 89 |
|
88 | | - private function handlePsr7Request(ServerRequestInterface $request): array |
| 90 | + private function handlePsr7Request(ServerRequestInterface $request): JsonResponse |
89 | 91 | { |
90 | 92 | $result = $this->standardServer->executePsrRequest($request); |
91 | 93 |
|
92 | 94 | if ($result instanceof ExecutionResult) { |
93 | | - return $result->toArray($this->debug); |
| 95 | + return new JsonResponse($result->toArray($this->debug), $this->decideHttpStatusCode($result)); |
94 | 96 | } |
95 | 97 | if (is_array($result)) { |
96 | | - return array_map(function (ExecutionResult $executionResult) { |
| 98 | + $finalResult = array_map(function (ExecutionResult $executionResult) { |
97 | 99 | return $executionResult->toArray($this->debug); |
98 | 100 | }, $result); |
| 101 | + // Let's return the highest result. |
| 102 | + $statuses = array_map([$this, 'decideHttpStatusCode'], $result); |
| 103 | + $status = max($statuses); |
| 104 | + return new JsonResponse($finalResult, $status); |
99 | 105 | } |
100 | 106 | if ($result instanceof Promise) { |
101 | 107 | throw new RuntimeException('Only SyncPromiseAdapter is supported'); |
102 | 108 | } |
103 | 109 | throw new RuntimeException('Unexpected response from StandardServer::executePsrRequest'); // @codeCoverageIgnore |
104 | 110 | } |
| 111 | + |
| 112 | + /** |
| 113 | + * Decides the HTTP status code based on the answer. |
| 114 | + * |
| 115 | + * @see https://github.com/APIs-guru/graphql-over-http#status-codes |
| 116 | + */ |
| 117 | + private function decideHttpStatusCode(ExecutionResult $result): int |
| 118 | + { |
| 119 | + // If the data entry in the response has any value other than null (when the operation has successfully executed without error) then the response should use the 200 (OK) status code. |
| 120 | + if ($result->data !== null) { |
| 121 | + return 200; |
| 122 | + } |
| 123 | + |
| 124 | + if (empty($result->errors)) { |
| 125 | + return 200; |
| 126 | + } |
| 127 | + |
| 128 | + $status = 0; |
| 129 | + // There might be many errors. Let's return the highest code we encounter. |
| 130 | + foreach ($result->errors as $error) { |
| 131 | + if ($error->getCategory() === Error::CATEGORY_GRAPHQL) { |
| 132 | + $code = 400; |
| 133 | + } else { |
| 134 | + $code = $error->getCode(); |
| 135 | + if (!isset(Response::$statusTexts[$code])) { |
| 136 | + // The exception code is not a valid HTTP code. Let's ignore it |
| 137 | + continue; |
| 138 | + } |
| 139 | + } |
| 140 | + $status = max($status, $code); |
| 141 | + } |
| 142 | + |
| 143 | + // If exceptions have been thrown and they have not a "HTTP like code", let's throw a 500. |
| 144 | + if ($status < 200) { |
| 145 | + $status = 500; |
| 146 | + } |
| 147 | + |
| 148 | + return $status; |
| 149 | + } |
105 | 150 | } |
0 commit comments