diff --git a/app/config/errors.php b/app/config/errors.php index 8420ddbb74..dc07689205 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -102,7 +102,7 @@ return [ ], Exception::USER_ALREADY_EXISTS => [ 'name' => Exception::USER_ALREADY_EXISTS, - 'description' => 'A user with the same email ID already exists in your project.', + 'description' => 'A user with the same email already exists in your project.', 'code' => 409, ], Exception::USER_BLOCKED => [ @@ -122,12 +122,12 @@ return [ ], Exception::USER_EMAIL_NOT_WHITELISTED => [ 'name' => Exception::USER_EMAIL_NOT_WHITELISTED, - 'description' => 'The user\'s email is not part of the whitelist. Please check the _APP_CONSOLE_WHITELIST_EMAILS environment variable of your Appwrite server.', + 'description' => 'Console registration is restricted to specific emails. Contact your administrator for more information.', 'code' => 401, ], Exception::USER_IP_NOT_WHITELISTED => [ 'name' => Exception::USER_IP_NOT_WHITELISTED, - 'description' => 'The user\'s IP address is not part of the whitelist. Please check the _APP_CONSOLE_WHITELIST_IPS environment variable of your Appwrite server.', + 'description' => 'Console registration is restricted to specific IPs. Contact your administrator for more information.', 'code' => 401, ], Exception::USER_INVALID_CREDENTIALS => [ @@ -152,7 +152,7 @@ return [ ], Exception::USER_EMAIL_ALREADY_EXISTS => [ 'name' => Exception::USER_EMAIL_ALREADY_EXISTS, - 'description' => 'Another user with the same email already exists in the current project.', + 'description' => 'A user with the same email already exists in the current project.', 'code' => 409, ], Exception::USER_PASSWORD_MISMATCH => [ @@ -185,6 +185,11 @@ return [ 'description' => 'The current user does not have a phone number associated with their account.', 'code' => 400, ], + Exception::USER_MISSING_ID => [ + 'name' => Exception::USER_MISSING_ID, + 'description' => 'Missing ID from OAuth2 provider.', + 'code' => 400, + ], /** Teams */ Exception::TEAM_NOT_FOUND => [ @@ -194,7 +199,7 @@ return [ ], Exception::TEAM_INVITE_ALREADY_EXISTS => [ 'name' => Exception::TEAM_INVITE_ALREADY_EXISTS, - 'description' => 'The current user has already received an invitation to join the team.', + 'description' => 'User has already been invited or is already a member of this team', 'code' => 409, ], Exception::TEAM_INVITE_NOT_FOUND => [ @@ -218,13 +223,17 @@ return [ 'code' => 401, ], - /** Membership */ Exception::MEMBERSHIP_NOT_FOUND => [ 'name' => Exception::MEMBERSHIP_NOT_FOUND, 'description' => 'Membership with the requested ID could not be found.', 'code' => 404, ], + Exception::MEMBERSHIP_ALREADY_CONFIRMED => [ + 'name' => Exception::MEMBERSHIP_ALREADY_CONFIRMED, + 'description' => 'Membership already confirmed', + 'code' => 409, + ], /** Avatars */ Exception::AVATAR_SET_NOT_FOUND => [ @@ -271,7 +280,7 @@ return [ ], Exception::STORAGE_FILE_TYPE_UNSUPPORTED => [ 'name' => Exception::STORAGE_FILE_TYPE_UNSUPPORTED, - 'description' => 'The file type is not supported.', + 'description' => 'The given file extension is not supported.', 'code' => 400, ], Exception::STORAGE_INVALID_FILE_SIZE => [ @@ -325,7 +334,7 @@ return [ ], Exception::BUILD_NOT_READY => [ 'name' => Exception::BUILD_NOT_READY, - 'description' => 'Build with the requested ID is builing and not ready for execution.', + 'description' => 'Build with the requested ID is building and not ready for execution.', 'code' => 400, ], Exception::BUILD_IN_PROGRESS => [ @@ -348,6 +357,19 @@ return [ 'code' => 404, ], + /** Databases */ + Exception::DATABASE_NOT_FOUND => [ + 'name' => Exception::DATABASE_NOT_FOUND, + 'description' => 'Database not found', + 'code' => 404 + ], + + Exception::DATABASE_ALREADY_EXISTS => [ + 'name' => Exception::DATABASE_ALREADY_EXISTS, + 'description' => 'Database already exists', + 'code' => 409 + ], + /** Collections */ Exception::COLLECTION_NOT_FOUND => [ 'name' => Exception::COLLECTION_NOT_FOUND, @@ -469,19 +491,24 @@ return [ ], Exception::PROJECT_INVALID_SUCCESS_URL => [ 'name' => Exception::PROJECT_INVALID_SUCCESS_URL, - 'description' => 'Invalid URL received for OAuth success redirect.', + 'description' => 'Invalid redirect URL for OAuth success.', 'code' => 400, ], Exception::PROJECT_INVALID_FAILURE_URL => [ 'name' => Exception::PROJECT_INVALID_FAILURE_URL, - 'description' => 'Invalid URL received for OAuth failure redirect.', + 'description' => 'Invalid redirect URL for OAuth failure.', 'code' => 400, ], - Exception::PROJECT_MISSING_USER_ID => [ - 'name' => Exception::PROJECT_MISSING_USER_ID, - 'description' => 'Failed to obtain user ID from the OAuth provider.', + Exception::PROJECT_RESERVED_PROJECT => [ + 'name' => Exception::PROJECT_RESERVED_PROJECT, + 'description' => 'The project ID is reserved. Please choose another project ID.', 'code' => 400, ], + Exception::PROJECT_KEY_EXPIRED => [ + 'name' => Exception::PROJECT_KEY_EXPIRED, + 'description' => 'The project key has expired. Please generate a new key using the Appwrite console.', + 'code' => 401, + ], Exception::WEBHOOK_NOT_FOUND => [ 'name' => Exception::WEBHOOK_NOT_FOUND, 'description' => 'Webhook with the requested ID could not be found.', @@ -511,5 +538,5 @@ return [ 'name' => Exception::DOMAIN_VERIFICATION_FAILED, 'description' => 'Domain verification for the requested domain has failed.', 'code' => 401, - ] + ], ]; diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 79eb52cfc6..0dd533a69e 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -75,11 +75,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); } } @@ -89,7 +89,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); } } @@ -114,7 +114,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); @@ -169,11 +169,11 @@ App::post('/v1/account/sessions/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')); @@ -278,13 +278,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, 'This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.'); } $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)) { @@ -390,7 +390,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); @@ -399,18 +399,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, 'Failed to parse login state params as passed from OAuth2 provider'); } } 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; @@ -424,7 +424,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::GENERAL_SERVER_ERROR, 'Failed to obtain access token'); } $oauth2ID = $oauth2->getUserID($accessToken); @@ -434,7 +434,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::USER_MISSING_ID); } $sessions = $user->getAttribute('sessions', []); @@ -473,7 +473,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); } } @@ -498,13 +498,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 @@ -623,7 +623,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, 'SMTP disabled'); } $roles = Authorization::getRoles(); @@ -639,7 +639,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); } } @@ -753,13 +753,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')); @@ -806,7 +806,7 @@ App::put('/v1/account/sessions/magic-url') $user = $dbForProject->updateDocument('users', $user->getId(), $user); if (false === $user) { - throw new Exception('Failed saving user to DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB'); } $audits->setResource('user/' . $user->getId()); @@ -864,7 +864,7 @@ App::post('/v1/account/sessions/phone') ->inject('phone') ->action(function (string $userId, string $number, Request $request, Response $response, Document $project, Database $dbForProject, Audit $audits, Event $events, EventPhone $messaging, Phone $phone) { if (empty(App::getEnv('_APP_PHONE_PROVIDER'))) { - throw new Exception('Phone provider not configured', 503, Exception::GENERAL_PHONE_DISABLED); + throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } $roles = Authorization::getRoles(); @@ -880,7 +880,7 @@ App::post('/v1/account/sessions/phone') $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); } } @@ -983,13 +983,13 @@ App::put('/v1/account/sessions/phone') $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::phoneTokenVerify($user->getAttribute('tokens', []), $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')); @@ -1034,7 +1034,7 @@ App::put('/v1/account/sessions/phone') $user = $dbForProject->updateDocument('users', $user->getId(), $user); if (false === $user) { - throw new Exception('Failed saving user to DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB'); } $audits->setResource('user/' . $user->getId()); @@ -1096,11 +1096,11 @@ App::post('/v1/account/sessions/anonymous') $protocol = $request->getProtocol(); if ('console' === $project->getId()) { - throw new Exception('Failed to create anonymous user.', 401, Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED); + throw new Exception(Exception::USER_ANONYMOUS_CONSOLE_PROHIBITED, 'Failed to create anonymous user'); } if (!$user->isEmpty()) { - throw new Exception('Cannot create an anonymous user when logged in.', 401, Exception::USER_SESSION_ALREADY_EXISTS); + throw new Exception(Exception::USER_SESSION_ALREADY_EXISTS, 'Cannot create an anonymous user when logged in'); } $limit = $project->getAttribute('auths', [])['limit'] ?? 0; @@ -1109,7 +1109,7 @@ App::post('/v1/account/sessions/anonymous') $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); } } @@ -1227,7 +1227,7 @@ App::post('/v1/account/jwt') } if ($current->isEmpty()) { - throw new Exception('No valid session found', 404, Exception::USER_SESSION_NOT_FOUND); + throw new Exception(Exception::USER_SESSION_NOT_FOUND); } $jwt = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway. @@ -1422,7 +1422,7 @@ App::get('/v1/account/sessions/:sessionId') } } - throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND); + throw new Exception(Exception::USER_SESSION_NOT_FOUND); }); App::patch('/v1/account/name') @@ -1484,8 +1484,8 @@ App::patch('/v1/account/password') ->action(function (string $password, string $oldPassword, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { // Check old password only if its an existing user. - if ($user->getAttribute('passwordUpdate') !== null && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user password - throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); + if ($user->getAttribute('passwordUpdate') !== 0 && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user password + throw new Exception(Exception::USER_INVALID_CREDENTIALS); } $user = $dbForProject->updateDocument( @@ -1535,7 +1535,7 @@ App::patch('/v1/account/email') !$isAnonymousUser && !Auth::passwordVerify($password, $user->getAttribute('password')) ) { // Double check user password - throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); + throw new Exception(Exception::USER_INVALID_CREDENTIALS); } $email = \strtolower($email); @@ -1549,7 +1549,7 @@ App::patch('/v1/account/email') try { $user = $dbForProject->updateDocument('users', $user->getId(), $user); } catch (Duplicate $th) { - throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS); + throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } $audits @@ -1591,7 +1591,7 @@ App::patch('/v1/account/phone') !$isAnonymousUser && !Auth::passwordVerify($password, $user->getAttribute('password')) ) { // Double check user password - throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); + throw new Exception(Exception::USER_INVALID_CREDENTIALS); } $user @@ -1602,7 +1602,7 @@ App::patch('/v1/account/phone') try { $user = $dbForProject->updateDocument('users', $user->getId(), $user); } catch (Duplicate $th) { - throw new Exception('Phone number already exists', 409, Exception::USER_PHONE_ALREADY_EXISTS); + throw new Exception(Exception::USER_PHONE_ALREADY_EXISTS); } $audits @@ -1760,7 +1760,7 @@ App::delete('/v1/account/sessions/:sessionId') } } - throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND); + throw new Exception(Exception::USER_SESSION_NOT_FOUND); }); App::patch('/v1/account/sessions/:sessionId') @@ -1814,7 +1814,7 @@ App::patch('/v1/account/sessions/:sessionId') $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, '', [], []); @@ -1847,7 +1847,7 @@ App::patch('/v1/account/sessions/:sessionId') } } - throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND); + throw new Exception(Exception::USER_SESSION_NOT_FOUND); }); App::delete('/v1/account/sessions') @@ -1946,7 +1946,7 @@ App::post('/v1/account/recovery') ->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $mails, Audit $audits, Event $events, Stats $usage) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); } $roles = Authorization::getRoles(); @@ -1960,11 +1960,11 @@ App::post('/v1/account/recovery') ]); if (!$profile) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } if (false === $profile->getAttribute('status')) { // Account is blocked - throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED); + throw new Exception(Exception::USER_BLOCKED); } $expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_RECOVERY); @@ -2048,20 +2048,20 @@ App::put('/v1/account/recovery') ->action(function (string $userId, string $secret, string $password, string $passwordAgain, Response $response, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { if ($password !== $passwordAgain) { - throw new Exception('Passwords must match', 400, Exception::USER_PASSWORD_MISMATCH); + throw new Exception(Exception::USER_PASSWORD_MISMATCH); } $profile = $dbForProject->getDocument('users', $userId); if ($profile->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $tokens = $profile->getAttribute('tokens', []); $recovery = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_RECOVERY, $secret); if (!$recovery) { - throw new Exception('Invalid recovery token', 401, Exception::USER_INVALID_TOKEN); + throw new Exception(Exception::USER_INVALID_TOKEN); } Authorization::setRole('user:' . $profile->getId()); @@ -2120,7 +2120,7 @@ App::post('/v1/account/verification') ->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Mail $mails, Stats $usage) { if (empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled'); } $roles = Authorization::getRoles(); @@ -2207,14 +2207,14 @@ App::put('/v1/account/verification') $profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $tokens = $profile->getAttribute('tokens', []); $verification = Auth::tokenVerify($tokens, Auth::TOKEN_TYPE_VERIFICATION, $secret); if (!$verification) { - throw new Exception('Invalid verification token', 401, Exception::USER_INVALID_TOKEN); + throw new Exception(Exception::USER_INVALID_TOKEN); } Authorization::setRole('user:' . $profile->getId()); @@ -2268,11 +2268,11 @@ App::post('/v1/account/verification/phone') ->action(function (Request $request, Response $response, Phone $phone, Document $user, Database $dbForProject, Audit $audits, Event $events, Stats $usage, EventPhone $messaging) { if (empty(App::getEnv('_APP_PHONE_PROVIDER'))) { - throw new Exception('Phone provider not configured', 503, Exception::GENERAL_PHONE_DISABLED); + throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } if (empty($user->getAttribute('phone'))) { - throw new Exception('User has no phone number.', 400, Exception::USER_PHONE_NOT_FOUND); + throw new Exception(Exception::USER_PHONE_NOT_FOUND); } $roles = Authorization::getRoles(); @@ -2353,13 +2353,13 @@ App::put('/v1/account/verification/phone') $profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $verification = Auth::phoneTokenVerify($user->getAttribute('tokens', []), $secret); if (!$verification) { - throw new Exception('Invalid verification token', 401, Exception::USER_INVALID_TOKEN); + throw new Exception(Exception::USER_INVALID_TOKEN); } Authorization::setRole('user:' . $profile->getId()); diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index f31d02f8d4..afdd7d4daf 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -25,15 +25,15 @@ $avatarCallback = function (string $type, string $code, int $width, int $height, $set = Config::getParam('avatar-' . $type, []); if (empty($set)) { - throw new Exception('Avatar set not found', 404, Exception::AVATAR_SET_NOT_FOUND); + throw new Exception(Exception::AVATAR_SET_NOT_FOUND); } if (!\array_key_exists($code, $set)) { - throw new Exception('Avatar not found', 404, Exception::AVATAR_NOT_FOUND); + throw new Exception(Exception::AVATAR_NOT_FOUND); } if (!\extension_loaded('imagick')) { - throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } $output = 'png'; @@ -43,7 +43,7 @@ $avatarCallback = function (string $type, string $code, int $width, int $height, $type = 'png'; if (!\is_readable($path)) { - throw new Exception('File not readable in ' . $path, 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'File not readable in ' . $path); } $cache = new Cache(new Filesystem(APP_STORAGE_CACHE . '/app-0')); // Limit file number or size @@ -166,19 +166,19 @@ App::get('/v1/avatars/image') } if (!\extension_loaded('imagick')) { - throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } $fetch = @\file_get_contents($url, false); if (!$fetch) { - throw new Exception('Image not found', 404, Exception::AVATAR_IMAGE_NOT_FOUND); + throw new Exception(Exception::AVATAR_IMAGE_NOT_FOUND); } try { $image = new Image($fetch); } catch (\Exception $exception) { - throw new Exception('Unable to parse image', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unable to parse image'); } $image->crop((int) $width, (int) $height); @@ -232,7 +232,7 @@ App::get('/v1/avatars/favicon') } if (!\extension_loaded('imagick')) { - throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } $curl = \curl_init(); @@ -254,7 +254,7 @@ App::get('/v1/avatars/favicon') \curl_close($curl); if (!$html) { - throw new Exception('Failed to fetch remote URL', 404, Exception::AVATAR_REMOTE_URL_FAILED); + throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED); } $doc = new DOMDocument(); @@ -312,7 +312,7 @@ App::get('/v1/avatars/favicon') $data = @\file_get_contents($outputHref, false); if (empty($data) || (\mb_substr($data, 0, 5) === 'save($key, $data); @@ -327,7 +327,7 @@ App::get('/v1/avatars/favicon') $fetch = @\file_get_contents($outputHref, false); if (!$fetch) { - throw new Exception('Icon not found', 404, Exception::AVATAR_ICON_NOT_FOUND); + throw new Exception(Exception::AVATAR_ICON_NOT_FOUND); } $image = new Image($fetch); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index 6c942225f3..572e30bb0b 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -69,28 +69,28 @@ function createAttribute(string $databaseId, string $collectionId, Document $att $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } if (!empty($format)) { if (!Structure::hasFormat($format, $type)) { - throw new Exception("Format {$format} not available for {$type} attributes.", 400, Exception::ATTRIBUTE_FORMAT_UNSUPPORTED); + throw new Exception(Exception::ATTRIBUTE_FORMAT_UNSUPPORTED, "Format {$format} not available for {$type} attributes."); } } // Must throw here since dbForProject->createAttribute is performed by db worker if ($required && $default) { - throw new Exception('Cannot set default value for required attribute', 400, Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED); + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for required attribute'); } if ($array && $default) { - throw new Exception('Cannot set default value for array attributes', 400, Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED); + throw new Exception(Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED, 'Cannot set default value for array attributes'); } try { @@ -116,9 +116,9 @@ function createAttribute(string $databaseId, string $collectionId, Document $att $dbForProject->checkAttribute($collection, $attribute); $attribute = $dbForProject->createDocument('attributes', $attribute); } catch (DuplicateException $exception) { - throw new Exception('Attribute already exists', 409, Exception::ATTRIBUTE_ALREADY_EXISTS); + throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS); } catch (LimitException $exception) { - throw new Exception('Attribute limit exceeded', 400, Exception::ATTRIBUTE_LIMIT_EXCEEDED); + throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED, 'Attribute limit exceeded'); } $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId); @@ -186,7 +186,7 @@ App::post('/v1/databases') $collections = Config::getParam('collections', [])['collections'] ?? []; if (empty($collections)) { - throw new Exception('Collections collection is not configured.', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'The "collections" collection is not configured.'); } $attributes = []; @@ -217,7 +217,7 @@ App::post('/v1/databases') } $dbForProject->createCollection('database_' . $database->getInternalId(), $attributes, $indexes); } catch (DuplicateException $th) { - throw new Exception('Database already exists', 409, Exception::DATABASE_ALREADY_EXISTS); + throw new Exception(Exception::DATABASE_ALREADY_EXISTS); } $audits @@ -268,7 +268,7 @@ App::get('/v1/databases') $cursorDocument = $dbForProject->getDocument('databases', $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Collection '{$cursor}' for the 'cursor' value not found."); } $queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -302,7 +302,7 @@ App::get('/v1/databases/:databaseId') $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $usage->setParam('databases.read', 1); @@ -333,7 +333,7 @@ App::get('/v1/databases/:databaseId/logs') $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $audit = new Audit($dbForProject); @@ -413,7 +413,7 @@ App::put('/v1/databases/:databaseId') $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } try { @@ -421,9 +421,9 @@ App::put('/v1/databases/:databaseId') ->setAttribute('name', $name) ->setAttribute('search', implode(' ', [$databaseId, $name]))); } catch (AuthorizationException $exception) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } catch (StructureException $exception) { - throw new Exception('Bad structure. ' . $exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage()); } $audits @@ -460,11 +460,11 @@ App::delete('/v1/databases/:databaseId') $database = $dbForProject->getDocument('databases', $databaseId); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } if (!$dbForProject->deleteDocument('databases', $databaseId)) { - throw new Exception('Failed to remove collection from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); } $dbForProject->deleteCachedCollection('databases' . $database->getInternalId()); @@ -518,7 +518,7 @@ App::post('/v1/databases/:databaseId/collections') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collectionId = $collectionId == 'unique()' ? $dbForProject->getId() : $collectionId; @@ -539,9 +539,9 @@ App::post('/v1/databases/:databaseId/collections') $dbForProject->createCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); } catch (DuplicateException $th) { - throw new Exception('Collection already exists', 409, Exception::COLLECTION_ALREADY_EXISTS); + throw new Exception(Exception::COLLECTION_ALREADY_EXISTS); } catch (LimitException $th) { - throw new Exception('Collection limit exceeded', 400, Exception::COLLECTION_LIMIT_EXCEEDED); + throw new Exception(Exception::COLLECTION_LIMIT_EXCEEDED); } $audits @@ -589,7 +589,7 @@ App::get('/v1/databases/:databaseId/collections') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $filterQueries = []; @@ -606,7 +606,7 @@ App::get('/v1/databases/:databaseId/collections') $cursorDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Collection '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Collection '{$cursor}' for the 'cursor' value not found."); } $queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -644,12 +644,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $usage @@ -684,13 +684,13 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/logs') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $audit = new Audit($dbForProject); @@ -779,12 +779,12 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $read ??= $collection->getRead() ?? []; // By default inherit read permissions @@ -800,9 +800,9 @@ App::put('/v1/databases/:databaseId/collections/:collectionId') ->setAttribute('enabled', $enabled) ->setAttribute('search', implode(' ', [$collectionId, $name]))); } catch (AuthorizationException $exception) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } catch (StructureException $exception) { - throw new Exception('Bad structure. ' . $exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Bad structure. ' . $exception->getMessage()); } $audits @@ -847,17 +847,17 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } if (!$dbForProject->deleteDocument('database_' . $database->getInternalId(), $collectionId)) { - throw new Exception('Failed to remove collection from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove collection from DB'); } $dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); @@ -917,7 +917,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/string // Ensure attribute default is within required size $validator = new Text($size); if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); } $attribute = createAttribute($databaseId, $collectionId, new Document([ @@ -1007,13 +1007,13 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/enum') foreach ($elements as $element) { $length = \strlen($element); if ($length === 0) { - throw new Exception('Each enum element must not be empty', 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Each enum element must not be empty'); } $size = ($length > $size) ? $length : $size; } if (!is_null($default) && !in_array($default, $elements)) { - throw new Exception('Default value not found in elements', 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Default value not found in elements'); } $attribute = createAttribute($databaseId, $collectionId, new Document([ @@ -1147,13 +1147,13 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/intege $max = (is_null($max)) ? PHP_INT_MAX : \intval($max); if ($min > $max) { - throw new Exception('Minimum value must be lesser than maximum value', 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); } $validator = new Range($min, $max, Database::VAR_INTEGER); if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); } $size = $max > 2147483647 ? 8 : 4; // Automatically create BigInt depending on max value @@ -1217,7 +1217,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' $max = (is_null($max)) ? PHP_FLOAT_MAX : \floatval($max); if ($min > $max) { - throw new Exception('Minimum value must be lesser than maximum value', 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, 'Minimum value must be lesser than maximum value'); } // Ensure default value is a float @@ -1228,7 +1228,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/float' $validator = new Range($min, $max, Database::VAR_FLOAT); if (!is_null($default) && !$validator->isValid($default)) { - throw new Exception($validator->getDescription(), 400, Exception::ATTRIBUTE_VALUE_INVALID); + throw new Exception(Exception::ATTRIBUTE_VALUE_INVALID, $validator->getDescription()); } $attribute = createAttribute($databaseId, $collectionId, new Document([ @@ -1361,12 +1361,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $attributes = $collection->getAttribute('attributes'); @@ -1413,19 +1413,19 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/attributes/:key') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $attribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); if ($attribute->isEmpty()) { - throw new Exception('Attribute not found', 404, Exception::ATTRIBUTE_NOT_FOUND); + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } // Select response model based on type and format @@ -1480,18 +1480,18 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $attribute = $dbForProject->getDocument('attributes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); if ($attribute->isEmpty()) { - throw new Exception('Attribute not found', 404, Exception::ATTRIBUTE_NOT_FOUND); + throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } // Only update status if removing available attribute @@ -1579,12 +1579,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $count = $dbForProject->count('indexes', [ @@ -1595,7 +1595,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $limit = 64 - MariaDB::getNumberOfDefaultIndexes(); if ($count >= $limit) { - throw new Exception('Index limit exceeded', 400, Exception::INDEX_LIMIT_EXCEEDED); + throw new Exception(Exception::INDEX_LIMIT_EXCEEDED, 'Index limit exceeded'); } // Convert Document[] to array of attribute metadata @@ -1641,7 +1641,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key')); if ($attributeIndex === false) { - throw new Exception('Unknown attribute: ' . $attribute, 400, Exception::ATTRIBUTE_UNKNOWN); + throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown attribute: ' . $attribute); } $attributeStatus = $oldAttributes[$attributeIndex]['status']; @@ -1650,7 +1650,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') // ensure attribute is available if ($attributeStatus !== 'available') { - throw new Exception('Attribute not available: ' . $oldAttributes[$attributeIndex]['key'], 400, Exception::ATTRIBUTE_NOT_AVAILABLE); + throw new Exception(Exception::ATTRIBUTE_NOT_AVAILABLE, 'Attribute not available: ' . $oldAttributes[$attributeIndex]['key']); } // set attribute size as index length only for strings @@ -1672,7 +1672,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') 'orders' => $orders, ])); } catch (DuplicateException $th) { - throw new Exception('Index already exists', 409, Exception::INDEX_ALREADY_EXISTS); + throw new Exception(Exception::INDEX_ALREADY_EXISTS); } $dbForProject->deleteCachedDocument('database_' . $db->getInternalId(), $collectionId); @@ -1727,12 +1727,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $indexes = $collection->getAttribute('indexes'); @@ -1770,12 +1770,12 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $indexes = $collection->getAttribute('indexes'); @@ -1784,7 +1784,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $indexIndex = array_search($key, array_column($indexes, 'key')); if ($indexIndex === false) { - throw new Exception('Index not found', 404, Exception::INDEX_NOT_FOUND); + throw new Exception(Exception::INDEX_NOT_FOUND); } $index = new Document([\array_merge($indexes[$indexIndex], [ @@ -1824,18 +1824,18 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/indexes/:key') $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($db->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $db->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $index = $dbForProject->getDocument('indexes', $db->getInternalId() . '_' . $collection->getInternalId() . '_' . $key); if (empty($index->getId())) { - throw new Exception('Index not found', 404, Exception::INDEX_NOT_FOUND); + throw new Exception(Exception::INDEX_NOT_FOUND); } // Only update status if removing available index @@ -1904,16 +1904,16 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data)) { - throw new Exception('Missing payload', 400, Exception::DOCUMENT_MISSING_PAYLOAD); + throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD); } if (isset($data['$id'])) { - throw new Exception('$id is not allowed for creating new documents, try update instead', 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, '$id is not allowed for creating new documents, try update instead'); } /** @@ -1925,7 +1925,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -1933,7 +1933,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization('write'); if (!$validator->isValid($collection->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -1948,13 +1948,12 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach ($data['$read'] as $read) { if (!Authorization::isRole($read)) { - // TODO: Isn't this a 401: Unauthorized Error ? - throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')'); } } foreach ($data['$write'] as $write) { if (!Authorization::isRole($write)) { - throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')'); } } } @@ -1968,9 +1967,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') } $document->setAttribute('$collection', $collectionId); } catch (StructureException $exception) { - throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage()); } catch (DuplicateException $exception) { - throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS); + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } $events @@ -2026,7 +2025,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } /** * Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions. @@ -2037,7 +2036,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -2045,12 +2044,18 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization('read'); if (!$validator->isValid($collection->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } $filterQueries = \array_map(function ($query) { - return Query::parse($query); + $query = Query::parse($query); + + if (\count($query->getValues()) > 100) { + throw new Exception(Exception::GENERAL_QUERY_LIMIT_EXCEEDED, "You cannot use more than 100 query values on attribute '{$query->getAttribute()}'"); + } + + return $query; }, $queries); $otherQueries = []; @@ -2066,7 +2071,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') : $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Document '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Document '{$cursor}' for the 'cursor' value not found."); } $otherQueries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -2078,7 +2083,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $attributes = $collection->getAttribute('attributes', []); $validator = new QueriesValidator(new QueryValidator($attributes), $attributes, $collection->getAttribute('indexes', []), true); if (!$validator->isValid($allQueries)) { - throw new Exception($validator->getDescription(), 400, Exception::GENERAL_QUERY_INVALID); + throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription()); } } @@ -2132,7 +2137,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } /** * Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions. @@ -2141,7 +2146,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -2149,7 +2154,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization('read'); if (!$validator->isValid($collection->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -2166,7 +2171,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $document->setAttribute('$collection', $collectionId); if ($document->isEmpty()) { - throw new Exception('No document found', 404, Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::DOCUMENT_NOT_FOUND); } $usage @@ -2204,18 +2209,18 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } $collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $document = $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId); if ($document->isEmpty()) { - throw new Exception('No document found', 404, Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::DOCUMENT_NOT_FOUND); } $audit = new Audit($dbForProject); @@ -2302,7 +2307,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } /** * Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions. @@ -2311,7 +2316,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -2319,7 +2324,7 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization('write'); if (!$validator->isValid($collection->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } $document = Authorization::skip(fn() => $dbForProject->getDocument('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $documentId)); @@ -2329,17 +2334,17 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum if ($document->isEmpty()) { - throw new Exception('Document not found', 404, Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::DOCUMENT_NOT_FOUND); } $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array if (empty($data) && empty($read) && empty($write)) { - throw new Exception('Missing payload or read/write permissions', 400, Exception::DOCUMENT_MISSING_PAYLOAD); + throw new Exception(Exception::DOCUMENT_MISSING_PAYLOAD, 'Missing payload or read/write permissions'); } if (!\is_array($data)) { - throw new Exception('Data param should be a valid JSON object', 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, 'Data param should be a valid JSON object'); } $data = \array_merge($document->getArrayCopy(), $data); @@ -2357,14 +2362,14 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum if (!is_null($read)) { foreach ($data['$read'] as $read) { if (!Authorization::isRole($read)) { - throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')'); } } } if (!is_null($write)) { foreach ($data['$write'] as $write) { if (!Authorization::isRole($write)) { - throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')'); } } } @@ -2382,11 +2387,11 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum */ $document->setAttribute('$collection', $collectionId); } catch (AuthorizationException $exception) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } catch (DuplicateException $exception) { - throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS); + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } catch (StructureException $exception) { - throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage()); } $events @@ -2438,7 +2443,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); if ($database->isEmpty()) { - throw new Exception('Database not found', 404, Exception::DATABASE_NOT_FOUND); + throw new Exception(Exception::DATABASE_NOT_FOUND); } /** * Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions. @@ -2447,7 +2452,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu if ($collection->isEmpty() || !$collection->getAttribute('enabled')) { if (!($mode === APP_MODE_ADMIN && Auth::isPrivilegedUser(Authorization::getRoles()))) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } } @@ -2455,7 +2460,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu if ($collection->getAttribute('permission') === 'collection') { $validator = new Authorization('write'); if (!$validator->isValid($collection->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -2467,7 +2472,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu } if ($document->isEmpty()) { - throw new Exception('No document found', 404, Exception::DOCUMENT_NOT_FOUND); + throw new Exception(Exception::DOCUMENT_NOT_FOUND); } if ($collection->getAttribute('permission') === 'collection') { @@ -2762,7 +2767,7 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/usage') $collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collectionDocument->getInternalId()); if ($collection->isEmpty()) { - throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND); + throw new Exception(Exception::COLLECTION_NOT_FOUND); } $usage = []; diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 71f2be493e..438aa9ba02 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -122,7 +122,7 @@ App::get('/v1/functions') $cursorDocument = $dbForProject->getDocument('functions', $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Function '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Function '{$cursor}' for the 'cursor' value not found."); } $queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -179,7 +179,7 @@ App::get('/v1/functions/:functionId') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $response->dynamic($function, Response::MODEL_FUNCTION); @@ -204,7 +204,7 @@ App::get('/v1/functions/:functionId/usage') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $usage = []; @@ -314,7 +314,7 @@ App::put('/v1/functions/:functionId') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $original = $function->getAttribute('schedule', ''); @@ -373,19 +373,19 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId') $build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } if ($build->isEmpty()) { - throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND); + throw new Exception(Exception::BUILD_NOT_FOUND); } if ($build->getAttribute('status') !== 'ready') { - throw new Exception('Build not ready', 400, Exception::BUILD_NOT_READY); + throw new Exception(Exception::BUILD_NOT_READY); } $schedule = $function->getAttribute('schedule', ''); @@ -434,11 +434,11 @@ App::delete('/v1/functions/:functionId') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } if (!$dbForProject->deleteDocument('functions', $function->getId())) { - throw new Exception('Failed to remove function from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove function from DB'); } $deletes @@ -481,7 +481,7 @@ App::post('/v1/functions/:functionId/deployments') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $file = $request->getFiles('code'); @@ -490,7 +490,7 @@ App::post('/v1/functions/:functionId/deployments') $upload = new Upload(); if (empty($file)) { - throw new Exception('No file sent', 400, Exception::STORAGE_FILE_EMPTY); + throw new Exception(Exception::STORAGE_FILE_EMPTY, 'No file sent'); } // Make sure we handle a single file and multiple files the same way @@ -499,7 +499,7 @@ App::post('/v1/functions/:functionId/deployments') $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; if (!$fileExt->isValid($file['name'])) { // Check if file type is allowed - throw new Exception('File type not allowed', 400, Exception::STORAGE_FILE_TYPE_UNSUPPORTED); + throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED); } $contentRange = $request->getHeader('content-range'); @@ -513,7 +513,7 @@ App::post('/v1/functions/:functionId/deployments') $fileSize = $request->getContentRangeSize(); $deploymentId = $request->getHeader('x-appwrite-id', $deploymentId); if (is_null($start) || is_null($end) || is_null($fileSize)) { - throw new Exception('Invalid content-range header', 400, Exception::STORAGE_INVALID_CONTENT_RANGE); + throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE); } if ($end === $fileSize) { @@ -527,11 +527,11 @@ App::post('/v1/functions/:functionId/deployments') } if (!$fileSizeValidator->isValid($fileSize)) { // Check if file size is exceeding allowed limit - throw new Exception('File size not allowed', 400, Exception::STORAGE_INVALID_FILE_SIZE); + throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE); } if (!$upload->isValid($fileTmpName)) { - throw new Exception('Invalid file', 403, Exception::STORAGE_INVALID_FILE); + throw new Exception(Exception::STORAGE_INVALID_FILE); } // Save to storage @@ -552,7 +552,7 @@ App::post('/v1/functions/:functionId/deployments') $chunksUploaded = $deviceFunctions->upload($fileTmpName, $path, $chunk, $chunks, $metadata); if (empty($chunksUploaded)) { - throw new Exception('Failed moving file', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed moving file'); } $activate = (bool) filter_var($activate, FILTER_VALIDATE_BOOLEAN); @@ -659,7 +659,7 @@ App::get('/v1/functions/:functionId/deployments') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $filterQueries = []; @@ -679,7 +679,7 @@ App::get('/v1/functions/:functionId/deployments') $cursorDocument = $dbForProject->getDocument('deployments', $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Tag '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Tag '{$cursor}' for the 'cursor' value not found."); } $queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -721,17 +721,17 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $deployment = $dbForProject->getDocument('deployments', $deploymentId); if ($deployment->getAttribute('resourceId') !== $function->getId()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } $response->dynamic($deployment, Response::MODEL_DEPLOYMENT); @@ -760,21 +760,21 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $deployment = $dbForProject->getDocument('deployments', $deploymentId); if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } if ($deployment->getAttribute('resourceId') !== $function->getId()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } if ($deviceFunctions->delete($deployment->getAttribute('path', ''))) { if (!$dbForProject->deleteDocument('deployments', $deployment->getId())) { - throw new Exception('Failed to remove deployment from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove deployment from DB'); } } @@ -826,7 +826,7 @@ App::post('/v1/functions/:functionId/executions') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $runtimes = Config::getParam('runtimes', []); @@ -834,33 +834,33 @@ App::post('/v1/functions/:functionId/executions') $runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null; if (\is_null($runtime)) { - throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported', 400, Exception::FUNCTION_RUNTIME_UNSUPPORTED); + throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $function->getAttribute('runtime', '') . '" is not supported'); } $deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', ''))); if ($deployment->getAttribute('resourceId') !== $function->getId()) { - throw new Exception('Deployment not found. Create a deployment before trying to execute a function', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); } if ($deployment->isEmpty()) { - throw new Exception('Deployment not found. Create a deployment before trying to execute a function', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND, 'Deployment not found. Create a deployment before trying to execute a function'); } /** Check if build has completed */ $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', ''))); if ($build->isEmpty()) { - throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND); + throw new Exception(Exception::BUILD_NOT_FOUND); } if ($build->getAttribute('status') !== 'ready') { - throw new Exception('Build not ready', 400, Exception::BUILD_NOT_READY); + throw new Exception(Exception::BUILD_NOT_READY); } $validator = new Authorization('execute'); if (!$validator->isValid($function->getAttribute('execute'))) { // Check if user has write access to execute function - throw new Exception($validator->getDescription(), 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription()); } $executionId = $dbForProject->getId(); @@ -1007,7 +1007,7 @@ App::get('/v1/functions/:functionId/executions') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $filterQueries = [ @@ -1026,7 +1026,7 @@ App::get('/v1/functions/:functionId/executions') $cursorDocument = $dbForProject->getDocument('executions', $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Execution '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Tag '{$cursor}' for the 'cursor' value not found."); } $queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -1061,17 +1061,17 @@ App::get('/v1/functions/:functionId/executions/:executionId') $function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId)); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } $execution = $dbForProject->getDocument('executions', $executionId); if ($execution->getAttribute('functionId') !== $function->getId()) { - throw new Exception('Execution not found', 404, Exception::EXECUTION_NOT_FOUND); + throw new Exception(Exception::EXECUTION_NOT_FOUND); } if ($execution->isEmpty()) { - throw new Exception('Execution not found', 404, Exception::EXECUTION_NOT_FOUND); + throw new Exception(Exception::EXECUTION_NOT_FOUND); } $response->dynamic($execution, Response::MODEL_EXECUTION); @@ -1101,21 +1101,21 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') $deployment = $dbForProject->getDocument('deployments', $deploymentId); if ($function->isEmpty()) { - throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND); + throw new Exception(Exception::FUNCTION_NOT_FOUND); } if ($deployment->isEmpty()) { - throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND); + throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } $build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId)); if ($build->isEmpty()) { - throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND); + throw new Exception(Exception::BUILD_NOT_FOUND); } if ($build->getAttribute('status') !== 'failed') { - throw new Exception('Build not failed', 400, Exception::BUILD_IN_PROGRESS); + throw new Exception(Exception::BUILD_IN_PROGRESS, 'Build not failed'); } $events diff --git a/app/controllers/api/graphql.php b/app/controllers/api/graphql.php index e89ae17961..f4df08ce05 100644 --- a/app/controllers/api/graphql.php +++ b/app/controllers/api/graphql.php @@ -19,6 +19,6 @@ App::post('/v1/graphql') ->label('scope', 'public') ->action( function () { - throw new Exception('GraphQL support is coming soon!', 502, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GraphQL support is coming soon!', 503); } ); diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 9eb9ca580e..4384fcbcd5 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -73,7 +73,7 @@ App::get('/v1/health/db') $statement->execute(); } catch (Exception $_e) { - throw new Exception('Database is not available', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Database is not available'); } $output = [ @@ -104,7 +104,7 @@ App::get('/v1/health/cache') $redis = $utopia->getResource('cache'); if (!$redis->ping(true)) { - throw new Exception('Cache is not available', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Cache is not available'); } $output = [ @@ -160,7 +160,7 @@ App::get('/v1/health/time') $diff = ($timestamp - \time()); if ($diff > $gap || $diff < ($gap * -1)) { - throw new Exception('Server time gaps detected', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Server time gaps detected'); } $output = [ @@ -267,11 +267,11 @@ App::get('/v1/health/storage/local') $device = new Local($volume); if (!\is_readable($device->getRoot())) { - throw new Exception('Device ' . $key . ' dir is not readable', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Device ' . $key . ' dir is not readable'); } if (!\is_writable($device->getRoot())) { - throw new Exception('Device ' . $key . ' dir is not writable', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Device ' . $key . ' dir is not writable'); } } @@ -315,7 +315,7 @@ App::get('/v1/health/anti-virus') $output['version'] = @$antivirus->version(); $output['status'] = (@$antivirus->ping()) ? 'pass' : 'fail'; } catch (\Exception $e) { - throw new Exception('Antivirus is not available', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Antivirus is not available'); } } diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 504d362b94..f10ae175c3 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -37,7 +37,7 @@ App::init() ->inject('project') ->action(function (Document $project) { if ($project->getId() !== 'console') { - throw new Exception('Access to this API is forbidden.', 401, Exception::GENERAL_ACCESS_FORBIDDEN); + throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN); } }); @@ -71,7 +71,7 @@ App::post('/v1/projects') $team = $dbForConsole->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $auth = Config::getParam('auth', []); @@ -83,7 +83,7 @@ App::post('/v1/projects') $projectId = ($projectId == 'unique()') ? $dbForConsole->getId() : $projectId; if ($projectId === 'console') { - throw new Exception("'console' is a reserved project.", 400, Exception::PROJECT_RESERVED_PROJECT); + throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project."); } $project = $dbForConsole->createDocument('projects', new Document([ @@ -196,7 +196,7 @@ App::get('/v1/projects') $cursorDocument = $dbForConsole->getDocument('projects', $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Project '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Project '{$cursor}' for the 'cursor' value not found."); } $queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -229,7 +229,7 @@ App::get('/v1/projects/:projectId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $response->dynamic($project, Response::MODEL_PROJECT); @@ -256,7 +256,7 @@ App::get('/v1/projects/:projectId/usage') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $usage = []; @@ -375,7 +375,7 @@ App::patch('/v1/projects/:projectId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $project = $dbForConsole->updateDocument('projects', $project->getId(), $project @@ -414,7 +414,7 @@ App::patch('/v1/projects/:projectId/service') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $services = $project->getAttribute('services', []); @@ -446,7 +446,7 @@ App::patch('/v1/projects/:projectId/oauth2') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $providers = $project->getAttribute('authProviders', []); @@ -477,7 +477,7 @@ App::patch('/v1/projects/:projectId/auth/limit') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $auths = $project->getAttribute('auths', []); @@ -512,7 +512,7 @@ App::patch('/v1/projects/:projectId/auth/:method') $status = ($status === '1' || $status === 'true' || $status === 1 || $status === true); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $auths = $project->getAttribute('auths', []); @@ -541,13 +541,13 @@ App::delete('/v1/projects/:projectId') ->action(function (string $projectId, string $password, Response $response, Document $user, Database $dbForConsole, Delete $deletes) { if (!Auth::passwordVerify($password, $user->getAttribute('password'))) { // Double check user password - throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); + throw new Exception(Exception::USER_INVALID_CREDENTIALS); } $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $deletes @@ -556,11 +556,11 @@ App::delete('/v1/projects/:projectId') ; if (!$dbForConsole->deleteDocument('teams', $project->getAttribute('teamId', null))) { - throw new Exception('Failed to remove project team from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove project team from DB'); } if (!$dbForConsole->deleteDocument('projects', $projectId)) { - throw new Exception('Failed to remove project from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove project from DB'); } $response->noContent(); @@ -592,7 +592,7 @@ App::post('/v1/projects/:projectId/webhooks') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $security = (bool) filter_var($security, FILTER_VALIDATE_BOOLEAN); @@ -638,7 +638,7 @@ App::get('/v1/projects/:projectId/webhooks') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $webhooks = $dbForConsole->find('webhooks', [ @@ -671,7 +671,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $webhook = $dbForConsole->findOne('webhooks', [ @@ -680,7 +680,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') ]); if ($webhook === false || $webhook->isEmpty()) { - throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND); + throw new Exception(Exception::WEBHOOK_NOT_FOUND); } $response->dynamic($webhook, Response::MODEL_WEBHOOK); @@ -711,7 +711,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $security = ($security === '1' || $security === 'true' || $security === 1 || $security === true); @@ -722,7 +722,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') ]); if ($webhook === false || $webhook->isEmpty()) { - throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND); + throw new Exception(Exception::WEBHOOK_NOT_FOUND); } $webhook @@ -759,7 +759,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $webhook = $dbForConsole->findOne('webhooks', [ @@ -768,7 +768,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature') ]); if ($webhook === false || $webhook->isEmpty()) { - throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND); + throw new Exception(Exception::WEBHOOK_NOT_FOUND); } $webhook->setAttribute('signatureKey', \bin2hex(\random_bytes(64))); @@ -797,7 +797,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $webhook = $dbForConsole->findOne('webhooks', [ @@ -806,7 +806,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') ]); if ($webhook === false || $webhook->isEmpty()) { - throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND); + throw new Exception(Exception::WEBHOOK_NOT_FOUND); } $dbForConsole->deleteDocument('webhooks', $webhook->getId()); @@ -839,7 +839,7 @@ App::post('/v1/projects/:projectId/keys') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $key = new Document([ @@ -880,7 +880,7 @@ App::get('/v1/projects/:projectId/keys') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $keys = $dbForConsole->find('keys', [ @@ -913,7 +913,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $key = $dbForConsole->findOne('keys', [ @@ -922,7 +922,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') ]); if ($key === false || $key->isEmpty()) { - throw new Exception('Key not found', 404, Exception::KEY_NOT_FOUND); + throw new Exception(Exception::KEY_NOT_FOUND); } $response->dynamic($key, Response::MODEL_KEY); @@ -950,7 +950,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $key = $dbForConsole->findOne('keys', [ @@ -959,7 +959,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') ]); if ($key === false || $key->isEmpty()) { - throw new Exception('Key not found', 404, Exception::KEY_NOT_FOUND); + throw new Exception(Exception::KEY_NOT_FOUND); } $key @@ -993,7 +993,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $key = $dbForConsole->findOne('keys', [ @@ -1002,7 +1002,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') ]); if ($key === false || $key->isEmpty()) { - throw new Exception('Key not found', 404, Exception::KEY_NOT_FOUND); + throw new Exception(Exception::KEY_NOT_FOUND); } $dbForConsole->deleteDocument('keys', $key->getId()); @@ -1036,7 +1036,7 @@ App::post('/v1/projects/:projectId/platforms') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $platform = new Document([ @@ -1078,7 +1078,7 @@ App::get('/v1/projects/:projectId/platforms') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $platforms = $dbForConsole->find('platforms', [ @@ -1111,7 +1111,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $platform = $dbForConsole->findOne('platforms', [ @@ -1120,7 +1120,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') ]); if ($platform === false || $platform->isEmpty()) { - throw new Exception('Platform not found', 404, Exception::PLATFORM_NOT_FOUND); + throw new Exception(Exception::PLATFORM_NOT_FOUND); } $response->dynamic($platform, Response::MODEL_PLATFORM); @@ -1148,7 +1148,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $platform = $dbForConsole->findOne('platforms', [ @@ -1157,7 +1157,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') ]); if ($platform === false || $platform->isEmpty()) { - throw new Exception('Platform not found', 404, Exception::PLATFORM_NOT_FOUND); + throw new Exception(Exception::PLATFORM_NOT_FOUND); } $platform @@ -1192,7 +1192,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $platform = $dbForConsole->findOne('platforms', [ @@ -1201,7 +1201,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') ]); if ($platform === false || $platform->isEmpty()) { - throw new Exception('Platform not found', 404, Exception::PLATFORM_NOT_FOUND); + throw new Exception(Exception::PLATFORM_NOT_FOUND); } $dbForConsole->deleteDocument('platforms', $platformId); @@ -1232,7 +1232,7 @@ App::post('/v1/projects/:projectId/domains') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $document = $dbForConsole->findOne('domains', [ @@ -1241,13 +1241,13 @@ App::post('/v1/projects/:projectId/domains') ]); if ($document && !$document->isEmpty()) { - throw new Exception('Domain already exists', 409, Exception::DOMAIN_ALREADY_EXISTS); + throw new Exception(Exception::DOMAIN_ALREADY_EXISTS); } $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); if (!$target->isKnown() || $target->isTest()) { - throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); } $domain = new Domain($domain); @@ -1292,7 +1292,7 @@ App::get('/v1/projects/:projectId/domains') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $domains = $dbForConsole->find('domains', [ @@ -1325,7 +1325,7 @@ App::get('/v1/projects/:projectId/domains/:domainId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $domain = $dbForConsole->findOne('domains', [ @@ -1334,7 +1334,7 @@ App::get('/v1/projects/:projectId/domains/:domainId') ]); if ($domain === false || $domain->isEmpty()) { - throw new Exception('Domain not found', 404, Exception::DOMAIN_NOT_FOUND); + throw new Exception(Exception::DOMAIN_NOT_FOUND); } $response->dynamic($domain, Response::MODEL_DOMAIN); @@ -1359,7 +1359,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $domain = $dbForConsole->findOne('domains', [ @@ -1368,13 +1368,13 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') ]); if ($domain === false || $domain->isEmpty()) { - throw new Exception('Domain not found', 404, Exception::DOMAIN_NOT_FOUND); + throw new Exception(Exception::DOMAIN_NOT_FOUND); } $target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', '')); if (!$target->isKnown() || $target->isTest()) { - throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.'); } if ($domain->getAttribute('verification') === true) { @@ -1384,7 +1384,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') $validator = new CNAME($target->get()); // Verify Domain with DNS records if (!$validator->isValid($domain->getAttribute('domain', ''))) { - throw new Exception('Failed to verify domain', 401, Exception::DOMAIN_VERIFICATION_FAILED); + throw new Exception(Exception::DOMAIN_VERIFICATION_FAILED); } @@ -1419,7 +1419,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId') $project = $dbForConsole->getDocument('projects', $projectId); if ($project->isEmpty()) { - throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND); + throw new Exception(Exception::PROJECT_NOT_FOUND); } $domain = $dbForConsole->findOne('domains', [ @@ -1428,7 +1428,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId') ]); if ($domain === false || $domain->isEmpty()) { - throw new Exception('Domain not found', 404, Exception::DOMAIN_NOT_FOUND); + throw new Exception(Exception::DOMAIN_NOT_FOUND); } $dbForConsole->deleteDocument('domains', $domain->getId()); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 1c2c38b1db..c4a3214efa 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -75,7 +75,7 @@ App::post('/v1/storage/buckets') try { $files = Config::getParam('collections', [])['files'] ?? []; if (empty($files)) { - throw new Exception('Files collection is not configured.', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Files collection is not configured.'); } $attributes = []; @@ -124,7 +124,7 @@ App::post('/v1/storage/buckets') $dbForProject->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); } catch (Duplicate $th) { - throw new Exception('Bucket already exists', 409, Exception::STORAGE_BUCKET_ALREADY_EXISTS); + throw new Exception(Exception::STORAGE_BUCKET_ALREADY_EXISTS); } $audits @@ -178,7 +178,7 @@ App::get('/v1/storage/buckets') $cursorDocument = $dbForProject->getDocument('buckets', $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Bucket '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Bucket '{$cursor}' for the 'cursor' value not found."); } $queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -212,7 +212,7 @@ App::get('/v1/storage/buckets/:bucketId') $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $usage->setParam('storage.buckets.read', 1); @@ -251,7 +251,7 @@ App::put('/v1/storage/buckets/:bucketId') $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $read ??= $bucket->getAttribute('$read', []); // By default inherit read permissions @@ -309,11 +309,11 @@ App::delete('/v1/storage/buckets/:bucketId') $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } if (!$dbForProject->deleteDocument('buckets', $bucketId)) { - throw new Exception('Failed to remove project from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove bucket from DB'); } $deletes @@ -372,7 +372,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced @@ -380,7 +380,7 @@ App::post('/v1/storage/buckets/:bucketId/files') if ($permissionBucket) { $validator = new Authorization('write'); if (!$validator->isValid($bucket->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -393,12 +393,12 @@ App::post('/v1/storage/buckets/:bucketId/files') if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach ($read as $role) { if (!Authorization::isRole($role)) { - throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400); } } foreach ($write as $role) { if (!Authorization::isRole($role)) { - throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400); } } } @@ -413,12 +413,12 @@ App::post('/v1/storage/buckets/:bucketId/files') $maximumFileSize = $bucket->getAttribute('maximumFileSize', 0); if ($maximumFileSize > (int) App::getEnv('_APP_STORAGE_LIMIT', 0)) { - throw new Exception('Maximum bucket file size is larger than _APP_STORAGE_LIMIT', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Maximum bucket file size is larger than _APP_STORAGE_LIMIT'); } $file = $request->getFiles('file'); if (empty($file)) { - throw new Exception('No file sent', 400, Exception::STORAGE_FILE_EMPTY); + throw new Exception(Exception::STORAGE_FILE_EMPTY); } // Make sure we handle a single file and multiple files the same way @@ -437,7 +437,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $fileSize = $request->getContentRangeSize(); $fileId = $request->getHeader('x-appwrite-id', $fileId); if (is_null($start) || is_null($end) || is_null($fileSize)) { - throw new Exception('Invalid content-range header', 400, Exception::STORAGE_INVALID_CONTENT_RANGE); + throw new Exception(Exception::STORAGE_INVALID_CONTENT_RANGE); } if ($end === $fileSize) { @@ -457,18 +457,18 @@ App::post('/v1/storage/buckets/:bucketId/files') $allowedFileExtensions = $bucket->getAttribute('allowedFileExtensions', []); $fileExt = new FileExt($allowedFileExtensions); if (!empty($allowedFileExtensions) && !$fileExt->isValid($fileName)) { - throw new Exception('File extension not allowed', 400, Exception::STORAGE_FILE_TYPE_UNSUPPORTED); + throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED, 'File extension not allowed'); } // Check if file size is exceeding allowed limit $fileSizeValidator = new FileSize($maximumFileSize); if (!$fileSizeValidator->isValid($fileSize)) { - throw new Exception('File size not allowed', 400, Exception::STORAGE_INVALID_FILE_SIZE); + throw new Exception(Exception::STORAGE_INVALID_FILE_SIZE, 'File size not allowed'); } $upload = new Upload(); if (!$upload->isValid($fileTmpName)) { - throw new Exception('Invalid file', 403, Exception::STORAGE_INVALID_FILE); + throw new Exception(Exception::STORAGE_INVALID_FILE); } // Save to storage @@ -495,7 +495,7 @@ App::post('/v1/storage/buckets/:bucketId/files') $chunksUploaded = $deviceFiles->upload($fileTmpName, $path, $chunk, $chunks, $metadata); if (empty($chunksUploaded)) { - throw new Exception('Failed uploading file', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed uploading file'); } $read = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; @@ -509,7 +509,7 @@ App::post('/v1/storage/buckets/:bucketId/files') if (!$antivirus->fileScan($path)) { $deviceFiles->delete($path); - throw new Exception('Invalid file', 400, Exception::STORAGE_INVALID_FILE); + throw new Exception(Exception::STORAGE_INVALID_FILE); } } @@ -533,7 +533,7 @@ App::post('/v1/storage/buckets/:bucketId/files') if (!empty($data)) { if (!$deviceFiles->write($path, $data, $mimeType)) { - throw new Exception('Failed to save file', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to save file'); } } @@ -600,9 +600,9 @@ App::post('/v1/storage/buckets/:bucketId/files') } } } catch (StructureException $exception) { - throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage()); } catch (DuplicateException $exception) { - throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS); + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } $audits @@ -652,9 +652,9 @@ App::post('/v1/storage/buckets/:bucketId/files') } } } catch (StructureException $exception) { - throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE); + throw new Exception(Exception::DOCUMENT_INVALID_STRUCTURE, $exception->getMessage()); } catch (DuplicateException $exception) { - throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS); + throw new Exception(Exception::DOCUMENT_ALREADY_EXISTS); } } @@ -701,14 +701,14 @@ App::get('/v1/storage/buckets/:bucketId/files') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('read'); if (!$validator->isValid($bucket->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -730,7 +730,7 @@ App::get('/v1/storage/buckets/:bucketId/files') } if ($cursorDocument->isEmpty()) { - throw new Exception("File '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "File '{$cursor}' for the 'cursor' value not found."); } $queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -781,14 +781,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('read'); if (!$validator->isValid($bucket->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -799,7 +799,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') } if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $usage @@ -846,7 +846,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, Stats $usage, string $mode, Device $deviceFiles, Device $deviceLocal) { if (!\extension_loaded('imagick')) { - throw new Exception('Imagick extension is missing', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); } $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -855,14 +855,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('read'); if (!$validator->isValid($bucket->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND, 'Unauthorized permissions'); } } @@ -885,7 +885,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') } if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $path = $file->getAttribute('path'); @@ -914,7 +914,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $compressor = new GZIP(); if (!$deviceFiles->exists($path)) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $cache = new Cache(new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId() . DIRECTORY_SEPARATOR . $bucketId . DIRECTORY_SEPARATOR . $fileId)); // Limit file number or size @@ -1023,14 +1023,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('read'); if (!$validator->isValid($bucket->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -1041,13 +1041,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') } if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $path = $file->getAttribute('path', ''); if (!$deviceFiles->exists($path)) { - throw new Exception('File not found in ' . $path, 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path); } $usage @@ -1075,7 +1075,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') } if ($unit !== 'bytes' || $start >= $end || $end >= $size) { - throw new Exception('Invalid range', 416, Exception::STORAGE_INVALID_RANGE); + throw new Exception(Exception::STORAGE_INVALID_RANGE); } $response @@ -1162,14 +1162,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('read'); if (!$validator->isValid($bucket->getRead())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -1182,13 +1182,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $mimes = Config::getParam('storage-mimes'); if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $path = $file->getAttribute('path', ''); if (!$deviceFiles->exists($path)) { - throw new Exception('File not found in ' . $path, 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path); } $compressor = new GZIP(); @@ -1221,7 +1221,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') } if ($unit != 'bytes' || $start >= $end || $end >= $size) { - throw new Exception('Invalid range', 416, Exception::STORAGE_INVALID_RANGE); + throw new Exception(Exception::STORAGE_INVALID_RANGE); } $response @@ -1321,12 +1321,12 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) { foreach ($read as $role) { if (!Authorization::isRole($role)) { - throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400); } } foreach ($write as $role) { if (!Authorization::isRole($role)) { - throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400); } } } @@ -1335,14 +1335,14 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('write'); if (!$validator->isValid($bucket->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -1353,7 +1353,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') } if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $file @@ -1412,14 +1412,14 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') $bucket->isEmpty() || (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN) ) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } // Check bucket permissions when enforced if ($bucket->getAttribute('permission') === 'bucket') { $validator = new Authorization('write'); if (!$validator->isValid($bucket->getWrite())) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } } @@ -1430,7 +1430,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') } if ($file->isEmpty() || $file->getAttribute('bucketId') !== $bucketId) { - throw new Exception('File not found', 404, Exception::STORAGE_FILE_NOT_FOUND); + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } $deviceDeleted = false; @@ -1455,10 +1455,10 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') $deleted = $dbForProject->deleteDocument('bucket_' . $bucket->getInternalId(), $fileId); } if (!$deleted) { - throw new Exception('Failed to remove file from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove file from DB'); } } else { - throw new Exception('Failed to delete file from device', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to delete file from device'); } $audits->setResource('file/' . $file->getId()); @@ -1609,7 +1609,7 @@ App::get('/v1/storage/:bucketId/usage') $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { - throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND); + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } $usage = []; diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 5b6503ccdc..ea5fb59110 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -141,7 +141,7 @@ App::get('/v1/teams') $cursorDocument = $dbForProject->getDocument('teams', $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Team '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Team '{$cursor}' for the 'cursor' value not found."); } $queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -175,7 +175,7 @@ App::get('/v1/teams/:teamId') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $response->dynamic($team, Response::MODEL_TEAM); @@ -204,7 +204,7 @@ App::put('/v1/teams/:teamId') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $team = $dbForProject->updateDocument('teams', $team->getId(), $team @@ -239,7 +239,7 @@ App::delete('/v1/teams/:teamId') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $memberships = $dbForProject->find('memberships', [ @@ -250,12 +250,12 @@ App::delete('/v1/teams/:teamId') // TODO delete all members individually from the user object foreach ($memberships as $membership) { if (!$dbForProject->deleteDocument('memberships', $membership->getId())) { - throw new Exception('Failed to remove membership for team from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove membership for team from DB'); } } if (!$dbForProject->deleteDocument('teams', $teamId)) { - throw new Exception('Failed to remove team from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove team from DB'); } $deletes @@ -309,7 +309,7 @@ App::post('/v1/teams/:teamId/memberships') $isAppUser = Auth::isAppUser(Authorization::getRoles()); if (!$isPrivilegedUser && !$isAppUser && empty(App::getEnv('_APP_SMTP_HOST'))) { - throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED); + throw new Exception(Exception::GENERAL_SMTP_DISABLED); } $email = \strtolower($email); @@ -317,7 +317,7 @@ App::post('/v1/teams/:teamId/memberships') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $invitee = $dbForProject->findOne('users', [Query::equal('email', [$email])]); // Get user by email address @@ -329,7 +329,7 @@ App::post('/v1/teams/:teamId/memberships') $total = $dbForProject->count('users', [], 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, 'Project registration is restricted. Contact your administrator for more information.'); } } @@ -359,14 +359,14 @@ App::post('/v1/teams/:teamId/memberships') '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); } } $isOwner = Authorization::isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) - throw new Exception('User is not allowed to send invitations for this team', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team'); } $secret = Auth::tokenGenerator(); @@ -392,7 +392,7 @@ App::post('/v1/teams/:teamId/memberships') try { $membership = Authorization::skip(fn() => $dbForProject->createDocument('memberships', $membership)); } catch (Duplicate $th) { - throw new Exception('User is already a member of this team', 409, Exception::TEAM_INVITE_ALREADY_EXISTS); + throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS); } $team->setAttribute('total', $team->getAttribute('total', 0) + 1); $team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team)); @@ -402,7 +402,7 @@ App::post('/v1/teams/:teamId/memberships') try { $membership = $dbForProject->createDocument('memberships', $membership); } catch (Duplicate $th) { - throw new Exception('User has already been invited or is already a member of this team', 409, Exception::TEAM_INVITE_ALREADY_EXISTS); + throw new Exception(Exception::TEAM_INVITE_ALREADY_EXISTS); } } @@ -467,7 +467,7 @@ App::get('/v1/teams/:teamId/memberships') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $filterQueries = [Query::equal('teamId', [$teamId])]; @@ -484,7 +484,7 @@ App::get('/v1/teams/:teamId/memberships') $cursorDocument = $dbForProject->getDocument('memberships', $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("Membership '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Membership '{$cursor}' for the 'cursor' value not found."); } $otherQueries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -541,13 +541,13 @@ App::get('/v1/teams/:teamId/memberships/:membershipId') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $membership = $dbForProject->getDocument('memberships', $membershipId); if ($membership->isEmpty() || empty($membership->getAttribute('userId'))) { - throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND); + throw new Exception(Exception::MEMBERSHIP_NOT_FOUND); } $user = $dbForProject->getDocument('users', $membership->getAttribute('userId')); @@ -586,17 +586,17 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $membership = $dbForProject->getDocument('memberships', $membershipId); if ($membership->isEmpty()) { - throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND); + throw new Exception(Exception::MEMBERSHIP_NOT_FOUND); } $profile = $dbForProject->getDocument('users', $membership->getAttribute('userId')); if ($profile->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -604,7 +604,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') $isOwner = Authorization::isRole('team:' . $team->getId() . '/owner'); if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server) - throw new Exception('User is not allowed to modify roles', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to modify roles'); } /** @@ -662,25 +662,25 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $membership = $dbForProject->getDocument('memberships', $membershipId); if ($membership->isEmpty()) { - throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND); + throw new Exception(Exception::MEMBERSHIP_NOT_FOUND); } if ($membership->getAttribute('teamId') !== $teamId) { - throw new Exception('Team IDs don\'t match', 404, Exception::TEAM_MEMBERSHIP_MISMATCH); + throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH); } $team = Authorization::skip(fn() => $dbForProject->getDocument('teams', $teamId)); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } if (Auth::hash($secret) !== $membership->getAttribute('secret')) { - throw new Exception('Secret key not valid', 401, Exception::TEAM_INVALID_SECRET); + throw new Exception(Exception::TEAM_INVALID_SECRET); } if ($userId !== $membership->getAttribute('userId')) { - throw new Exception('Invite does not belong to current user (' . $user->getAttribute('email') . ')', 401, Exception::TEAM_INVITE_MISMATCH); + throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')'); } if ($user->isEmpty()) { @@ -688,11 +688,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') } if ($membership->getAttribute('userId') !== $user->getId()) { - throw new Exception('Invite does not belong to current user (' . $user->getAttribute('email') . ')', 401, Exception::TEAM_INVITE_MISMATCH); + throw new Exception(Exception::TEAM_INVITE_MISMATCH, 'Invite does not belong to current user (' . $user->getAttribute('email') . ')'); } if ($membership->getAttribute('confirm') === true) { - throw new Exception('Membership already confirmed', 409); + throw new Exception(Exception::MEMBERSHIP_ALREADY_CONFIRMED); } $membership // Attach user to team @@ -788,31 +788,31 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') $membership = $dbForProject->getDocument('memberships', $membershipId); if ($membership->isEmpty()) { - throw new Exception('Invite not found', 404, Exception::TEAM_INVITE_NOT_FOUND); + throw new Exception(Exception::TEAM_INVITE_NOT_FOUND); } if ($membership->getAttribute('teamId') !== $teamId) { - throw new Exception('Team IDs don\'t match', 404); + throw new Exception(Exception::TEAM_MEMBERSHIP_MISMATCH); } $user = $dbForProject->getDocument('users', $membership->getAttribute('userId')); if ($user->isEmpty()) { - throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); + throw new Exception(Exception::USER_NOT_FOUND); } $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } try { $dbForProject->deleteDocument('memberships', $membership->getId()); } catch (AuthorizationException $exception) { - throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED); + throw new Exception(Exception::USER_UNAUTHORIZED); } catch (\Exception $exception) { - throw new Exception('Failed to remove membership from DB', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove membership from DB'); } $dbForProject->deleteCachedDocument('users', $user->getId()); @@ -861,7 +861,7 @@ App::get('/v1/teams/:teamId/logs') $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { - throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND); + throw new Exception(Exception::TEAM_NOT_FOUND); } $audit = new Audit($dbForProject); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 320ee44868..c7cb3ba3a8 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -75,7 +75,7 @@ App::post('/v1/users') '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); } $usage @@ -126,7 +126,7 @@ App::get('/v1/users') $cursorDocument = $dbForProject->getDocument('users', $cursor); if ($cursorDocument->isEmpty()) { - throw new Exception("User '{$cursor}' for the 'cursor' value not found.", 400, Exception::GENERAL_CURSOR_NOT_FOUND); + throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "User '{$cursor}' for the 'cursor' value not found."); } $queries[] = $cursorDirection === Database::CURSOR_AFTER ? Query::cursorAfter($cursorDocument) : Query::cursorBefore($cursorDocument); @@ -162,7 +162,7 @@ App::get('/v1/users/:userId') $user = $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); } $usage @@ -191,7 +191,7 @@ App::get('/v1/users/:userId/prefs') $user = $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); } $prefs = $user->getAttribute('prefs', new \stdClass()); @@ -223,7 +223,7 @@ App::get('/v1/users/:userId/sessions') $user = $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); } $sessions = $user->getAttribute('sessions', []); @@ -266,7 +266,7 @@ App::get('/v1/users/:userId/memberships') $user = $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); } $memberships = array_map(function ($membership) use ($dbForProject, $user) { @@ -310,7 +310,7 @@ App::get('/v1/users/:userId/logs') $user = $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); } $audit = new Audit($dbForProject); @@ -391,7 +391,7 @@ App::patch('/v1/users/:userId/status') $user = $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); } $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status)); @@ -430,7 +430,7 @@ App::patch('/v1/users/:userId/verification') $user = $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); } $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification)); @@ -469,7 +469,7 @@ App::patch('/v1/users/:userId/verification/phone') $user = $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); } $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('phoneVerification', $phoneVerification)); @@ -508,7 +508,7 @@ App::patch('/v1/users/:userId/name') $user = $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); } $user @@ -552,7 +552,7 @@ App::patch('/v1/users/:userId/password') $user = $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); } $user @@ -595,7 +595,7 @@ App::patch('/v1/users/:userId/email') $user = $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); } $email = \strtolower($email); @@ -609,7 +609,7 @@ App::patch('/v1/users/:userId/email') try { $user = $dbForProject->updateDocument('users', $user->getId(), $user); } catch (Duplicate $th) { - throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS); + throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } @@ -647,7 +647,7 @@ App::patch('/v1/users/:userId/phone') $user = $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); } $user @@ -659,7 +659,7 @@ App::patch('/v1/users/:userId/phone') try { $user = $dbForProject->updateDocument('users', $user->getId(), $user); } catch (Duplicate $th) { - throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS); + throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); } @@ -697,7 +697,7 @@ App::patch('/v1/users/:userId/prefs') $user = $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); } $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs)); @@ -735,13 +735,13 @@ App::delete('/v1/users/:userId/sessions/:sessionId') $user = $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); } $session = $dbForProject->getDocument('sessions', $sessionId); if ($session->isEmpty()) { - throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND); + throw new Exception(Exception::USER_SESSION_NOT_FOUND); } $dbForProject->deleteDocument('sessions', $session->getId()); @@ -782,7 +782,7 @@ App::delete('/v1/users/:userId/sessions') $user = $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); } $sessions = $user->getAttribute('sessions', []); @@ -829,7 +829,7 @@ App::delete('/v1/users/:userId') $user = $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); } // clone user object to send to workers diff --git a/app/controllers/general.php b/app/controllers/general.php index 068fac3ccb..3a08742429 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -132,11 +132,11 @@ App::init() } if ($project->isEmpty()) { - throw new AppwriteException('Project not found', 404, AppwriteException::PROJECT_NOT_FOUND); + throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND); } if (!empty($route->getLabel('sdk.auth', [])) && $project->isEmpty() && ($route->getLabel('scope', '') !== 'public')) { - throw new AppwriteException('Missing or unknown project ID', 400, AppwriteException::PROJECT_UNKNOWN); + throw new AppwriteException(AppwriteException::PROJECT_UNKNOWN); } $referrer = $request->getReferer(); @@ -207,7 +207,7 @@ App::init() if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS if ($request->getProtocol() !== 'https') { if ($request->getMethod() !== Request::METHOD_GET) { - throw new AppwriteException('Method unsupported over HTTP.', 500, AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED); + throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP.'); } return $response->redirect('https://' . $request->getHostname() . $request->getURI()); @@ -240,7 +240,7 @@ App::init() && $route->getLabel('origin', false) !== '*' && empty($request->getHeader('x-appwrite-key', '')) ) { - throw new AppwriteException($originValidator->getDescription(), 403, AppwriteException::GENERAL_UNKNOWN_ORIGIN); + throw new AppwriteException(AppwriteException::GENERAL_UNKNOWN_ORIGIN, $originValidator->getDescription()); } /* @@ -296,7 +296,7 @@ App::init() $expire = $key->getAttribute('expire'); if (!empty($expire) && $expire < DateTime::now()) { - throw new AppwriteException('Project key expired', 401, AppwriteException:: PROJECT_KEY_EXPIRED); + throw new AppwriteException(AppwriteException:: PROJECT_KEY_EXPIRED); } Authorization::setRole('role:' . Auth::USER_ROLE_APP); @@ -317,24 +317,24 @@ App::init() && !$project->getAttribute('services', [])[$service] && !(Auth::isPrivilegedUser(Authorization::getRoles()) || Auth::isAppUser(Authorization::getRoles())) ) { - throw new AppwriteException('Service is disabled', 503, AppwriteException::GENERAL_SERVICE_DISABLED); + throw new AppwriteException(AppwriteException::GENERAL_SERVICE_DISABLED); } } if (!\in_array($scope, $scopes)) { if ($project->isEmpty()) { // Check if permission is denied because project is missing - throw new AppwriteException('Project not found', 404, AppwriteException::PROJECT_NOT_FOUND); + throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND); } - throw new AppwriteException($user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')', 401, AppwriteException::GENERAL_UNAUTHORIZED_SCOPE); + throw new AppwriteException(AppwriteException::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')'); } if (false === $user->getAttribute('status')) { // Account is blocked - throw new AppwriteException('Invalid credentials. User is blocked', 401, AppwriteException::USER_BLOCKED); + throw new AppwriteException(AppwriteException::USER_BLOCKED); } if ($user->getAttribute('reset')) { - throw new AppwriteException('Password reset is required', 412, AppwriteException::USER_PASSWORD_RESET_REQUIRED); + throw new AppwriteException(AppwriteException::USER_PASSWORD_RESET_REQUIRED); } }); @@ -446,7 +446,7 @@ App::error() /** Handle Utopia Errors */ if ($error instanceof Utopia\Exception) { - $error = new AppwriteException($message, $code, AppwriteException::GENERAL_UNKNOWN, $error); + $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); switch ($code) { case 400: $error->setType(AppwriteException::GENERAL_ARGUMENT_INVALID); @@ -459,7 +459,7 @@ App::error() /** Wrap all exceptions inside Appwrite\Extend\Exception */ if (!($error instanceof AppwriteException)) { - $error = new AppwriteException($message, $code, AppwriteException::GENERAL_UNKNOWN, $error); + $error = new AppwriteException(AppwriteException::GENERAL_UNKNOWN, $message, $code, $error); } switch ($code) { // Don't show 500 errors! @@ -602,32 +602,32 @@ App::get('/.well-known/acme-challenge') ]); if (!$validator->isValid($token) || \count($uriChunks) !== 4) { - throw new AppwriteException('Invalid challenge token.', 400); + throw new AppwriteException(AppwriteException::GENERAL_ARGUMENT_INVALID, 'Invalid challenge token.'); } $base = \realpath(APP_STORAGE_CERTIFICATES); $absolute = \realpath($base . '/.well-known/acme-challenge/' . $token); if (!$base) { - throw new AppwriteException('Storage error', 500, AppwriteException::GENERAL_SERVER_ERROR); + throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Storage error'); } if (!$absolute) { - throw new AppwriteException('Unknown path', 404); + throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND, 'Unknown path'); } if (!\substr($absolute, 0, \strlen($base)) === $base) { - throw new AppwriteException('Invalid path', 401); + throw new AppwriteException(AppwriteException::GENERAL_UNAUTHORIZED_SCOPE, 'Invalid path'); } if (!\file_exists($absolute)) { - throw new AppwriteException('Unknown path', 404); + throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND, 'Unknown path'); } $content = @\file_get_contents($absolute); if (!$content) { - throw new AppwriteException('Failed to get contents', 500, AppwriteException::GENERAL_SERVER_ERROR); + throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Failed to get contents'); } $response->text($content); diff --git a/app/controllers/mock.php b/app/controllers/mock.php index d3b150a55f..0c6b986277 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::GENERAL_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::GENERAL_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::GENERAL_MOCK, 'First chunked request cannot have id header'); } 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::GENERAL_MOCK, 'All chunked request must have id header (except first)'); } 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::GENERAL_MOCK, 'Chunk size must be 5MB (except last chunk)'); } if ($end !== $size && $file['size'] !== $chunkSize) { - throw new Exception('Wrong chunk size', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK, 'Wrong chunk size'); } if ($file['size'] > $chunkSize) { - throw new Exception('Chunk size must be 5MB or less', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK, 'Chunk size must be 5MB or less'); } 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::GENERAL_MOCK, 'Wrong file name'); } if ($file['size'] !== 38756) { - throw new Exception('Wrong file size', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_MOCK, 'Wrong 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::GENERAL_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::GENERAL_MOCK, 'Missing cookie value'); } }); @@ -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::GENERAL_MOCK, 'Mock 400 error'); }); 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::GENERAL_MOCK, 'Mock 500 error', 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::GENERAL_MOCK, 'Invalid client ID'); } if ($client_secret != '123456') { - throw new Exception('Invalid client secret', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_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::GENERAL_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::GENERAL_MOCK, 'Invalid refresh token'); } $response->json($responseJson); } else { - throw new Exception('Invalid grant type', 400, Exception::GENERAL_MOCK); + throw new Exception(Exception::GENERAL_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::GENERAL_MOCK, 'Invalid token'); } $response->json([ @@ -571,7 +571,7 @@ App::shutdown() $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::GENERAL_MOCK, 'Failed to read results', 500); } $result[$route->getMethod() . ':' . $route->getPath()] = true; @@ -579,7 +579,7 @@ App::shutdown() $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::GENERAL_MOCK, 'Failed to save results', 500); } $response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getPath() . ':passed']), Response::MODEL_MOCK); diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 1ba37eb920..559d29e17e 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -39,7 +39,7 @@ App::init() $route = $utopia->match($request); if ($project->isEmpty() && $route->getLabel('abuse-limit', 0) > 0) { // Abuse limit requires an active project scope - throw new Exception('Missing or unknown project ID', 400, Exception::PROJECT_UNKNOWN); + throw new Exception(Exception::PROJECT_UNKNOWN); } /* @@ -92,7 +92,7 @@ App::init() && $abuse->check()) // Abuse is not disabled && (!$isAppUser && !$isPrivilegedUser) ) { // User is not an admin or API key - throw new Exception('Too many requests', 429, Exception::GENERAL_RATE_LIMIT_EXCEEDED); + throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED); } } @@ -154,36 +154,37 @@ App::init() switch ($route->getLabel('auth.type', '')) { case 'emailPassword': if (($auths['emailPassword'] ?? true) === false) { - throw new Exception('Email / Password authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Email / Password authentication is disabled for this project'); } break; case 'magic-url': if ($project->getAttribute('usersAuthMagicURL', true) === false) { - throw new Exception('Magic URL authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Magic URL authentication is disabled for this project'); } break; case 'anonymous': if (($auths['anonymous'] ?? true) === false) { - throw new Exception('Anonymous authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Anonymous authentication is disabled for this project'); } break; case 'invites': if (($auths['invites'] ?? true) === false) { - throw new Exception('Invites authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Invites authentication is disabled for this project'); } break; case 'jwt': if (($auths['JWT'] ?? true) === false) { - throw new Exception('JWT authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'JWT authentication is disabled for this project'); } break; default: - throw new Exception('Unsupported authentication route', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED); + throw new Exception(Exception::USER_AUTH_METHOD_UNSUPPORTED, 'Unsupported authentication route'); + break; } }); diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php index e627ff986e..f3c92b3d06 100644 --- a/app/controllers/web/console.php +++ b/app/controllers/web/console.php @@ -512,9 +512,9 @@ App::get('/console/version') if ($version && isset($version['version'])) { return $response->json(['version' => $version['version']]); } else { - throw new Exception('Failed to check for a newer version', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to check for a newer version'); } } catch (\Throwable $th) { - throw new Exception('Failed to check for a newer version', 500, Exception::GENERAL_SERVER_ERROR); + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to check for a newer version'); } }); diff --git a/app/init.php b/app/init.php index 03709727ae..d3462e125d 100644 --- a/app/init.php +++ b/app/init.php @@ -458,7 +458,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::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled"); } $classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName); @@ -828,7 +828,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, 'Failed to verify JWT. ' . $error->getMessage()); } $jwtUserId = $payload['userId'] ?? ''; diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 45628f5bcc..3da3fe4afe 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -595,7 +595,7 @@ services: - _APP_REDIS_PASS mariadb: - image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p + image: mariadb:10.8.3 # fix issues when upgrading using: mysql_upgrade -u root -p container_name: appwrite-mariadb <<: *x-logging restart: unless-stopped @@ -611,7 +611,7 @@ services: command: 'mysqld --innodb-flush-method=fsync' redis: - image: redis:6.2-alpine + image: redis:7.0.4-alpine container_name: appwrite-redis <<: *x-logging restart: unless-stopped diff --git a/composer.json b/composer.json index af42acedba..ca95433f4c 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "autoload-dev": { "psr-4": { "Tests\\E2E\\": "tests/e2e", + "Tests\\Unit\\": "tests/unit", "Appwrite\\Tests\\": "tests/extensions" } }, diff --git a/docker-compose.yml b/docker-compose.yml index df2938cbc7..46631edb04 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -639,7 +639,7 @@ services: - _APP_REDIS_PASS mariadb: - image: mariadb:10.7 # fix issues when upgrading using: mysql_upgrade -u root -p + image: mariadb:10.8.3 # fix issues when upgrading using: mysql_upgrade -u root -p container_name: appwrite-mariadb <<: *x-logging networks: @@ -669,7 +669,7 @@ services: # - SMARTHOST_PORT=587 redis: - image: redis:6.2-alpine + image: redis:7.0.4-alpine <<: *x-logging container_name: appwrite-redis command: > diff --git a/phpunit.xml b/phpunit.xml index 58fc319ed8..e955ab7cee 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -13,7 +13,7 @@ - ./tests/unit/ + ./tests/unit ./tests/e2e/Client.php diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index 69cc32b715..7b2f2644e6 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -2,6 +2,8 @@ namespace Appwrite\Extend; +use Utopia\Config\Config; + class Exception extends \Exception { /** @@ -47,7 +49,7 @@ 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'; /** Users */ public const USER_COUNT_EXCEEDED = 'user_count_exceeded'; @@ -69,6 +71,7 @@ class Exception extends \Exception public const USER_AUTH_METHOD_UNSUPPORTED = 'user_auth_method_unsupported'; public const USER_PHONE_ALREADY_EXISTS = 'user_phone_already_exists'; public const USER_PHONE_NOT_FOUND = 'user_phone_not_found'; + public const USER_MISSING_ID = 'user_missing_id'; /** Teams */ public const TEAM_NOT_FOUND = 'team_not_found'; @@ -80,6 +83,7 @@ class Exception extends \Exception /** Membership */ public const MEMBERSHIP_NOT_FOUND = 'membership_not_found'; + public const MEMBERSHIP_ALREADY_CONFIRMED = 'membership_already_confirmed'; /** Avatars */ public const AVATAR_SET_NOT_FOUND = 'avatar_set_not_found'; @@ -116,8 +120,8 @@ class Exception extends \Exception public const EXECUTION_NOT_FOUND = 'execution_not_found'; /** Databases */ - public const DATABASE_NOT_FOUND = 'database_not_found'; - public const DATABASE_ALREADY_EXISTS = 'database_already_exists'; + public const DATABASE_NOT_FOUND = 'database_not_found'; + public const DATABASE_ALREADY_EXISTS = 'database_already_exists'; /** Collections */ public const COLLECTION_NOT_FOUND = 'collection_not_found'; @@ -152,7 +156,6 @@ class Exception extends \Exception public const PROJECT_PROVIDER_UNSUPPORTED = 'project_provider_unsupported'; public const PROJECT_INVALID_SUCCESS_URL = 'project_invalid_success_url'; public const PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url'; - public const PROJECT_MISSING_USER_ID = 'project_missing_user_id'; public const PROJECT_RESERVED_PROJECT = 'project_reserved_project'; public const PROJECT_KEY_EXPIRED = 'project_key_expired'; @@ -170,14 +173,22 @@ class Exception extends \Exception public const DOMAIN_ALREADY_EXISTS = 'domain_already_exists'; public const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed'; + protected $type = ''; - 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 = null, int $code = null, \Throwable $previous = null) { + $this->errors = Config::getParam('errors'); $this->type = $type; - parent::__construct($message, $code, $previous); + if (isset($this->errors[$type])) { + $this->code = $this->errors[$type]['code']; + $this->message = $this->errors[$type]['description']; + } + + $this->message = $message ?? $this->message; + $this->code = $code ?? $this->code; + + parent::__construct($this->message, $this->code, $previous); } /** diff --git a/src/Appwrite/Utopia/Database/Validator/Queries.php b/src/Appwrite/Utopia/Database/Validator/Queries.php index 6be381dbf7..8ea70231d5 100644 --- a/src/Appwrite/Utopia/Database/Validator/Queries.php +++ b/src/Appwrite/Utopia/Database/Validator/Queries.php @@ -9,7 +9,7 @@ class Queries extends ValidatorQueries { /** * Expression constructor - * + * * This Queries Validator that filters indexes for only available indexes * * @param QueryValidator $validator diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index 6a7aee6ede..82240d3429 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -201,7 +201,7 @@ class Response extends SwooleResponse /** * @var array */ - protected $payload = []; + protected array $payload = []; /** * Response constructor. @@ -303,8 +303,7 @@ class Response extends SwooleResponse // Verification // Recovery // Tests (keep last) - ->setModel(new Mock()) - ; + ->setModel(new Mock()); parent::__construct($response); } @@ -394,12 +393,13 @@ class Response extends SwooleResponse if ($model->isAny()) { $this->payload = $document->getArrayCopy(); + return $this->payload; } foreach ($model->getRules() as $key => $rule) { - if (!$document->isSet($key) && $rule['require']) { // do not set attribute in response if not required - if (!is_null($rule['default'])) { + if (!$document->isSet($key) && $rule['required']) { // do not set attribute in response if not required + if (\array_key_exists('default', $rule)) { $document->setAttribute($key, $rule['default']); } else { throw new Exception('Model ' . $model->getName() . ' is missing response key: ' . $key); @@ -411,7 +411,7 @@ class Response extends SwooleResponse throw new Exception($key . ' must be an array of type ' . $rule['type']); } - foreach ($data[$key] as &$item) { + foreach ($data[$key] as $index => $item) { if ($item instanceof Document) { if (\is_array($rule['type'])) { foreach ($rule['type'] as $type) { @@ -435,9 +435,13 @@ class Response extends SwooleResponse throw new Exception('Missing model for rule: ' . $ruleType); } - $item = $this->output($item, $ruleType); + $data[$key][$index] = $this->output($item, $ruleType); } } + } else { + if ($data[$key] instanceof Document) { + $data[$key] = $this->output($data[$key], $rule['type']); + } } $output[$key] = $data[$key]; @@ -468,8 +472,7 @@ class Response extends SwooleResponse $this ->setContentType(Response::CONTENT_TYPE_YAML) - ->send(yaml_emit($data, YAML_UTF8_ENCODING)) - ; + ->send(yaml_emit($data, YAML_UTF8_ENCODING)); } /** diff --git a/src/Appwrite/Utopia/Response/Model.php b/src/Appwrite/Utopia/Response/Model.php index ec97c3a914..b4b8bde3db 100644 --- a/src/Appwrite/Utopia/Response/Model.php +++ b/src/Appwrite/Utopia/Response/Model.php @@ -17,22 +17,28 @@ abstract class Model /** * @var bool */ - protected $none = false; + protected bool $none = false; /** * @var bool */ - protected $any = false; + protected bool $any = false; /** * @var bool */ - protected $public = true; + protected bool $public = true; /** * @var array */ - protected $rules = []; + protected array $rules = []; + + /** + * @var array + */ + public array $conditions = []; + /** * Filter Document Structure @@ -78,12 +84,10 @@ abstract class Model protected function addRule(string $key, array $options): self { $this->rules[$key] = array_merge([ - 'require' => true, - 'type' => '', + 'required' => true, + 'array' => false, 'description' => '', - 'default' => null, - 'example' => '', - 'array' => false + 'example' => '' ], $options); return $this; @@ -94,7 +98,7 @@ abstract class Model $list = []; foreach ($this->rules as $key => $rule) { - if ($rule['require'] ?? false) { + if ($rule['required'] ?? false) { $list[] = $key; } } diff --git a/src/Appwrite/Utopia/Response/Model/Any.php b/src/Appwrite/Utopia/Response/Model/Any.php index 589dfd1eb4..634e0859b9 100644 --- a/src/Appwrite/Utopia/Response/Model/Any.php +++ b/src/Appwrite/Utopia/Response/Model/Any.php @@ -10,7 +10,7 @@ class Any extends Model /** * @var bool */ - protected $any = true; + protected bool $any = true; /** * Get Name diff --git a/src/Appwrite/Utopia/Response/Model/Attribute.php b/src/Appwrite/Utopia/Response/Model/Attribute.php index 274b0caa92..ee1b8628ed 100644 --- a/src/Appwrite/Utopia/Response/Model/Attribute.php +++ b/src/Appwrite/Utopia/Response/Model/Attribute.php @@ -39,7 +39,6 @@ class Attribute extends Model 'description' => 'Is attribute an array?', 'default' => false, 'example' => false, - 'require' => false ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php index 0623406636..e31b517de6 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeBoolean.php @@ -28,9 +28,7 @@ class AttributeBoolean extends Attribute 'type' => self::TYPE_BOOLEAN, 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', 'default' => null, - 'example' => false, - 'array' => false, - 'require' => false, + 'example' => false ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php index 64fd5b42fe..e521c31a85 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEmail.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeEmail.php @@ -37,8 +37,6 @@ class AttributeEmail extends Attribute 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', 'default' => null, 'example' => 'default@example.com', - 'array' => false, - 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeEnum.php b/src/Appwrite/Utopia/Response/Model/AttributeEnum.php index 2c3875c005..3b111c27e1 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeEnum.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeEnum.php @@ -30,7 +30,6 @@ class AttributeEnum extends Attribute 'default' => null, 'example' => 'element', 'array' => true, - 'require' => true, ]) ->addRule('format', [ 'type' => self::TYPE_STRING, @@ -45,8 +44,6 @@ class AttributeEnum extends Attribute 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', 'default' => null, 'example' => 'element', - 'array' => false, - 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php index 5cc6f90649..e2d0594e7b 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeFloat.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeFloat.php @@ -29,24 +29,18 @@ class AttributeFloat extends Attribute 'description' => 'Minimum value to enforce for new documents.', 'default' => null, 'example' => 1.5, - 'array' => false, - 'require' => false, ]) ->addRule('max', [ 'type' => self::TYPE_FLOAT, 'description' => 'Maximum value to enforce for new documents.', 'default' => null, 'example' => 10.5, - 'array' => false, - 'require' => false, ]) ->addRule('default', [ 'type' => self::TYPE_FLOAT, 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', 'default' => null, 'example' => 2.5, - 'array' => false, - 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeIP.php b/src/Appwrite/Utopia/Response/Model/AttributeIP.php index 6cd401fe35..14724144ec 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeIP.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeIP.php @@ -37,8 +37,6 @@ class AttributeIP extends Attribute 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', 'default' => null, 'example' => '192.0.2.0', - 'array' => false, - 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php index eefa5e39d7..35fd955a5e 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeInteger.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeInteger.php @@ -29,24 +29,18 @@ class AttributeInteger extends Attribute 'description' => 'Minimum value to enforce for new documents.', 'default' => null, 'example' => 1, - 'array' => false, - 'require' => false, ]) ->addRule('max', [ 'type' => self::TYPE_INTEGER, 'description' => 'Maximum value to enforce for new documents.', 'default' => null, 'example' => 10, - 'array' => false, - 'require' => false, ]) ->addRule('default', [ 'type' => self::TYPE_INTEGER, 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', 'default' => null, 'example' => 10, - 'array' => false, - 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeString.php b/src/Appwrite/Utopia/Response/Model/AttributeString.php index c09ce0eea8..75c3a71019 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeString.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeString.php @@ -23,8 +23,6 @@ class AttributeString extends Attribute 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', 'default' => null, 'example' => 'default', - 'array' => false, - 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/AttributeURL.php b/src/Appwrite/Utopia/Response/Model/AttributeURL.php index 46641bc283..d59a871072 100644 --- a/src/Appwrite/Utopia/Response/Model/AttributeURL.php +++ b/src/Appwrite/Utopia/Response/Model/AttributeURL.php @@ -37,8 +37,6 @@ class AttributeURL extends Attribute 'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.', 'default' => null, 'example' => 'http://example.com', - 'array' => false, - 'require' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/BaseList.php b/src/Appwrite/Utopia/Response/Model/BaseList.php index ee8788e6a6..06a5249388 100644 --- a/src/Appwrite/Utopia/Response/Model/BaseList.php +++ b/src/Appwrite/Utopia/Response/Model/BaseList.php @@ -2,7 +2,6 @@ namespace Appwrite\Utopia\Response\Model; -use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; class BaseList extends Model @@ -10,12 +9,12 @@ class BaseList extends Model /** * @var string */ - protected $name = ''; + protected string $name = ''; /** * @var string */ - protected $type = ''; + protected string $type = ''; /** * @param string $name diff --git a/src/Appwrite/Utopia/Response/Model/Domain.php b/src/Appwrite/Utopia/Response/Model/Domain.php index b23f612ab3..2946a78686 100644 --- a/src/Appwrite/Utopia/Response/Model/Domain.php +++ b/src/Appwrite/Utopia/Response/Model/Domain.php @@ -10,7 +10,7 @@ class Domain extends Model /** * @var bool */ - protected $public = false; + protected bool $public = false; public function __construct() { diff --git a/src/Appwrite/Utopia/Response/Model/ErrorDev.php b/src/Appwrite/Utopia/Response/Model/ErrorDev.php index b5450c67ad..9319397c77 100644 --- a/src/Appwrite/Utopia/Response/Model/ErrorDev.php +++ b/src/Appwrite/Utopia/Response/Model/ErrorDev.php @@ -9,7 +9,7 @@ class ErrorDev extends Error /** * @var bool */ - protected $public = false; + protected bool $public = false; public function __construct() { diff --git a/src/Appwrite/Utopia/Response/Model/Index.php b/src/Appwrite/Utopia/Response/Model/Index.php index b7ac626be9..9f27613669 100644 --- a/src/Appwrite/Utopia/Response/Model/Index.php +++ b/src/Appwrite/Utopia/Response/Model/Index.php @@ -41,7 +41,6 @@ class Index extends Model 'default' => [], 'example' => [], 'array' => true, - 'required' => false, ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/Key.php b/src/Appwrite/Utopia/Response/Model/Key.php index 5ab8be21ea..4062454d39 100644 --- a/src/Appwrite/Utopia/Response/Model/Key.php +++ b/src/Appwrite/Utopia/Response/Model/Key.php @@ -10,7 +10,7 @@ class Key extends Model /** * @var bool */ - protected $public = false; + protected bool $public = false; public function __construct() { diff --git a/src/Appwrite/Utopia/Response/Model/Metric.php b/src/Appwrite/Utopia/Response/Model/Metric.php index 70706d6143..7fb1b98386 100644 --- a/src/Appwrite/Utopia/Response/Model/Metric.php +++ b/src/Appwrite/Utopia/Response/Model/Metric.php @@ -16,7 +16,7 @@ class Metric extends Model 'default' => -1, 'example' => 1, ]) - ->addRule('timestamp', [ + ->addRule('date', [ 'type' => self::TYPE_INTEGER, 'description' => 'The UNIX timestamp at which this metric was aggregated.', 'default' => 0, diff --git a/src/Appwrite/Utopia/Response/Model/None.php b/src/Appwrite/Utopia/Response/Model/None.php index ecc387498e..8f98ae892f 100644 --- a/src/Appwrite/Utopia/Response/Model/None.php +++ b/src/Appwrite/Utopia/Response/Model/None.php @@ -10,7 +10,7 @@ class None extends Model /** * @var bool */ - protected $none = true; + protected bool $none = true; /** * Get Name diff --git a/src/Appwrite/Utopia/Response/Model/Platform.php b/src/Appwrite/Utopia/Response/Model/Platform.php index 5b56601c1b..10e8484b09 100644 --- a/src/Appwrite/Utopia/Response/Model/Platform.php +++ b/src/Appwrite/Utopia/Response/Model/Platform.php @@ -10,7 +10,7 @@ class Platform extends Model /** * @var bool */ - protected $public = false; + protected bool $public = false; public function __construct() { diff --git a/src/Appwrite/Utopia/Response/Model/Preferences.php b/src/Appwrite/Utopia/Response/Model/Preferences.php index ad4bf66132..379543660c 100644 --- a/src/Appwrite/Utopia/Response/Model/Preferences.php +++ b/src/Appwrite/Utopia/Response/Model/Preferences.php @@ -6,11 +6,6 @@ use Appwrite\Utopia\Response; class Preferences extends Any { - /** - * @var bool - */ - protected $any = true; - /** * Get Name * diff --git a/src/Appwrite/Utopia/Response/Model/Project.php b/src/Appwrite/Utopia/Response/Model/Project.php index 6e11965527..cbb48162e9 100644 --- a/src/Appwrite/Utopia/Response/Model/Project.php +++ b/src/Appwrite/Utopia/Response/Model/Project.php @@ -12,7 +12,7 @@ class Project extends Model /** * @var bool */ - protected $public = false; + protected bool $public = false; public function __construct() { diff --git a/src/Appwrite/Utopia/Response/Model/Webhook.php b/src/Appwrite/Utopia/Response/Model/Webhook.php index 88a9ac4869..e38134ed26 100644 --- a/src/Appwrite/Utopia/Response/Model/Webhook.php +++ b/src/Appwrite/Utopia/Response/Model/Webhook.php @@ -10,7 +10,7 @@ class Webhook extends Model /** * @var bool */ - protected $public = false; + protected bool $public = false; public function __construct() { diff --git a/tests/unit/Auth/AuthTest.php b/tests/unit/Auth/AuthTest.php index e0ebe4a556..da5553c0e9 100644 --- a/tests/unit/Auth/AuthTest.php +++ b/tests/unit/Auth/AuthTest.php @@ -1,6 +1,6 @@ assertEquals(Auth::$cookieName, $name); } - public function testEncodeDecodeSession() + public function testEncodeDecodeSession(): void { $id = 'id'; $secret = 'secret'; @@ -42,13 +38,13 @@ class AuthTest extends TestCase $this->assertEquals(Auth::decodeSession($session), ['id' => $id, 'secret' => $secret]); } - public function testHash() + public function testHash(): void { $secret = 'secret'; $this->assertEquals(Auth::hash($secret), '2bb80d537b1da3e38bd30361aa855686bde0eacd7162fef6a25fe97bf527a25b'); } - public function testPassword() + public function testPassword(): void { $secret = 'secret'; $static = '$2y$08$PDbMtV18J1KOBI9tIYabBuyUwBrtXPGhLxCy9pWP6xkldVOKLrLKy'; @@ -58,19 +54,19 @@ class AuthTest extends TestCase $this->assertEquals(Auth::passwordVerify($secret, $static), true); } - public function testPasswordGenerator() + public function testPasswordGenerator(): void { $this->assertEquals(\mb_strlen(Auth::passwordGenerator()), 40); $this->assertEquals(\mb_strlen(Auth::passwordGenerator(5)), 10); } - public function testTokenGenerator() + public function testTokenGenerator(): void { $this->assertEquals(\mb_strlen(Auth::tokenGenerator()), 256); $this->assertEquals(\mb_strlen(Auth::tokenGenerator(5)), 10); } - public function testSessionVerify() + public function testSessionVerify(): void { $secret = 'secret1'; $hash = Auth::hash($secret); @@ -114,7 +110,7 @@ class AuthTest extends TestCase $this->assertEquals(Auth::sessionVerify($tokens2, 'false-secret'), false); } - public function testTokenVerify() + public function testTokenVerify(): void { $secret = 'secret1'; $hash = Auth::hash($secret); @@ -171,7 +167,7 @@ class AuthTest extends TestCase $this->assertEquals(Auth::tokenVerify($tokens3, Auth::TOKEN_TYPE_RECOVERY, 'false-secret'), false); } - public function testIsPrivilegedUser() + public function testIsPrivilegedUser(): void { $this->assertEquals(false, Auth::isPrivilegedUser([])); $this->assertEquals(false, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_GUEST])); @@ -188,7 +184,7 @@ class AuthTest extends TestCase $this->assertEquals(true, Auth::isPrivilegedUser(['role:' . Auth::USER_ROLE_OWNER, 'role:' . Auth::USER_ROLE_ADMIN, 'role:' . Auth::USER_ROLE_DEVELOPER])); } - public function testIsAppUser() + public function testIsAppUser(): void { $this->assertEquals(false, Auth::isAppUser([])); $this->assertEquals(false, Auth::isAppUser(['role:' . Auth::USER_ROLE_GUEST])); @@ -205,7 +201,7 @@ class AuthTest extends TestCase $this->assertEquals(false, Auth::isAppUser(['role:' . Auth::USER_ROLE_OWNER, 'role:' . Auth::USER_ROLE_ADMIN, 'role:' . Auth::USER_ROLE_DEVELOPER])); } - public function testGuestRoles() + public function testGuestRoles(): void { $user = new Document([ '$id' => '' @@ -216,7 +212,7 @@ class AuthTest extends TestCase $this->assertContains('role:guest', $roles); } - public function testUserRoles() + public function testUserRoles(): void { $user = new Document([ '$id' => '123', @@ -249,7 +245,7 @@ class AuthTest extends TestCase $this->assertContains('team:def/guest', $roles); } - public function testPrivilegedUserRoles() + public function testPrivilegedUserRoles(): void { Authorization::setRole('role:' . Auth::USER_ROLE_OWNER); $user = new Document([ @@ -283,7 +279,7 @@ class AuthTest extends TestCase $this->assertContains('team:def/guest', $roles); } - public function testAppUserRoles() + public function testAppUserRoles(): void { Authorization::setRole('role:' . Auth::USER_ROLE_APP); $user = new Document([ diff --git a/tests/unit/Auth/Validator/PasswordTest.php b/tests/unit/Auth/Validator/PasswordTest.php index 5a96941b10..0d84ff3573 100644 --- a/tests/unit/Auth/Validator/PasswordTest.php +++ b/tests/unit/Auth/Validator/PasswordTest.php @@ -1,27 +1,20 @@ object = new Password(); } - public function tearDown(): void - { - } - - public function testValues() + public function testValues(): void { $this->assertEquals($this->object->isValid(false), false); $this->assertEquals($this->object->isValid(null), false); diff --git a/tests/unit/Auth/Validator/PhoneTest.php b/tests/unit/Auth/Validator/PhoneTest.php index f6695918c3..7bfa8db477 100644 --- a/tests/unit/Auth/Validator/PhoneTest.php +++ b/tests/unit/Auth/Validator/PhoneTest.php @@ -1,6 +1,6 @@ object = new Phone(); } - public function tearDown(): void - { - } - - public function testValues() + public function testValues(): void { $this->assertEquals($this->object->isValid(false), false); $this->assertEquals($this->object->isValid(null), false); diff --git a/tests/unit/DSN/DSNTest.php b/tests/unit/DSN/DSNTest.php index be31a640b8..d1f5ba1197 100644 --- a/tests/unit/DSN/DSNTest.php +++ b/tests/unit/DSN/DSNTest.php @@ -1,20 +1,12 @@ expectException(\InvalidArgumentException::class); - $dsn = new DSN("mariadb://"); + new DSN("mariadb://"); } } diff --git a/tests/unit/Detector/DetectorTest.php b/tests/unit/Detector/DetectorTest.php index a70ad44cf6..12a803de8c 100644 --- a/tests/unit/Detector/DetectorTest.php +++ b/tests/unit/Detector/DetectorTest.php @@ -1,16 +1,13 @@ assertEquals($this->object->getOS(), [ 'osCode' => 'WIN', @@ -30,7 +27,7 @@ class DetectorTest extends TestCase ]); } - public function testGetClient() + public function testGetClient(): void { $this->assertEquals($this->object->getClient(), [ 'clientType' => 'browser', @@ -42,7 +39,7 @@ class DetectorTest extends TestCase ]); } - public function testGetDevice() + public function testGetDevice(): void { $this->assertEquals($this->object->getDevice(), [ 'deviceName' => 'desktop', diff --git a/tests/unit/Docker/ComposeTest.php b/tests/unit/Docker/ComposeTest.php index ad3f40ab54..9f974b5970 100644 --- a/tests/unit/Docker/ComposeTest.php +++ b/tests/unit/Docker/ComposeTest.php @@ -1,6 +1,6 @@ object = new Compose($data); } - public function tearDown(): void - { - } - - public function testVersion() + public function testVersion(): void { $this->assertEquals('3', $this->object->getVersion()); } - public function testServices() + public function testServices(): void { $this->assertCount(17, $this->object->getServices()); $this->assertEquals('appwrite-telegraf', $this->object->getService('telegraf')->getContainerName()); @@ -44,12 +36,12 @@ class ComposeTest extends TestCase $this->assertEquals(['2080' => '80', '2443' => '443', '8080' => '8080'], $this->object->getService('traefik')->getPorts()); } - public function testNetworks() + public function testNetworks(): void { $this->assertCount(2, $this->object->getNetworks()); } - public function testVolumes() + public function testVolumes(): void { $this->assertCount(9, $this->object->getVolumes()); $this->assertEquals('appwrite-mariadb', $this->object->getVolumes()[0]); diff --git a/tests/unit/Docker/EnvTest.php b/tests/unit/Docker/EnvTest.php index f436354efa..f085d62e56 100644 --- a/tests/unit/Docker/EnvTest.php +++ b/tests/unit/Docker/EnvTest.php @@ -1,6 +1,6 @@ object = new Env($data); } - public function tearDown(): void - { - } - - public function testVars() + public function testVars(): void { $this->object->setVar('_APP_TEST', 'value4'); @@ -39,7 +31,7 @@ class EnvTest extends TestCase $this->assertEquals('value4', $this->object->getVar('_APP_TEST')); } - public function testExport() + public function testExport(): void { $this->assertEquals("_APP_X=value1 _APP_Y=value2 diff --git a/tests/unit/Event/EventTest.php b/tests/unit/Event/EventTest.php index 8a8ff1e6d2..dee905638f 100644 --- a/tests/unit/Event/EventTest.php +++ b/tests/unit/Event/EventTest.php @@ -1,6 +1,6 @@ object = new Event($this->queue, 'TestsV1'); } - public function tearDown(): void - { - } - - public function testQueue() + public function testQueue(): void { $this->assertEquals($this->queue, $this->object->getQueue()); @@ -44,7 +33,7 @@ class EventTest extends TestCase $this->object->setQueue($this->queue); } - public function testClass() + public function testClass(): void { $this->assertEquals('TestsV1', $this->object->getClass()); @@ -55,7 +44,7 @@ class EventTest extends TestCase $this->object->setClass('TestsV1'); } - public function testParams() + public function testParams(): void { $this->object ->setParam('eventKey1', 'eventValue1') @@ -69,7 +58,7 @@ class EventTest extends TestCase $this->assertEquals(\Resque::size($this->queue), 1); } - public function testReset() + public function testReset(): void { $this->object ->setParam('eventKey1', 'eventValue1') @@ -85,7 +74,7 @@ class EventTest extends TestCase $this->assertEquals(null, $this->object->getParam('eventKey3')); } - public function testGenerateEvents() + public function testGenerateEvents(): void { $event = Event::generateEvents('users.[userId].create', [ 'userId' => 'torsten' diff --git a/tests/unit/Event/Validator/EventValidatorTest.php b/tests/unit/Event/Validator/EventValidatorTest.php index e40dcce05c..59a31f272d 100644 --- a/tests/unit/Event/Validator/EventValidatorTest.php +++ b/tests/unit/Event/Validator/EventValidatorTest.php @@ -1,6 +1,6 @@ collections = require('app/config/collections.php'); } - public function tearDown(): void - { - } - - public function testDuplicateRules() + public function testDuplicateRules(): void { foreach ($this->collections as $key => $collection) { if (array_key_exists('attributes', $collection)) { diff --git a/tests/unit/General/ExtensionsTest.php b/tests/unit/General/ExtensionsTest.php index 2a7c9d58e9..d638cfffa6 100644 --- a/tests/unit/General/ExtensionsTest.php +++ b/tests/unit/General/ExtensionsTest.php @@ -1,116 +1,77 @@ assertEquals(true, extension_loaded('redis')); } - public function testSwoole() + public function testSwoole(): void { $this->assertEquals(true, extension_loaded('swoole')); } - public function testYAML() + public function testYAML(): void { $this->assertEquals(true, extension_loaded('yaml')); } - public function testOPCache() + public function testOPCache(): void { $this->assertEquals(true, extension_loaded('Zend OPcache')); } - public function testDOM() + public function testDOM(): void { $this->assertEquals(true, extension_loaded('dom')); } - public function testPDO() + public function testPDO(): void { $this->assertEquals(true, extension_loaded('PDO')); } - public function testImagick() + public function testImagick(): void { $this->assertEquals(true, extension_loaded('imagick')); } - public function testJSON() + public function testJSON(): void { $this->assertEquals(true, extension_loaded('json')); } - public function testCURL() + public function testCURL(): void { $this->assertEquals(true, extension_loaded('curl')); } - public function testMBString() + public function testMBString(): void { $this->assertEquals(true, extension_loaded('mbstring')); } - public function testOPENSSL() + public function testOPENSSL(): void { $this->assertEquals(true, extension_loaded('openssl')); } - public function testZLIB() + public function testZLIB(): void { $this->assertEquals(true, extension_loaded('zlib')); } - public function testSockets() + public function testSockets(): void { $this->assertEquals(true, extension_loaded('sockets')); } - public function testMaxminddb() + public function testMaxminddb(): void { $this->assertEquals(true, extension_loaded('maxminddb')); } diff --git a/tests/unit/Messaging/MessagingChannelsTest.php b/tests/unit/Messaging/MessagingChannelsTest.php index e62c273cc8..833345680b 100644 --- a/tests/unit/Messaging/MessagingChannelsTest.php +++ b/tests/unit/Messaging/MessagingChannelsTest.php @@ -1,6 +1,6 @@ connectionsCount = 0; } - public function testSubscriptions() + public function testSubscriptions(): void { /** * Check for 1 project. @@ -148,7 +148,7 @@ class MessagingChannelsTest extends TestCase /** * Tests Wildcard (role:all) Permissions on every channel. */ - public function testWildcardPermission() + public function testWildcardPermission(): void { foreach ($this->allChannels as $index => $channel) { $event = [ @@ -177,7 +177,7 @@ class MessagingChannelsTest extends TestCase } } - public function testRolePermissions() + public function testRolePermissions(): void { $roles = ['role:guest', 'role:member']; foreach ($this->allChannels as $index => $channel) { @@ -211,7 +211,7 @@ class MessagingChannelsTest extends TestCase } } - public function testUserPermissions() + public function testUserPermissions(): void { foreach ($this->allChannels as $index => $channel) { $permissions = []; @@ -244,7 +244,7 @@ class MessagingChannelsTest extends TestCase } } - public function testTeamPermissions() + public function testTeamPermissions(): void { foreach ($this->allChannels as $index => $channel) { $permissions = []; diff --git a/tests/unit/Messaging/MessagingGuestTest.php b/tests/unit/Messaging/MessagingGuestTest.php index f6b5e22cda..ac5795b8a2 100644 --- a/tests/unit/Messaging/MessagingGuestTest.php +++ b/tests/unit/Messaging/MessagingGuestTest.php @@ -1,13 +1,13 @@ assertEmpty($realtime->subscriptions); } - public function testConvertChannelsGuest() + public function testConvertChannelsGuest(): void { $user = new Document([ '$id' => '' @@ -157,7 +157,7 @@ class MessagingTest extends TestCase $this->assertArrayNotHasKey('account.456', $channels); } - public function testConvertChannelsUser() + public function testConvertChannelsUser(): void { $user = new Document([ '$id' => '123', diff --git a/tests/unit/Migration/MigrationTest.php b/tests/unit/Migration/MigrationTest.php index 24aab8d1e3..d424c16bb7 100644 --- a/tests/unit/Migration/MigrationTest.php +++ b/tests/unit/Migration/MigrationTest.php @@ -1,6 +1,6 @@ assertArrayHasKey(APP_VERSION_STABLE, Migration::$versions); } - public function testHasDifference() + public function testHasDifference(): void { $this->assertFalse(Migration::hasDifference([], [])); $this->assertFalse(Migration::hasDifference([ diff --git a/tests/unit/Migration/MigrationV12Test.php b/tests/unit/Migration/MigrationV12Test.php index 57deb546f3..2a0a0512a2 100644 --- a/tests/unit/Migration/MigrationV12Test.php +++ b/tests/unit/Migration/MigrationV12Test.php @@ -1,6 +1,6 @@ method->setAccessible(true); } - public function testMigrationProjects() + public function testMigrationProjects(): void { $document = $this->fixDocument(new Document([ '$id' => 'project', @@ -30,7 +30,7 @@ class MigrationV12Test extends MigrationTest $this->assertEquals($document->getAttribute('search'), 'project Appwrite'); } - public function testMigrationUsers() + public function testMigrationUsers(): void { $document = $this->fixDocument(new Document([ '$id' => 'user', @@ -42,7 +42,7 @@ class MigrationV12Test extends MigrationTest $this->assertEquals($document->getAttribute('search'), 'user test@appwrite.io Torsten Dittmann'); } - public function testMigrationTeams() + public function testMigrationTeams(): void { $document = $this->fixDocument(new Document([ '$id' => 'team', @@ -53,7 +53,7 @@ class MigrationV12Test extends MigrationTest $this->assertEquals($document->getAttribute('search'), 'team Appwrite'); } - public function testMigrationFunctions() + public function testMigrationFunctions(): void { $document = $this->fixDocument(new Document([ '$id' => 'function', @@ -65,7 +65,7 @@ class MigrationV12Test extends MigrationTest $this->assertEquals($document->getAttribute('search'), 'function My Function php-8.0'); } - public function testMigrationExecutions() + public function testMigrationExecutions(): void { $document = $this->fixDocument(new Document([ '$id' => 'execution', diff --git a/tests/unit/Migration/MigrationV13Test.php b/tests/unit/Migration/MigrationV13Test.php index ff04403536..954aea71cf 100644 --- a/tests/unit/Migration/MigrationV13Test.php +++ b/tests/unit/Migration/MigrationV13Test.php @@ -1,8 +1,7 @@ method->setAccessible(true); } - public function testMigrateFunctions() + public function testMigrateFunctions(): void { $document = $this->fixDocument(new Document([ '$id' => 'func', @@ -28,7 +27,7 @@ class MigrationV13Test extends MigrationTest $this->assertEquals($document->getAttribute('events'), ['users.*.create']); } - public function testMigrationWebhooks() + public function testMigrationWebhooks(): void { $document = $this->fixDocument(new Document([ '$id' => 'webh', diff --git a/tests/unit/Migration/MigrationV14Test.php b/tests/unit/Migration/MigrationV14Test.php index ea839465af..1010a2c873 100644 --- a/tests/unit/Migration/MigrationV14Test.php +++ b/tests/unit/Migration/MigrationV14Test.php @@ -1,6 +1,6 @@ method->setAccessible(true); } - public function testMigrateProjects() + public function testMigrateProjects(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', @@ -28,7 +28,7 @@ class MigrationV14Test extends MigrationTest $this->assertEquals($document->getAttribute('version'), '0.15.0'); } - public function testMigrateKeys() + public function testMigrateKeys(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', @@ -39,7 +39,7 @@ class MigrationV14Test extends MigrationTest $this->assertEquals($document->getAttribute('expire'), 0); } - public function testMigrateWebhooks() + public function testMigrateWebhooks(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', @@ -50,7 +50,7 @@ class MigrationV14Test extends MigrationTest $this->assertEquals(strlen($document->getAttribute('signatureKey')), 128); } - public function testMigrateUsers() + public function testMigrateUsers(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', @@ -62,7 +62,7 @@ class MigrationV14Test extends MigrationTest $this->assertFalse($document->getAttribute('phoneVerification')); } - public function testMigratePlatforms() + public function testMigratePlatforms(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', @@ -77,7 +77,7 @@ class MigrationV14Test extends MigrationTest $this->assertEquals($document->getUpdatedAt(), 987654321); } - public function testMigrateFunctions() + public function testMigrateFunctions(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', @@ -92,7 +92,7 @@ class MigrationV14Test extends MigrationTest $this->assertEquals($document->getUpdatedAt(), 987654321); } - public function testMigrateDeployments() + public function testMigrateDeployments(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', @@ -104,7 +104,7 @@ class MigrationV14Test extends MigrationTest $this->assertEquals($document->getCreatedAt(), 123456789); } - public function testMigrateExecutions() + public function testMigrateExecutions(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', @@ -116,7 +116,7 @@ class MigrationV14Test extends MigrationTest $this->assertEquals($document->getCreatedAt(), 123456789); } - public function testMigrateTeams() + public function testMigrateTeams(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', @@ -128,7 +128,7 @@ class MigrationV14Test extends MigrationTest $this->assertEquals($document->getCreatedAt(), 123456789); } - public function testMigrateAudits() + public function testMigrateAudits(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', @@ -151,7 +151,7 @@ class MigrationV14Test extends MigrationTest $this->assertEquals($document->getAttribute('event'), 'databases.default.collections.movies.documents.avatar.create'); } - public function testMigrateStats() + public function testMigrateStats(): void { $document = $this->fixDocument(new Document([ '$id' => 'appwrite', diff --git a/tests/unit/Network/Validators/CNAMETest.php b/tests/unit/Network/Validators/CNAMETest.php index 5aa7a69db4..cbb07f19b5 100644 --- a/tests/unit/Network/Validators/CNAMETest.php +++ b/tests/unit/Network/Validators/CNAMETest.php @@ -1,16 +1,13 @@ assertEquals($this->object->isValid(''), false); $this->assertEquals($this->object->isValid(null), false); diff --git a/tests/unit/Network/Validators/DomainTest.php b/tests/unit/Network/Validators/DomainTest.php index eb77b19a3d..631ea10753 100644 --- a/tests/unit/Network/Validators/DomainTest.php +++ b/tests/unit/Network/Validators/DomainTest.php @@ -1,16 +1,13 @@ domain = null; } - public function testIsValid() + public function testIsValid(): void { // Assertions $this->assertEquals(true, $this->domain->isValid('example.com')); diff --git a/tests/unit/Network/Validators/EmailTest.php b/tests/unit/Network/Validators/EmailTest.php index ca73509c65..f629ed6ddc 100755 --- a/tests/unit/Network/Validators/EmailTest.php +++ b/tests/unit/Network/Validators/EmailTest.php @@ -12,16 +12,14 @@ * @license The MIT License (MIT) */ -namespace Appwrite\Network\Validator; +namespace Tests\Unit\Network\Validators; +use Appwrite\Network\Validator\Email; use PHPUnit\Framework\TestCase; class EmailTest extends TestCase { - /** - * @var Email - */ - protected $email = null; + protected ?Email $email = null; public function setUp(): void { @@ -33,9 +31,8 @@ class EmailTest extends TestCase $this->email = null; } - public function testIsValid() + public function testIsValid(): void { - // Assertions $this->assertEquals(true, $this->email->isValid('email@domain.com')); $this->assertEquals(true, $this->email->isValid('firstname.lastname@domain.com')); $this->assertEquals(true, $this->email->isValid('email@subdomain.domain.com')); diff --git a/tests/unit/Network/Validators/HostTest.php b/tests/unit/Network/Validators/HostTest.php index a23911dc76..7974bf86a1 100755 --- a/tests/unit/Network/Validators/HostTest.php +++ b/tests/unit/Network/Validators/HostTest.php @@ -12,16 +12,14 @@ * @license The MIT License (MIT) */ -namespace Appwrite\Network\Validator; +namespace Tests\Unit\Network\Validators; +use Appwrite\Network\Validator\Host; use PHPUnit\Framework\TestCase; class HostTest extends TestCase { - /** - * @var Host - */ - protected $host = null; + protected ?Host $host = null; public function setUp(): void { @@ -33,7 +31,7 @@ class HostTest extends TestCase $this->host = null; } - public function testIsValid() + public function testIsValid(): void { // Assertions $this->assertEquals($this->host->isValid('https://appwrite.io/link'), true); diff --git a/tests/unit/Network/Validators/IPTest.php b/tests/unit/Network/Validators/IPTest.php index 0f5fc63ce9..57e395111c 100755 --- a/tests/unit/Network/Validators/IPTest.php +++ b/tests/unit/Network/Validators/IPTest.php @@ -12,71 +12,76 @@ * @license The MIT License (MIT) */ -namespace Appwrite\Network\Validator; +namespace Tests\Unit\Network\Validators; +use Appwrite\Network\Validator\IP; use PHPUnit\Framework\TestCase; class IPTest extends TestCase { + protected ?IP $validator; + + public function setUp(): void + { + $this->validator = new IP(); + } + public function tearDown(): void { $this->validator = null; } - public function testIsValidIP() + public function testIsValidIP(): void { - $validator = new IP(); - - // Assertions - $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); - $this->assertEquals($validator->isValid('109.67.204.101'), true); - $this->assertEquals($validator->isValid(23.5), false); - $this->assertEquals($validator->isValid('23.5'), false); - $this->assertEquals($validator->isValid(null), false); - $this->assertEquals($validator->isValid(true), false); - $this->assertEquals($validator->isValid(false), false); - $this->assertEquals($validator->getType(), 'string'); + $this->assertEquals($this->validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); + $this->assertEquals($this->validator->isValid('109.67.204.101'), true); + $this->assertEquals($this->validator->isValid(23.5), false); + $this->assertEquals($this->validator->isValid('23.5'), false); + $this->assertEquals($this->validator->isValid(null), false); + $this->assertEquals($this->validator->isValid(true), false); + $this->assertEquals($this->validator->isValid(false), false); + $this->assertEquals($this->validator->getType(), 'string'); } - public function testIsValidIPALL() + public function testIsValidIPALL(): void { - $validator = new IP(IP::ALL); + $this->validator = new IP(IP::ALL); // Assertions - $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); - $this->assertEquals($validator->isValid('109.67.204.101'), true); - $this->assertEquals($validator->isValid(23.5), false); - $this->assertEquals($validator->isValid('23.5'), false); - $this->assertEquals($validator->isValid(null), false); - $this->assertEquals($validator->isValid(true), false); - $this->assertEquals($validator->isValid(false), false); + $this->assertEquals($this->validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); + $this->assertEquals($this->validator->isValid('109.67.204.101'), true); + $this->assertEquals($this->validator->isValid(23.5), false); + $this->assertEquals($this->validator->isValid('23.5'), false); + $this->assertEquals($this->validator->isValid(null), false); + $this->assertEquals($this->validator->isValid(true), false); + $this->assertEquals($this->validator->isValid(false), false); } - public function testIsValidIPV4() + public function testIsValidIPV4(): void { - $validator = new IP(IP::V4); + $this->validator = new IP(IP::V4); // Assertions - $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), false); - $this->assertEquals($validator->isValid('109.67.204.101'), true); - $this->assertEquals($validator->isValid(23.5), false); - $this->assertEquals($validator->isValid('23.5'), false); - $this->assertEquals($validator->isValid(null), false); - $this->assertEquals($validator->isValid(true), false); - $this->assertEquals($validator->isValid(false), false); + $this->assertEquals($this->validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), false); + $this->assertEquals($this->validator->isValid('109.67.204.101'), true); + $this->assertEquals($this->validator->isValid(23.5), false); + $this->assertEquals($this->validator->isValid('23.5'), false); + $this->assertEquals($this->validator->isValid(null), false); + $this->assertEquals($this->validator->isValid(true), false); + $this->assertEquals($this->validator->isValid(false), false); } - public function testIsValidIPV6() + public function testIsValidIPV6(): void { - $validator = new IP(IP::V6); + $this->validator = new IP(IP::V6); // Assertions - $this->assertEquals($validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); - $this->assertEquals($validator->isValid('109.67.204.101'), false); - $this->assertEquals($validator->isValid(23.5), false); - $this->assertEquals($validator->isValid('23.5'), false); - $this->assertEquals($validator->isValid(null), false); - $this->assertEquals($validator->isValid(true), false); - $this->assertEquals($validator->isValid(false), false); + $this->assertEquals($this->validator->isValid('2001:0db8:85a3:08d3:1319:8a2e:0370:7334'), true); + $this->assertEquals($this->validator->isValid('109.67.204.101'), false); + $this->assertEquals($this->validator->isValid(23.5), false); + $this->assertEquals($this->validator->isValid('23.5'), false); + $this->assertEquals($this->validator->isValid(null), false); + $this->assertEquals($this->validator->isValid(true), false); + $this->assertEquals($this->validator->isValid(false), false); } } diff --git a/tests/unit/Network/Validators/OriginTest.php b/tests/unit/Network/Validators/OriginTest.php index 414b380465..50dc0ad10a 100644 --- a/tests/unit/Network/Validators/OriginTest.php +++ b/tests/unit/Network/Validators/OriginTest.php @@ -1,13 +1,13 @@ */ -namespace Appwrite\Network\Validator; +namespace Tests\Unit\Network\Validators; +use Appwrite\Network\Validator\URL; use PHPUnit\Framework\TestCase; class URLTest extends TestCase diff --git a/tests/unit/OpenSSL/OpenSSLTest.php b/tests/unit/OpenSSL/OpenSSLTest.php index e3d5e59691..82386aaae0 100644 --- a/tests/unit/OpenSSL/OpenSSLTest.php +++ b/tests/unit/OpenSSL/OpenSSLTest.php @@ -1,6 +1,6 @@ object->setNamespace('appwritetest.usage'); $this->assertEquals('appwritetest.usage', $this->object->getNamespace()); } - public function testParams() + public function testParams(): void { $this->object ->setParam('projectId', 'appwrite_test') @@ -50,7 +50,7 @@ class StatsTest extends TestCase $this->assertEquals(null, $this->object->getParam('networkRequestSize')); } - public function testReset() + public function testReset(): void { $this->object ->setParam('projectId', 'appwrite_test') diff --git a/tests/unit/Task/Validator/CronTest.php b/tests/unit/Task/Validator/CronTest.php index fc0cfff679..f18d8621ba 100644 --- a/tests/unit/Task/Validator/CronTest.php +++ b/tests/unit/Task/Validator/CronTest.php @@ -1,6 +1,6 @@ assertEquals($this->object->isValid('0 2 * * *'), true); // execute at 2am daily $this->assertEquals($this->object->isValid('0 5,17 * * *'), true); // execute twice a day diff --git a/tests/unit/Template/TemplateTest.php b/tests/unit/Template/TemplateTest.php index 1103cbc9a2..1ca1595ca3 100644 --- a/tests/unit/Template/TemplateTest.php +++ b/tests/unit/Template/TemplateTest.php @@ -1,6 +1,6 @@ assertEquals($this->object->render(), 'Hello WORLD'); } - public function testParseURL() + public function testParseURL(): void { $url = $this->object->parseURL('https://appwrite.io/demo'); @@ -38,7 +38,7 @@ class TemplateTest extends TestCase $this->assertEquals($url['path'], '/demo'); } - public function testUnParseURL() + public function testUnParseURL(): void { $url = $this->object->parseURL('https://appwrite.io/demo'); @@ -49,18 +49,18 @@ class TemplateTest extends TestCase $this->assertEquals($this->object->unParseURL($url), 'http://example.com/new'); } - public function testMergeQuery() + public function testMergeQuery(): void { $this->assertEquals($this->object->mergeQuery('key1=value1&key2=value2', ['key1' => 'value3', 'key4' => 'value4']), 'key1=value3&key2=value2&key4=value4'); } - public function testFromCamelCaseToSnake() + public function testFromCamelCaseToSnake(): void { $this->assertEquals('app_write', Template::fromCamelCaseToSnake('appWrite')); $this->assertEquals('app_write', Template::fromCamelCaseToSnake('App Write')); } - public function testFromCamelCaseToDash() + public function testFromCamelCaseToDash(): void { $this->assertEquals('app-write', Template::fromCamelCaseToDash('appWrite')); $this->assertEquals('app-write', Template::fromCamelCaseToDash('App Write')); diff --git a/tests/unit/URL/URLTest.php b/tests/unit/URL/URLTest.php index 0348153a4b..fecaf25bca 100644 --- a/tests/unit/URL/URLTest.php +++ b/tests/unit/URL/URLTest.php @@ -1,13 +1,13 @@ assertEquals('', $url['query']); } - public function testUnparse() + public function testUnparse(): void { $url = URL::unparse([ 'scheme' => 'https', @@ -88,7 +88,7 @@ class URLTest extends TestCase $this->assertEquals('https://eldad:fux@appwrite.io/#bottom', $url); } - public function testParseQuery() + public function testParseQuery(): void { $result = URL::parseQuery('param1=value1¶m2=value2'); @@ -96,7 +96,7 @@ class URLTest extends TestCase $this->assertEquals(['param1' => 'value1', 'param2' => 'value2'], $result); } - public function testUnParseQuery() + public function testUnParseQuery(): void { $result = URL::unparseQuery(['param1' => 'value1', 'param2' => 'value2']); diff --git a/tests/unit/Utopia/Database/Validator/CustomIdTest.php b/tests/unit/Utopia/Database/Validator/CustomIdTest.php index 8b0ddf737a..7989c4f555 100644 --- a/tests/unit/Utopia/Database/Validator/CustomIdTest.php +++ b/tests/unit/Utopia/Database/Validator/CustomIdTest.php @@ -1,6 +1,6 @@ assertEquals($this->object->isValid('unique()'), true); $this->assertEquals($this->object->isValid('unique)'), false); diff --git a/tests/unit/Utopia/Lists.php b/tests/unit/Utopia/Lists.php new file mode 100644 index 0000000000..8f003cc2ae --- /dev/null +++ b/tests/unit/Utopia/Lists.php @@ -0,0 +1,28 @@ +addRule('singles', [ + 'type' => 'single', + 'default' => '', + 'array' => true + ]); + } + + public function getName(): string + { + return 'Lists'; + } + + public function getType(): string + { + return 'lists'; + } +} diff --git a/tests/unit/Utopia/Nested.php b/tests/unit/Utopia/Nested.php new file mode 100644 index 0000000000..2be0e85762 --- /dev/null +++ b/tests/unit/Utopia/Nested.php @@ -0,0 +1,31 @@ +addRule('lists', [ + 'type' => 'lists', + 'default' => '', + ]) + ->addRule('single', [ + 'type' => 'single', + 'default' => '' + ]); + } + + public function getName(): string + { + return 'Nested'; + } + + public function getType(): string + { + return 'nested'; + } +} diff --git a/tests/unit/Utopia/ResponseTest.php b/tests/unit/Utopia/ResponseTest.php index 246380dee0..e4389b3953 100644 --- a/tests/unit/Utopia/ResponseTest.php +++ b/tests/unit/Utopia/ResponseTest.php @@ -1,33 +1,144 @@ object = new Response(new SwooleResponse()); + $this->response = new Response(new SwooleResponse()); + $this->response->setModel(new Single()); + $this->response->setModel(new Lists()); + $this->response->setModel(new Nested()); } - public function testSetFilter() + public function testSetFilter(): void { - $this->assertEquals($this->object->hasFilter(), false); - $this->assertEquals($this->object->getFilter(), null); + $this->assertEquals($this->response->hasFilter(), false); + $this->assertEquals($this->response->getFilter(), null); $filter = new V11(); - $this->object->setFilter($filter); + $this->response->setFilter($filter); - $this->assertEquals($this->object->hasFilter(), true); - $this->assertEquals($this->object->getFilter(), $filter); + $this->assertEquals($this->response->hasFilter(), true); + $this->assertEquals($this->response->getFilter(), $filter); + } + + public function testResponseModel(): void + { + $output = $this->response->output(new Document([ + 'string' => 'lorem ipsum', + 'integer' => 123, + 'boolean' => true, + 'hidden' => 'secret', + ]), 'single'); + + $this->assertArrayHasKey('string', $output); + $this->assertArrayHasKey('integer', $output); + $this->assertArrayHasKey('boolean', $output); + $this->assertArrayNotHasKey('hidden', $output); + } + + public function testResponseModelRequired(): void + { + $output = $this->response->output(new Document([ + 'string' => 'lorem ipsum', + 'integer' => 123, + 'boolean' => true, + ]), 'single'); + + $this->assertArrayHasKey('string', $output); + $this->assertArrayHasKey('integer', $output); + $this->assertArrayHasKey('boolean', $output); + $this->assertArrayHasKey('required', $output); + $this->assertEquals('default', $output['required']); + } + + public function testResponseModelRequiredException(): void + { + $this->expectException(Exception::class); + $this->response->output(new Document([ + 'integer' => 123, + 'boolean' => true, + ]), 'single'); + } + + public function testResponseModelLists(): void + { + $output = $this->response->output(new Document([ + 'singles' => [ + new Document([ + 'string' => 'lorem ipsum', + 'integer' => 123, + 'boolean' => true, + 'hidden' => 'secret' + ]) + ], + 'hidden' => 'secret', + ]), 'lists'); + + $this->assertArrayHasKey('singles', $output); + $this->assertArrayNotHasKey('hidden', $output); + $this->assertCount(1, $output['singles']); + + $single = $output['singles'][0]; + $this->assertArrayHasKey('string', $single); + $this->assertArrayHasKey('integer', $single); + $this->assertArrayHasKey('boolean', $single); + $this->assertArrayHasKey('required', $single); + $this->assertArrayNotHasKey('hidden', $single); + } + + public function testResponseModelNested(): void + { + $output = $this->response->output(new Document([ + 'lists' => new Document([ + 'singles' => [ + new Document([ + 'string' => 'lorem ipsum', + 'integer' => 123, + 'boolean' => true, + 'hidden' => 'secret' + ]) + ], + 'hidden' => 'secret', + ]), + 'single' => new Document([ + 'string' => 'lorem ipsum', + 'integer' => 123, + 'boolean' => true, + 'hidden' => 'secret' + ]), + 'hidden' => 'secret', + ]), 'nested'); + + $this->assertArrayHasKey('lists', $output); + $this->assertArrayHasKey('single', $output); + $this->assertArrayNotHasKey('hidden', $output); + $this->assertCount(1, $output['lists']['singles']); + + + $single = $output['single']; + $this->assertArrayHasKey('string', $single); + $this->assertArrayHasKey('integer', $single); + $this->assertArrayHasKey('boolean', $single); + $this->assertArrayHasKey('required', $single); + $this->assertArrayNotHasKey('hidden', $single); + + $singleFromArray = $output['lists']['singles'][0]; + $this->assertArrayHasKey('string', $singleFromArray); + $this->assertArrayHasKey('integer', $singleFromArray); + $this->assertArrayHasKey('boolean', $singleFromArray); + $this->assertArrayHasKey('required', $single); + $this->assertArrayNotHasKey('hidden', $singleFromArray); } } diff --git a/tests/unit/Utopia/Single.php b/tests/unit/Utopia/Single.php new file mode 100644 index 0000000000..3bd09ef6da --- /dev/null +++ b/tests/unit/Utopia/Single.php @@ -0,0 +1,43 @@ +addRule('string', [ + 'type' => self::TYPE_STRING, + 'example' => '5e5ea5c16897e', + 'required' => true + ]) + ->addRule('integer', [ + 'type' => self::TYPE_INTEGER, + 'default' => 0, + 'example' => 1592981250, + ]) + ->addRule('boolean', [ + 'type' => self::TYPE_BOOLEAN, + 'default' => true, + 'example' => true, + ]) + ->addRule('required', [ + 'type' => self::TYPE_STRING, + 'default' => 'default', + 'required' => true + ]); + } + + public function getName(): string + { + return 'Single'; + } + + public function getType(): string + { + return 'single'; + } +}