Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
82.24% covered (warning)
82.24%
88 / 107
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
ExceptionService
82.24% covered (warning)
82.24%
88 / 107
50.00% covered (danger)
50.00%
1 / 2
46.08
0.00% covered (danger)
0.00%
0 / 1
 getError
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 handleException
82.08% covered (warning)
82.08%
87 / 106
0.00% covered (danger)
0.00%
0 / 1
44.88
1<?php
2
3declare(strict_types=1);
4
5namespace BO\Zmscitizenapi\Services\Core;
6
7use BO\Zmscitizenapi\Utils\ErrorMessages;
8use BO\Zmsclient\Psr7\RequestException;
9
10class ExceptionService
11{
12    private static function getError(string $key): array
13    {
14        return ErrorMessages::get($key);
15    }
16
17    /**
18     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
19     * @TODO: Consider using a strategy pattern or error handler chain to reduce method complexity
20     */
21    public static function handleException(\Exception $e): never
22    {
23        $exceptionName = json_decode(json_encode($e), true)['template'] ?? null;
24
25        if ($e instanceof RequestException) {
26            $error = self::getError('zmsClientCommunicationError');
27        } else {
28            switch ($exceptionName) {
29            // Zmsslim exception
30                case 'Slim\\Exception\\HttpNotFoundException':
31                    $error = self::getError('notFound');
32
33                    break;
34
35            // ZmsClient exception
36                case 'BO\\Zmsclient\\Exception':
37                    $error = self::getError('zmsClientCommunicationError');
38
39                    break;
40            // Missing mail template exceptions
41                case 'Twig\\Error\\RuntimeError':
42                    $error = self::getError('mailNotFound');
43
44                    break;
45                case 'Twig\\Error\\LoaderError':
46                    $error = self::getError('mailNotFound');
47
48                    break;
49                case 'BO\\Zmsapi\\Exception\\Mail\\MailNotFound':
50                    $error = self::getError('mailNotFound');
51
52                    break;
53            // Process exceptions
54                case 'BO\\Zmsapi\\Exception\\Process\\ProcessNotFound':
55                    $error = self::getError('appointmentNotFound');
56
57                    break;
58                case 'BO\\Zmsapi\\Exception\\Process\\AuthKeyMatchFailed':
59                case 'BO\\Zmsapi\\Exception\\Process\\ExternalUserIdMatchFailed':
60                    $error = self::getError('authKeyMismatch');
61
62                    break;
63                case 'BO\\Zmsapi\\Exception\\Process\\ProcessAlreadyCalled':
64                    $error = self::getError('processAlreadyCalled');
65
66                    break;
67                case 'BO\\Zmsapi\\Exception\\Process\\ProcessNotReservedAnymore':
68                    $error = self::getError('processNotReservedAnymore');
69
70                    break;
71                case 'BO\\Zmsapi\\Exception\\Process\\ProcessNotPreconfirmedAnymore':
72                    $error = self::getError('processNotPreconfirmedAnymore');
73
74                    break;
75                case 'BO\\Zmsapi\\Exception\\Process\\ProcessDeleteFailed':
76                    $error = self::getError('processDeleteFailed');
77
78                    break;
79                case 'BO\\Zmsapi\\Exception\\Process\\ProcessInvalid':
80                    $error = self::getError('processInvalid');
81
82                    break;
83                case 'BO\\Zmsapi\\Exception\\Process\\ProcessAlreadyExists':
84                    $error = self::getError('processAlreadyExists');
85
86                    break;
87                case 'BO\\Zmsapi\\Exception\\Process\\EmailRequired':
88                    $error = self::getError('emailIsRequired');
89
90                    break;
91                case 'BO\\Zmsapi\\Exception\\Process\\TelephoneRequired':
92                    $error = self::getError('telephoneIsRequired');
93
94                    break;
95                case 'BO\\Zmsapi\\Exception\\Process\\MoreThanAllowedAppointmentsPerMail':
96                    $error = self::getError('tooManyAppointmentsWithSameMail');
97
98                    break;
99                case 'BO\\Zmsapi\\Exception\\Process\\MoreThanAllowedSlotsPerAppointment':
100                    $error = self::getError('tooManySlotsPerAppointment');
101
102                    break;
103                case 'BO\\Zmsapi\\Exception\\Process\\MoreThanAllowedQuantityPerService':
104                    $error = self::getError('tooManyServicesPerAppointment');
105
106                    break;
107                case 'BO\\Zmsapi\\Exception\\Process\\PreconfirmationExpired':
108                    $error = self::getError('preconfirmationExpired');
109
110                    break;
111                case 'BO\\Zmsapi\\Exception\\Process\\ApiclientInvalid':
112                    $error = self::getError('invalidApiClient');
113
114                    break;
115            // Calendar exceptions
116                case 'BO\\Zmsapi\\Exception\\Calendar\\InvalidFirstDay':
117                    $error = self::getError('invalidDateRange');
118
119                    break;
120                case 'BO\\Zmsapi\\Exception\\Calendar\\AppointmentsMissed':
121                    $error = self::getError('noAppointmentForThisScope');
122                    break;
123                case 'BO\\Zmsdb\\Exception\\CalendarWithoutScopes':
124                    $error = self::getError('noAppointmentForThisScope');
125                    break;
126            // Other entity exceptions
127                case 'BO\\Zmsapi\\Exception\\Department\\DepartmentNotFound':
128                    $error = self::getError('departmentNotFound');
129
130                    break;
131                case 'BO\\Zmsapi\\Exception\\Organisation\\OrganisationNotFound':
132                    $error = self::getError('organisationNotFound');
133
134                    break;
135                case 'BO\\Zmsapi\\Exception\\Provider\\ProviderNotFound':
136                    $error = self::getError('providerNotFound');
137
138                    break;
139                case 'BO\\Zmsapi\\Exception\\Request\\RequestNotFound':
140                    $error = self::getError('requestNotFound');
141
142                    break;
143                case 'BO\\Zmsdb\\Exception\\Request\\RequestNotFound':
144                    $error = self::getError('requestNotFound');
145
146                    break;
147                case 'BO\\Zmsapi\\Exception\\Scope\\ScopeNotFound':
148                    $error = self::getError('scopeNotFound');
149
150                    break;
151                case 'BO\\Zmsapi\\Exception\\Source\\SourceNotFound':
152                    $error = self::getError('sourceNotFound');
153
154                    break;
155                case 'BO\\Zmsentities\\Exception\\SchemaValidation':
156                    $error = self::getError('invalidSchema');
157
158                    break;
159                case 'BO\\Zmsapi\\Exception\\Useraccount\\InvalidCredentials':
160                    $error = self::getError('invalidCredentials');
161
162                    break;
163
164            // Use original message for unmapped exceptions
165                default:
166                    $error = [
167                    'errorCode' => $exceptionName ?? 'unknown',
168                    'errorMessage' => $e->getMessage(),
169                    'statusCode' => $e->getCode() ?: 500
170                    ];
171            }
172        }
173
174        throw new \RuntimeException($error['errorCode'] . ': ' . $error['errorMessage'], $error['statusCode'], $e);
175    }
176}