Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.18% covered (success)
91.18%
31 / 34
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
Handler
91.18% covered (success)
91.18%
31 / 34
66.67% covered (warning)
66.67%
2 / 3
14.13
0.00% covered (danger)
0.00%
0 / 1
 register
n/a
0 / 0
n/a
0 / 0
1
 render
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
3
 handleApiV2Exception
89.29% covered (warning)
89.29%
25 / 28
0.00% covered (danger)
0.00%
0 / 1
9.10
 getReponse
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace App\Exceptions;
4
5use App\Enums\ApiError;
6use App\Enums\ApiStatus;
7use App\Exceptions\Api\V2\ApiErrorException;
8use App\Http\Resources\Api\V2\ApiResponse as V2ApiResponse;
9use App\Lib\Helper;
10use Illuminate\Auth\Access\AuthorizationException;
11use Illuminate\Auth\AuthenticationException;
12use Illuminate\Database\Eloquent\ModelNotFoundException;
13use Illuminate\Http\Exceptions\ThrottleRequestsException;
14use Illuminate\Http\JsonResponse;
15use Illuminate\Http\Request;
16use Illuminate\Validation\ValidationException;
17use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
18use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
19use Throwable;
20
21class Handler extends ExceptionHandler
22{
23    /**
24     * A list of the exception types that are not reported.
25     *
26     * @var array
27     */
28    protected $dontReport = [
29        ApiErrorException::class,
30        RedirectTo::class
31    ];
32
33    /**
34     * A list of the inputs that are never flashed for validation exceptions.
35     *
36     * @var array
37     */
38    protected $dontFlash = [
39        'password',
40        'password_confirmation',
41    ];
42
43    /**
44     * Register the exception handling callbacks for the application.
45     *
46     * @return void
47     */
48    public function register()
49    {
50    }
51
52    /**
53     * Render an exception into an HTTP response.
54     *
55     * @param  mixed      $request
56     * @param  \Throwable $e
57     * @return \Symfony\Component\HttpFoundation\Response
58     */
59    public function render($request, Throwable $e)
60    {
61        if (Helper::isRequestForApiV2($request) and $request->isJson()) {
62            return $this->handleApiV2Exception($request, $e);
63        } //end if
64        // @codeCoverageIgnoreStart
65        return parent::render($request, $e);
66        // @codeCoverageIgnoreEnd
67    }
68
69    /**
70     * Generate a json response for any apiV2 error case
71     *
72     * @param Request    $request
73     * @param \Throwable $e
74     * @return JsonResponse
75     */
76    private function handleApiV2Exception(Request $request, \Throwable $e): JsonResponse
77    {
78        switch (get_class($e)) {
79            case AuthenticationException::class:
80                /** @var AuthenticationException $e */
81                return $this->getReponse(
82                    $request,
83                    401,
84                    ApiError::apiAuthFailed()->value,
85                    sprintf('%s, %s', ApiError::apiAuthFailed()->label, $e->getMessage())
86                );
87            case MethodNotAllowedHttpException::class:
88                /** @var MethodNotAllowedHttpException $e */
89                return $this->getReponse(
90                    $request,
91                    $e->getStatusCode(),
92                    ApiError::methodNotAllowed()->value,
93                    sprintf('%s, %s', ApiError::methodNotAllowed()->label, $e->getMessage())
94                );
95            case ValidationException::class:
96                /** @var ValidationException $e */
97                return $this->getReponse(
98                    $request,
99                    $e->status,
100                    ApiError::validationFailed()->value,
101                    sprintf(ApiError::validationFailed()->label, $e->getMessage()),
102                    ['errors' => $e->errors()]
103                );
104            case ModelNotFoundException::class:
105                /** @var ModelNotFoundException $e */
106                return $this->getReponse(
107                    $request,
108                    404,
109                    ApiError::modelNotFound()->value,
110                    sprintf(
111                        ApiError::modelNotFound()->label,
112                        \array_slice(explode('\\', $e->getModel()), -1)[0]
113                    )
114                );
115            case ApiErrorException::class:
116                /** @var ApiErrorException $e */
117                return $this->getReponse(
118                    $request,
119                    $e->getStatusCode(),
120                    $e->getCode(),
121                    $e->getMessage()
122                );
123            case ThrottleRequestsException::class:
124                /** @var ThrottleRequestsException $e */
125                return $this->getReponse(
126                    $request,
127                    429,
128                    ApiError::throttleRequestsException()->value,
129                    ApiError::throttleRequestsException()->label
130                );
131            case AuthorizationException::class:
132                /** @var AuthorizationException $e */
133                return $this->getReponse(
134                    $request,
135                    403,
136                    ApiError::authorizationException()->value,
137                    ApiError::authorizationException()->label
138                );
139        } //end switch
140        // @codeCoverageIgnoreStart
141        if (config('app.env') === 'testing') {
142            // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Found
143            print_r((string)$e);
144        }
145        return $this->getReponse($request, 500, $e->getCode(), $e->getMessage());
146        // @codeCoverageIgnoreEnd
147    }
148
149    /**
150     * Generate an ApiV2 json response
151     *
152     * @param Request $request
153     * @param integer $httpCode
154     * @param integer $apiCode
155     * @param string  $message
156     * @param array   $data
157     * @return JsonResponse
158     */
159    private static function getReponse(
160        Request $request,
161        int $httpCode,
162        int $apiCode,
163        string $message,
164        array $data = []
165    ): JsonResponse {
166        return (new V2ApiResponse(
167            (object)array_merge($data, ['message' => $message, 'code' => $apiCode]),
168            ApiStatus::KO()
169        ))->toResponse($request)->setStatusCode($httpCode);
170    }
171}