From 40022decb4280df133ae382bd6cc2d64081c6870 Mon Sep 17 00:00:00 2001 From: Bradley Schofield Date: Fri, 22 Jul 2022 15:38:06 +0100 Subject: [PATCH] Start working on simplifying exceptions --- app/config/errors.php | 103 +++++++++++++++++++++++++++++- app/controllers/api/account.php | 42 ++++++------ app/controllers/general.php | 13 ++++ app/controllers/mock.php | 42 ++++++------ app/init.php | 4 +- src/Appwrite/Extend/Exception.php | 33 +++++++++- 6 files changed, 190 insertions(+), 47 deletions(-) diff --git a/app/config/errors.php b/app/config/errors.php index 8420ddbb74..43356f4b94 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -89,6 +89,13 @@ return [ 'code' => 500, ], + /** Account Errors */ + Exception::ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN => [ + 'name' => Exception::ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN, + 'description' => 'Failed to obtain access token.', + 'code' => 500, + ], + /** User Errors */ Exception::USER_COUNT_EXCEEDED => [ 'name' => Exception::USER_COUNT_EXCEEDED, @@ -511,5 +518,99 @@ return [ 'name' => Exception::DOMAIN_VERIFICATION_FAILED, 'description' => 'Domain verification for the requested domain has failed.', 'code' => 401, - ] + ], + + /* Registry Errors */ + Exception::LOGGER_NOT_SUPPORTED => [ + 'name' => Exception::LOGGER_NOT_SUPPORTED, + 'description' => 'Logging provider not supported. Logging disabled.', + 'code' => 500, + ], + + /** Mocks */ + Exception::MOCK_INVALID_CONTENT_RANGE_HEADER => [ + 'name' => Exception::MOCK_INVALID_CONTENT_RANGE_HEADER, + 'description' => 'Invalid content-range header', + 'code' => 400, + ], + Exception::MOCK_FIRST_CHUNK_CANNOT_HAVE_ID => [ + 'name' => Exception::MOCK_FIRST_CHUNK_CANNOT_HAVE_ID, + 'description' => 'First chunked request cannot have id header', + 'code' => 400, + ], + Exception::MOCK_CHUNK_MISSING_ID => [ + 'name' => Exception::MOCK_CHUNK_MISSING_ID, + 'description' => 'All chunked request must have id header (except first)', + 'code' => 400, + ], + Exception::MOCK_CHUNK_INVALID_SIZE => [ + 'name' => Exception::MOCK_CHUNK_INVALID_SIZE, + 'description' => 'Chunk size must be 5MB (except last chunk)', + 'code' => 400, + ], + Exception::MOCK_INVALID_FILE_NAME => [ + 'name' => Exception::MOCK_INVALID_FILE_NAME, + 'description' => 'Wrong file name', + 'code' => 400, + ], + Exception::MOCK_INVALID_FILE_SIZE => [ + 'name' => Exception::MOCK_INVALID_FILE_SIZE, + 'description' => 'Wrong file size', + 'code' => 400, + ], + Exception::MOCK_WRONG_FILE_UPLOADED => [ + 'name' => Exception::MOCK_WRONG_FILE_UPLOADED, + 'description' => 'Wrong file uploaded', + 'code' => 400, + ], + Exception::MOCK_MISSING_COOKIE => [ + 'name' => Exception::MOCK_MISSING_COOKIE, + 'description' => 'Missing cookie value', + 'code' => 400, + ], + Exception::MOCK_INVALID_CLIENT_ID => [ + 'name' => Exception::MOCK_INVALID_CLIENT_ID, + 'description' => 'Invalid client ID', + 'code' => 400, + ], + Exception::MOCK_INVALID_CLIENT_SECRET => [ + 'name' => Exception::MOCK_INVALID_CLIENT_SECRET, + 'description' => 'Invalid client secret', + 'code' => 400, + ], + Exception::MOCK_INVALID_TOKEN => [ + 'name' => Exception::MOCK_INVALID_TOKEN, + 'description' => 'Invalid token', + 'code' => 400, + ], + Exception::MOCK_INVALID_REFRESH_TOKEN => [ + 'name' => Exception::MOCK_INVALID_REFRESH_TOKEN, + 'description' => 'Invalid refresh token', + 'code' => 400, + ], + Exception::MOCK_INVALID_GRANT_TYPE => [ + 'name' => Exception::MOCK_INVALID_GRANT_TYPE, + 'description' => 'Invalid grant type', + 'code' => 400, + ], + Exception::MOCK_FAILED_TO_READ_RESULTS => [ + 'name' => Exception::MOCK_FAILED_TO_READ_RESULTS, + 'description' => 'Failed to read results', + 'code' => 500, + ], + Exception::MOCK_FAILED_TO_SAVE_RESULTS => [ + 'name' => Exception::MOCK_FAILED_TO_SAVE_RESULTS, + 'description' => 'Failed to save results', + 'code' => 500, + ], + Exception::MOCK_400 => [ + 'name' => Exception::MOCK_400, + 'description' => 'Mock 400 error', + 'code' => 400, + ], + Exception::MOCK_500 => [ + 'name' => Exception::MOCK_500, + 'description' => 'Mock 500 error', + 'code' => 500, + ], ]; diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 6c90441afb..cb06f6725b 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -74,11 +74,11 @@ App::post('/v1/account') $whitelistIPs = $project->getAttribute('authWhitelistIPs'); if (!empty($whitelistEmails) && !\in_array($email, $whitelistEmails)) { - throw new Exception('Console registration is restricted to specific emails. Contact your administrator for more information.', 401, Exception::USER_EMAIL_NOT_WHITELISTED); + throw new Exception(Exception::USER_EMAIL_NOT_WHITELISTED); } if (!empty($whitelistIPs) && !\in_array($request->getIP(), $whitelistIPs)) { - throw new Exception('Console registration is restricted to specific IPs. Contact your administrator for more information.', 401, Exception::USER_IP_NOT_WHITELISTED); + throw new Exception(Exception::USER_IP_NOT_WHITELISTED); } } @@ -88,7 +88,7 @@ App::post('/v1/account') $total = $dbForProject->count('users', max: APP_LIMIT_USERS); if ($total >= $limit) { - throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED); + throw new Exception(Exception::USER_COUNT_EXCEEDED); } } @@ -113,7 +113,7 @@ App::post('/v1/account') 'search' => implode(' ', [$userId, $email, $name]) ]))); } catch (Duplicate $th) { - throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS); + throw new Exception(Exception::USER_ALREADY_EXISTS); } Authorization::unsetRole('role:' . Auth::USER_ROLE_GUEST); @@ -167,11 +167,11 @@ App::post('/v1/account/sessions/email') new Query('email', Query::TYPE_EQUAL, [$email])]); if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) { - throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); // Wrong password or username + throw new Exception(Exception::USER_INVALID_CREDENTIALS); // Wrong password or username } if (false === $profile->getAttribute('status')) { // Account is blocked - throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED); // User is in status blocked + throw new Exception(Exception::USER_BLOCKED); // User is in status blocked } $detector = new Detector($request->getUserAgent('UNKNOWN')); @@ -276,13 +276,13 @@ App::get('/v1/account/sessions/oauth2/:provider') } if (empty($appId) || empty($appSecret)) { - throw new Exception('This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.', 412, Exception::PROJECT_PROVIDER_DISABLED); + throw new Exception(Exception::PROJECT_PROVIDER_DISABLED); } $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); if (!\class_exists($className)) { - throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED); + throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED); } if (empty($success)) { @@ -388,7 +388,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider); if (!\class_exists($className)) { - throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED); + throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED); } $oauth2 = new $className($appId, $appSecret, $callback); @@ -397,18 +397,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') try { $state = \array_merge($defaultState, $oauth2->parseState($state)); } catch (\Exception$exception) { - throw new Exception('Failed to parse login state params as passed from OAuth2 provider', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR); } } else { $state = $defaultState; } if (!$validateURL->isValid($state['success'])) { - throw new Exception('Invalid redirect URL for success login', 400, Exception::PROJECT_INVALID_SUCCESS_URL); + throw new Exception(Exception::PROJECT_INVALID_SUCCESS_URL); } if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) { - throw new Exception('Invalid redirect URL for failure login', 400, Exception::PROJECT_INVALID_FAILURE_URL); + throw new Exception(Exception::PROJECT_INVALID_FAILURE_URL); } $state['failure'] = null; @@ -422,7 +422,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $response->redirect($state['failure'], 301, 0); } - throw new Exception('Failed to obtain access token', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN); } $oauth2ID = $oauth2->getUserID($accessToken); @@ -432,7 +432,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $response->redirect($state['failure'], 301, 0); } - throw new Exception('Missing ID from OAuth2 provider', 400, Exception::PROJECT_MISSING_USER_ID); + throw new Exception(Exception::ACCOUNT_OAUTH_MISSING_ID); } $sessions = $user->getAttribute('sessions', []); @@ -470,7 +470,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') $total = $dbForProject->count('users', max: APP_LIMIT_USERS); if ($total >= $limit) { - throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED); + throw new Exception(Exception::USER_COUNT_EXCEEDED); } } @@ -495,13 +495,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'search' => implode(' ', [$userId, $email, $name]) ]))); } catch (Duplicate $th) { - throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS); + throw new Exception(Exception::USER_ALREADY_EXISTS); } } } if (false === $user->getAttribute('status')) { // Account is blocked - throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED); // User is in status blocked + throw new Exception(Exception::USER_BLOCKED); // User is in status blocked } // Create session token, verify user account and update OAuth2 ID and Access Token @@ -619,7 +619,7 @@ App::post('/v1/account/sessions/magic-url') ->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Mail $mails) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED); } $roles = Authorization::getRoles(); @@ -635,7 +635,7 @@ App::post('/v1/account/sessions/magic-url') $total = $dbForProject->count('users', max: APP_LIMIT_USERS); if ($total >= $limit) { - throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED); + throw new Exception(Exception::USER_COUNT_EXCEEDED); } } @@ -750,13 +750,13 @@ App::put('/v1/account/sessions/magic-url') $user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $token = Auth::tokenVerify($user->getAttribute('tokens', []), Auth::TOKEN_TYPE_MAGIC_URL, $secret); if (!$token) { - throw new Exception('Invalid login token', 401, Exception::USER_INVALID_TOKEN); + throw new Exception(Exception::USER_INVALID_TOKEN); } $detector = new Detector($request->getUserAgent('UNKNOWN')); diff --git a/app/controllers/general.php b/app/controllers/general.php index 620f16f1c1..d7b1a075c0 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -352,6 +352,19 @@ App::error(function (Throwable $error, App $utopia, Request $request, Response $ throw $error; } + if ($error instanceof Appwrite\Extend\Exception) { + // Find error code and error message + + $errors = Config::getParam('errors', []); + $errorData = $errors[$error->getType()]; + + $error = new Appwrite\Extend\Exception( + $errorData['description'] ?? $error->getMessage(), + $errorData['code'] ?? $error->getCode(), + $errorData['name'] ?? $error->getType() + ); + } + if ($logger) { if ($error->getCode() >= 500 || $error->getCode() === 0) { try { diff --git a/app/controllers/mock.php b/app/controllers/mock.php index f681cb2482..bf9d00c4f0 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -253,31 +253,31 @@ App::post('/v1/mock/tests/general/upload') $file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size']; if (is_null($start) || is_null($end) || is_null($size)) { - throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_CONTENT_RANGE_HEADER); } if ($start > $end || $end > $size) { - throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_CONTENT_RANGE_HEADER); } if ($start === 0 && !empty($id)) { - throw new Exception('First chunked request cannot have id header', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_FIRST_CHUNK_CANNOT_HAVE_ID); } if ($start !== 0 && $id !== 'newfileid') { - throw new Exception('All chunked request must have id header (except first)', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_CHUNK_MISSING_ID); } if ($end !== $size && $end - $start + 1 !== $chunkSize) { - throw new Exception('Chunk size must be 5MB (except last chunk)', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_CHUNK_INVALID_SIZE); } if ($end !== $size && $file['size'] !== $chunkSize) { - throw new Exception('Wrong chunk size', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_CHUNK_INVALID_SIZE); } if ($file['size'] > $chunkSize) { - throw new Exception('Chunk size must be 5MB or less', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_CHUNK_INVALID_SIZE); } if ($end !== $size) { @@ -293,15 +293,15 @@ App::post('/v1/mock/tests/general/upload') $file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size']; if ($file['name'] !== 'file.png') { - throw new Exception('Wrong file name', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_FILE_NAME); } if ($file['size'] !== 38756) { - throw new Exception('Wrong file size', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_FILE_SIZE); } if (\md5(\file_get_contents($file['tmp_name'])) !== 'd80e7e6999a3eb2ae0d631a96fe135a4') { - throw new Exception('Wrong file uploaded', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_WRONG_FILE_UPLOADED); } } }); @@ -374,7 +374,7 @@ App::get('/v1/mock/tests/general/get-cookie') ->action(function (Request $request) { if ($request->getCookie('cookieName', '') !== 'cookieValue') { - throw new Exception('Missing cookie value', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_MISSING_COOKIE); } }); @@ -408,7 +408,7 @@ App::get('/v1/mock/tests/general/400-error') ->label('sdk.response.model', Response::MODEL_ERROR) ->label('sdk.mock', true) ->action(function () { - throw new Exception('Mock 400 error', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_400); }); App::get('/v1/mock/tests/general/500-error') @@ -424,7 +424,7 @@ App::get('/v1/mock/tests/general/500-error') ->label('sdk.response.model', Response::MODEL_ERROR) ->label('sdk.mock', true) ->action(function () { - throw new Exception('Mock 500 error', 500, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_500); }); App::get('/v1/mock/tests/general/502-error') @@ -480,11 +480,11 @@ App::get('/v1/mock/tests/general/oauth2/token') ->action(function (string $client_id, string $client_secret, string $grantType, string $redirectURI, string $code, string $refreshToken, Response $response) { if ($client_id != '1') { - throw new Exception('Invalid client ID', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_CLIENT_ID); } if ($client_secret != '123456') { - throw new Exception('Invalid client secret', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_CLIENT_SECRET); } $responseJson = [ @@ -495,18 +495,18 @@ App::get('/v1/mock/tests/general/oauth2/token') if ($grantType === 'authorization_code') { if ($code !== 'abcdef') { - throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_TOKEN); } $response->json($responseJson); } elseif ($grantType === 'refresh_token') { if ($refreshToken !== 'tuvwxyz') { - throw new Exception('Invalid refresh token', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_REFRESH_TOKEN); } $response->json($responseJson); } else { - throw new Exception('Invalid grant type', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_GRANT_TYPE); } }); @@ -520,7 +520,7 @@ App::get('/v1/mock/tests/general/oauth2/user') ->action(function (string $token, Response $response) { if ($token != '123456') { - throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_INVALID_TOKEN); } $response->json([ @@ -566,7 +566,7 @@ App::shutdown(function (App $utopia, Response $response, Request $request) { $tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : []; if (!\is_array($tests)) { - throw new Exception('Failed to read results', 500, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_FAILED_TO_READ_RESULTS); } $result[$route->getMethod() . ':' . $route->getPath()] = true; @@ -574,7 +574,7 @@ App::shutdown(function (App $utopia, Response $response, Request $request) { $tests = \array_merge($tests, $result); if (!\file_put_contents($path, \json_encode($tests), LOCK_EX)) { - throw new Exception('Failed to save results', 500, Exception::GENERAL_MOCK); + throw new Exception(Exception::MOCK_FAILED_TO_SAVE_RESULTS); } $response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getPath() . ':passed']), Response::MODEL_MOCK); diff --git a/app/init.php b/app/init.php index 71407699b8..915cfe90c9 100644 --- a/app/init.php +++ b/app/init.php @@ -448,7 +448,7 @@ $register->set('logger', function () { } if (!Logger::hasProvider($providerName)) { - throw new Exception("Logging provider not supported. Logging disabled.", 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::LOGGER_NOT_SUPPORTED); } $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); @@ -818,7 +818,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons try { $payload = $jwt->decode($authJWT); } catch (JWTException $error) { - throw new Exception('Failed to verify JWT. ' . $error->getMessage(), 401, Exception::USER_JWT_INVALID); + throw new Exception(Exception::USER_JWT_INVALID); } $jwtUserId = $payload['userId'] ?? ''; diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 69cc32b715..34480d6bdc 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -12,6 +12,7 @@ class Exception extends \Exception * * Appwrite has the follwing entities: * - General + * - Account * - Users * - Teams * - Memberships @@ -29,6 +30,8 @@ class Exception extends \Exception * - Keys * - Platform * - Domain + * - Logger + * - Mocks */ /** General */ @@ -47,7 +50,11 @@ class Exception extends \Exception public const GENERAL_ROUTE_NOT_FOUND = 'general_route_not_found'; public const GENERAL_CURSOR_NOT_FOUND = 'general_cursor_not_found'; public const GENERAL_SERVER_ERROR = 'general_server_error'; - public const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported'; + public const GENERAL_PROTOCOL_UNSUPPORTED = 'general_protocol_unsupported'; + + /** Account */ + public const ACCOUNT_OAUTH_FAILED_TO_OBTAIN_TOKEN = 'account_oauth_failed_to_obtain_token'; + public const ACCOUNT_OAUTH_MISSING_ID = 'account_oauth_missing_id'; /** Users */ public const USER_COUNT_EXCEEDED = 'user_count_exceeded'; @@ -170,10 +177,32 @@ class Exception extends \Exception public const DOMAIN_ALREADY_EXISTS = 'domain_already_exists'; public const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed'; + /** Logger */ + public const LOGGER_NOT_SUPPORTED = 'logger_not_supported'; + + /** Mocks */ + public const MOCK_INVALID_CONTENT_RANGE_HEADER = 'mock_invalid_content_range_header'; + public const MOCK_FIRST_CHUNK_CANNOT_HAVE_ID = 'mock_first_chunk_cannot_have_id'; + public const MOCK_CHUNK_MISSING_ID = 'mock_chunk_missing_id'; + public const MOCK_CHUNK_INVALID_SIZE = 'mock_chunk_invalid_size'; + public const MOCK_INVALID_FILE_NAME = 'mock_invalid_file_name'; + public const MOCK_INVALID_FILE_SIZE = 'mock_invalid_file_size'; + public const MOCK_WRONG_FILE_UPLOADED = 'mock_wrong_file_uploaded'; + public const MOCK_MISSING_COOKIE = 'mock_missing_cookie'; + public const MOCK_INVALID_CLIENT_ID = 'mock_invalid_client_id'; + public const MOCK_INVALID_CLIENT_SECRET = 'mock_invalid_client_secret'; + public const MOCK_INVALID_TOKEN = 'mock_invalid_token'; + public const MOCK_INVALID_REFRESH_TOKEN = 'mock_invalid_refresh_token'; + public const MOCK_INVALID_GRANT_TYPE = 'mock_invalid_grant_type'; + public const MOCK_FAILED_TO_READ_RESULTS = 'mock_failed_to_read_results'; + public const MOCK_FAILED_TO_SAVE_RESULTS = 'mock_failed_to_save_results'; + public const MOCK_400 = 'mock_400'; + public const MOCK_500 = 'mock_500'; + private $type = ''; - public function __construct(string $message, int $code = 0, string $type = Exception::GENERAL_UNKNOWN, \Throwable $previous = null) + public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = '', int $code = 0, \Throwable $previous = null) { $this->type = $type;