Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Pull Request — master (#377)
by Hugo
12:43
created

ErrorHandler::findErrorClassUsingParentException()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 10
c 0
b 0
f 0
ccs 5
cts 5
cp 1
rs 9.9332
cc 3
nc 3
nop 1
crap 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Overblog\GraphQLBundle\Error;
6
7
use GraphQL\Error\ClientAware;
8
use GraphQL\Error\Debug;
9
use GraphQL\Error\Error as GraphQLError;
10
use GraphQL\Error\FormattedError;
11
use GraphQL\Error\UserError as GraphQLUserError;
12
use GraphQL\Executor\ExecutionResult;
13
use Overblog\GraphQLBundle\Event\ErrorFormattingEvent;
14
use Overblog\GraphQLBundle\Event\Events;
15
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
16
17
class ErrorHandler
18
{
19
    public const DEFAULT_ERROR_MESSAGE = 'Internal server Error';
20
21
    /** @var EventDispatcherInterface */
22
    private $dispatcher;
23
24
    /** @var string */
25
    private $internalErrorMessage;
26
27
    /** @var array */
28
    private $exceptionMap;
29
30
    /** @var bool */
31
    private $mapExceptionsToParent;
32
33 94
    public function __construct(
34
        EventDispatcherInterface $dispatcher,
35
        $internalErrorMessage = null,
36
        array $exceptionMap = [],
37
        $mapExceptionsToParent = false
38
    ) {
39 94
        $this->dispatcher = $dispatcher;
40 94
        if (empty($internalErrorMessage)) {
41 15
            $internalErrorMessage = self::DEFAULT_ERROR_MESSAGE;
42
        }
43 94
        $this->internalErrorMessage = $internalErrorMessage;
44 94
        $this->exceptionMap = $exceptionMap;
45 94
        $this->mapExceptionsToParent = $mapExceptionsToParent;
46 94
    }
47
48 93
    public function handleErrors(ExecutionResult $executionResult, $throwRawException = false, $debug = false): void
49
    {
50 93
        $errorFormatter = $this->createErrorFormatter($debug);
51 93
        $executionResult->setErrorFormatter($errorFormatter);
52 93
        $exceptions = $this->treatExceptions($executionResult->errors, $throwRawException);
53 88
        $executionResult->errors = $exceptions['errors'];
54 88
        if (!empty($exceptions['extensions']['warnings'])) {
55 7
            $executionResult->extensions['warnings'] = \array_map($errorFormatter, $exceptions['extensions']['warnings']);
56
        }
57 88
        if (!empty($exceptions['extensions']['deprecationWarnings'])) {
58
            $executionResult->extensions['deprecationWarnings'] = \array_map($errorFormatter, $exceptions['extensions']['deprecationWarnings']);
59
        }
60 88
    }
61
62 93
    private function createErrorFormatter($debug = false)
63
    {
64 93
        $debugMode = false;
65 93
        if ($debug) {
66 2
            $debugMode = Debug::INCLUDE_TRACE | Debug::INCLUDE_DEBUG_MESSAGE;
67
        }
68
69
        return function (GraphQLError $error) use ($debugMode) {
70 28
            $event = new ErrorFormattingEvent($error, FormattedError::createFromException($error, $debugMode, $this->internalErrorMessage));
71 28
            $this->dispatcher->dispatch(Events::ERROR_FORMATTING, $event);
72
73 28
            return $event->getFormattedError()->getArrayCopy();
74 93
        };
75
    }
76
77
    /**
78
     * @param GraphQLError[] $errors
79
     * @param bool           $throwRawException
80
     *
81
     * @return array
82
     *
83
     * @throws \Error|\Exception
84
     */
85 93
    private function treatExceptions(array $errors, bool $throwRawException): array
86
    {
87
        $treatedExceptions = [
88 93
            'errors' => [],
89
            'extensions' => [
90
                'warnings' => [],
91
                'deprecationWarnings' => [],
92
            ],
93
        ];
94
95
        /** @var GraphQLError $error */
96 93
        foreach ($this->flattenErrors($errors) as $error) {
97 33
            $rawException = $this->convertException($error->getPrevious());
98
99
            // raw GraphQL Error or InvariantViolation exception
100 33
            if (null === $rawException) {
101 10
                $treatedExceptions['errors'][] = $error;
102 10
                continue;
103
            }
104
105
            // recreate a error with converted exception
106 24
            $errorWithConvertedException = new GraphQLError(
107 24
                $error->getMessage(),
108 24
                $error->nodes,
109 24
                $error->getSource(),
110 24
                $error->getPositions(),
111 24
                $error->path,
112 24
                $rawException
113
            );
114
115
            // user error
116 24
            if ($rawException instanceof GraphQLUserError) {
117 8
                $treatedExceptions['errors'][] = $errorWithConvertedException;
118 8
                continue;
119
            }
120
121
            // user warning
122 17
            if ($rawException instanceof UserWarning) {
123 7
                $treatedExceptions['extensions']['warnings'][] = $errorWithConvertedException;
124 7
                continue;
125
            }
126
127
            // deprecation warning
128 11
            if ($rawException instanceof DeprecationWarning) {
129
                $treatedExceptions['extensions']['deprecationWarnings'][] = $errorWithConvertedException;
130
                continue;
131
            }
132
133
            // if is a catch exception wrapped in Error
134 11
            if ($throwRawException) {
135 5
                throw $rawException;
136
            }
137
138 6
            $treatedExceptions['errors'][] = $errorWithConvertedException;
139
        }
140
141 88
        return $treatedExceptions;
142
    }
143
144
    /**
145
     * @param GraphQLError[] $errors
146
     *
147
     * @return GraphQLError[]
148
     */
149 93
    private function flattenErrors(array $errors): array
150
    {
151 93
        $flattenErrors = [];
152
153 93
        foreach ($errors as $error) {
154 33
            $rawException = $error->getPrevious();
155
            // multiple errors
156 33
            if ($rawException instanceof UserErrors) {
157 1
                $rawExceptions = $rawException;
158 1
                foreach ($rawExceptions->getErrors() as $rawException) {
159 1
                    $flattenErrors[] = GraphQLError::createLocatedError($rawException, $error->nodes, $error->path);
160
                }
161
            } else {
162 33
                $flattenErrors[] = $error;
163
            }
164
        }
165
166 93
        return $flattenErrors;
167
    }
168
169
    /**
170
     * Tries to convert a raw exception into a user warning or error
171
     * that is displayed to the user.
172
     *
173
     * @param \Exception|\Error $rawException
174
     *
175
     * @return \Exception|\Error
176
     */
177 33
    private function convertException($rawException = null)
178
    {
179 33
        if (null === $rawException || $rawException instanceof ClientAware) {
180 15
            return $rawException;
181
        }
182
183 19
        $errorClass = $this->findErrorClass($rawException);
184 19
        if (null !== $errorClass) {
185 8
            return new $errorClass($rawException->getMessage(), $rawException->getCode(), $rawException);
186
        }
187
188 11
        return $rawException;
189
    }
190
191
    /**
192
     * @param \Exception|\Error $rawException
193
     *
194
     * @return string|null
195
     */
196 19
    private function findErrorClass($rawException)
197
    {
198 19
        $rawExceptionClass = \get_class($rawException);
199 19
        if (isset($this->exceptionMap[$rawExceptionClass])) {
200 7
            return $this->exceptionMap[$rawExceptionClass];
201
        }
202
203 12
        if ($this->mapExceptionsToParent) {
204 2
            return $this->findErrorClassUsingParentException($rawException);
205
        }
206
207 10
        return null;
208
    }
209
210
    /**
211
     * @param \Exception|\Error $rawException
212
     *
213
     * @return string|null
214
     */
215 2
    private function findErrorClassUsingParentException($rawException)
216
    {
217 2
        foreach ($this->exceptionMap as $rawExceptionClass => $errorClass) {
218 1
            if ($rawException instanceof $rawExceptionClass) {
219 1
                return $errorClass;
220
            }
221
        }
222
223 1
        return null;
224
    }
225
}
226