Skip to content

Commit e256ec2

Browse files
authored
Merge pull request #15 from moufmouf/return_code_if_error
Error HTTP code when an error occurs
2 parents 31003db + 209d425 commit e256ec2

File tree

6 files changed

+124
-9
lines changed

6 files changed

+124
-9
lines changed

.travis.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ matrix:
1414
fast_finish: true
1515
include:
1616
# Test the latest stable release
17-
- php: 7.1
1817
- php: 7.2
1918
- php: 7.3
2019
env: COVERAGE=true PHPUNIT_FLAGS="-v --coverage-text"
@@ -31,7 +30,7 @@ matrix:
3130
# Minimum supported dependencies with the latest and oldest PHP version
3231
- php: 7.3
3332
env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors"
34-
- php: 7.1
33+
- php: 7.2
3534
env: COMPOSER_FLAGS="--prefer-stable --prefer-lowest" SYMFONY_DEPRECATIONS_HELPER="weak_vendors"
3635
# Dev-master is allowed to fail.
3736
- env: STABILITY="dev"

Controller/GraphqliteController.php

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
namespace TheCodingMachine\Graphqlite\Bundle\Controller;
55

66

7+
use function array_map;
8+
use GraphQL\Error\ClientAware;
79
use GraphQL\Error\Debug;
10+
use GraphQL\Error\Error;
811
use GraphQL\Executor\ExecutionResult;
912
use GraphQL\Executor\Promise\Promise;
1013
use GraphQL\Server\StandardServer;
1114
use GraphQL\Upload\UploadMiddleware;
15+
use function in_array;
1216
use function json_decode;
1317
use Psr\Http\Message\ServerRequestInterface;
1418
use RuntimeException;
@@ -80,26 +84,67 @@ public function handleRequest(Request $request): Response
8084
$uploadMiddleware = new UploadMiddleware();
8185
$psr7Request = $uploadMiddleware->processRequest($psr7Request);
8286

83-
$result = $this->handlePsr7Request($psr7Request);
84-
85-
return new JsonResponse($result);
87+
return $this->handlePsr7Request($psr7Request);
8688
}
8789

88-
private function handlePsr7Request(ServerRequestInterface $request): array
90+
private function handlePsr7Request(ServerRequestInterface $request): JsonResponse
8991
{
9092
$result = $this->standardServer->executePsrRequest($request);
9193

9294
if ($result instanceof ExecutionResult) {
93-
return $result->toArray($this->debug);
95+
return new JsonResponse($result->toArray($this->debug), $this->decideHttpStatusCode($result));
9496
}
9597
if (is_array($result)) {
96-
return array_map(function (ExecutionResult $executionResult) {
98+
$finalResult = array_map(function (ExecutionResult $executionResult) {
9799
return $executionResult->toArray($this->debug);
98100
}, $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);
99105
}
100106
if ($result instanceof Promise) {
101107
throw new RuntimeException('Only SyncPromiseAdapter is supported');
102108
}
103109
throw new RuntimeException('Unexpected response from StandardServer::executePsrRequest'); // @codeCoverageIgnore
104110
}
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+
}
105150
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
4+
namespace TheCodingMachine\Graphqlite\Bundle\Tests\Fixtures\Controller;
5+
6+
7+
use GraphQL\Error\ClientAware;
8+
9+
class MyException extends \Exception implements ClientAware
10+
{
11+
12+
/**
13+
* Returns true when exception message is safe to be displayed to a client.
14+
*
15+
* @return bool
16+
*
17+
* @api
18+
*/
19+
public function isClientSafe()
20+
{
21+
return true;
22+
}
23+
24+
/**
25+
* Returns string describing a category of the error.
26+
*
27+
* Value "graphql" is reserved for errors produced by query parsing or validation, do not use it.
28+
*
29+
* @return string
30+
*
31+
* @api
32+
*/
33+
public function getCategory()
34+
{
35+
return 'foobar';
36+
}
37+
}

Tests/Fixtures/Controller/TestGraphqlController.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,13 @@ public function contacts(): ArrayResult
5656
{
5757
return new ArrayResult([new Contact('Mouf')]);
5858
}
59+
60+
/**
61+
* @Query()
62+
* @return string
63+
*/
64+
public function triggerError(): string
65+
{
66+
throw new MyException('Boom');
67+
}
5968
}

Tests/FunctionalTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,29 @@ public function testServiceWiring()
5858
]
5959
], $result);
6060
}
61+
62+
public function testErrors()
63+
{
64+
$kernel = new GraphqliteTestingKernel('test', true);
65+
$kernel->boot();
66+
67+
$request = Request::create('/graphql', 'GET', ['query' => '
68+
{
69+
notExists
70+
}']);
71+
72+
$response = $kernel->handle($request);
73+
74+
$this->assertSame(400, $response->getStatusCode());
75+
76+
$request = Request::create('/graphql', 'GET', ['query' => '
77+
{
78+
triggerError
79+
}']);
80+
81+
$response = $kernel->handle($request);
82+
83+
$this->assertSame(500, $response->getStatusCode());
84+
85+
}
6186
}

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
}
1717
],
1818
"require" : {
19-
"php" : ">=7.1",
19+
"php" : ">=7.2",
2020
"thecodingmachine/graphqlite" : "~4.0.0",
2121
"symfony/framework-bundle": "^4.1.9",
2222
"doctrine/annotations": "^1.6",

0 commit comments

Comments
 (0)