Completed
Push — master ( f77b2b...6fd5f2 )
by Rafael
05:32
created

GraphQLEndpointController::getDebugMode()   B

Complexity

Conditions 6
Paths 12

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 8.6047

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 7
cts 12
cp 0.5833
rs 8.5906
c 0
b 0
f 0
cc 6
eloc 12
nc 12
nop 0
crap 8.6047
1
<?php
2
3
/*******************************************************************************
4
 *  This file is part of the GraphQL Bundle package.
5
 *
6
 *  (c) YnloUltratech <[email protected]>
7
 *
8
 *  For the full copyright and license information, please view the LICENSE
9
 *  file that was distributed with this source code.
10
 ******************************************************************************/
11
12
namespace Ynlo\GraphQLBundle\Controller;
13
14
use GraphQL\Error\Debug;
15
use GraphQL\Error\Error;
16
use GraphQL\GraphQL;
17
use GraphQL\Validator\DocumentValidator;
18
use GraphQL\Validator\Rules;
19
use Psr\Log\LoggerInterface;
20
use Symfony\Component\HttpFoundation\JsonResponse;
21
use Symfony\Component\HttpFoundation\Request;
22
use Symfony\Component\HttpFoundation\Response;
23
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
24
use Symfony\Component\HttpKernel\Exception\HttpException;
25
use Ynlo\GraphQLBundle\Error\ErrorFormatterInterface;
26
use Ynlo\GraphQLBundle\Error\ErrorHandlerInterface;
27
use Ynlo\GraphQLBundle\Error\ErrorQueue;
28
use Ynlo\GraphQLBundle\Request\ExecuteQuery;
29
use Ynlo\GraphQLBundle\Request\RequestMiddlewareInterface;
30
use Ynlo\GraphQLBundle\Resolver\QueryExecutionContext;
31
use Ynlo\GraphQLBundle\Schema\SchemaCompiler;
32
use Ynlo\GraphQLBundle\Security\EndpointResolver;
33
34
class GraphQLEndpointController
35
{
36
    /**
37
     * @var EndpointResolver
38
     */
39
    protected $resolver;
40
41
    /**
42
     * @var SchemaCompiler
43
     */
44
    protected $compiler;
45
46
    /**
47
     * App Config
48
     *
49
     * @var array
50
     */
51
    protected $config = [];
52
53
    /**
54
     * @var ErrorFormatterInterface
55
     */
56
    protected $errorFormatter;
57
58
    /**
59
     * @var ErrorHandlerInterface
60
     */
61
    protected $errorHandler;
62
63
    /**
64
     * @var bool
65
     */
66
    protected $debug = false;
67
68
    /**
69
     * @var LoggerInterface
70
     */
71
    protected $logger;
72
73
    /**
74
     * @var iterable
75
     */
76
    protected $middlewares = [];
77
78
    /**
79
     * GraphQLEndpointController constructor.
80
     *
81
     * @param EndpointResolver $endpointResolver
82
     * @param SchemaCompiler   $compiler
83
     */
84 22
    public function __construct(EndpointResolver $endpointResolver, SchemaCompiler $compiler)
85
    {
86 22
        $this->resolver = $endpointResolver;
87 22
        $this->compiler = $compiler;
88 22
    }
89
90
    /**
91
     * @param ErrorFormatterInterface $errorFormatter
92
     */
93 22
    public function setErrorFormatter(ErrorFormatterInterface $errorFormatter): void
94
    {
95 22
        $this->errorFormatter = $errorFormatter;
96 22
    }
97
98
    /**
99
     * @param ErrorHandlerInterface $errorHandler
100
     */
101 22
    public function setErrorHandler(ErrorHandlerInterface $errorHandler): void
102
    {
103 22
        $this->errorHandler = $errorHandler;
104 22
    }
105
106
    /**
107
     * @param bool $debug
108
     */
109 22
    public function setDebug(bool $debug): void
110
    {
111 22
        $this->debug = $debug;
112 22
    }
113
114
    /**
115
     * @param LoggerInterface|null $logger
116
     */
117 22
    public function setLogger(?LoggerInterface $logger): void
118
    {
119 22
        $this->logger = $logger;
120 22
    }
121
122
    /**
123
     * @param array $config
124
     */
125 22
    public function setConfig(array $config): void
126
    {
127 22
        $this->config = $config;
128 22
    }
129
130
    /**
131
     * @param iterable $middlewares
132
     */
133 22
    public function setMiddlewares(iterable $middlewares): void
134
    {
135 22
        $this->middlewares = $middlewares;
136 22
    }
137
138 22
    public function __invoke(Request $request): JsonResponse
139
    {
140 22
        if (!$this->debug && $request->getMethod() !== Request::METHOD_POST) {
141
            throw new HttpException(Response::HTTP_BAD_REQUEST, 'The method should be POST to talk with GraphQL API');
142
        }
143
144
        try {
145 22
            $query = new ExecuteQuery();
146 22
            foreach ($this->middlewares as $middleware) {
147 22
                if ($middleware instanceof RequestMiddlewareInterface) {
148 22
                    $middleware->processRequest($request, $query);
149
                }
150
            }
151
152 22
            $context = new QueryExecutionContext();
153 22
            $validationRules = null;
154
155 22
            $endpoint = $this->resolver->resolveEndpoint($request);
156 22
            if (!$endpoint) {
157
                throw new AccessDeniedHttpException();
158
            }
159
160 22
            $schema = $this->compiler->compile($endpoint);
161 22
            $schema->assertValid();
162
163 22
            $result = GraphQL::executeQuery(
164 22
                $schema,
165 22
                $query->getRequestString(),
166 22
                null,
167 22
                $context,
168 22
                $query->getVariables(),
169 22
                $query->getOperationName(),
170 22
                null,
171 22
                $validationRules
172
            );
173
174
            //https://webonyx.github.io/graphql-php/error-handling/
175 22
            $formatter = $this->errorFormatter;
176 22
            $handler = $this->errorHandler;
177
178
            //get queued errors
179 22
            $exceptions = ErrorQueue::all();
180 22
            foreach ($exceptions as $exception) {
181 1
                $result->errors[] = Error::createLocatedError($exception);
182
            }
183
184 22
            $result->setErrorFormatter([$formatter, 'format']);
185 22
            $result->setErrorsHandler(
186 22
                function ($errors) use ($handler, $formatter) {
187 2
                    return $handler->handle($errors, $formatter, $this->getDebugMode());
188 22
                }
189
            );
190
191 22
            $output = $result->toArray($this->getDebugMode());
192 22
            $statusCode = Response::HTTP_OK;
193
        } catch (\Exception $e) {
194
            $error = Error::createLocatedError($e);
195
            $errors = $this->errorHandler->handle([$error], $this->errorFormatter, $this->debug);
196
            if ($e instanceof HttpException) {
197
                $statusCode = $e->getStatusCode();
198
            } else {
199
                $statusCode = Response::HTTP_INTERNAL_SERVER_ERROR;
200
            }
201
202
            $output = [
203
                'errors' => $errors,
204
            ];
205
        }
206
207 22
        return JsonResponse::create($output, $statusCode);
208
    }
209
210 22
    public function addGlobalValidationRules(array $validationRules): void
211
    {
212 22
        $rules = [];
213 22
        if (!empty($validationRules['query_complexity'])) {
214 22
            $rules[] = new Rules\QueryComplexity($validationRules['query_complexity']);
215
        }
216 22
        if (!empty($validationRules['query_depth'])) {
217
            $rules[] = new Rules\QueryDepth($validationRules['query_depth']);
218
        }
219 22
        if (!empty($validationRules['disable_introspection'])) {
220
            $rules[] = new Rules\DisableIntrospection();
221
        }
222 22
        array_map([DocumentValidator::class, 'addRule'], $rules);
223 22
    }
224
225
    /**
226
     * @return bool|int
227
     */
228 22
    private function getDebugMode()
229
    {
230 22
        if (!$this->debug) {
231
            // in case of debug = false
232
            // If API_DEBUG is passed, output of error formatter is enriched which debugging information.
233
            // Helpful for tests to get full error logs without the need of enable full app debug flag
234 22
            if (isset($_ENV['API_DEBUG'])) {
235
                $this->debug = $_ENV['API_DEBUG'];
236 22
            } elseif (isset($_SERVER['API_DEBUG'])) {
237
                $this->debug = $_SERVER['API_DEBUG'];
238
            }
239
        }
240
241 22
        $debugFlags = false;
242 22
        if ($this->debug) {
243
            if ($this->config['error_handling']['show_trace'] ?? true) {
244
                $debugFlags = Debug::INCLUDE_DEBUG_MESSAGE | Debug::INCLUDE_TRACE;
245
            } else {
246
                $debugFlags = Debug::INCLUDE_DEBUG_MESSAGE;
247
            }
248
        }
249
250 22
        return $debugFlags;
251
    }
252
}
253