diff --git a/app/config/collections.php b/app/config/collections.php index e01b4d36b1..35aa37d352 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -1065,9 +1065,9 @@ $collections = [ 'size' => 16384, 'signed' => true, 'required' => false, - 'default' => [], - 'array' => true, - 'filters' => ['json'], + 'default' => null, + 'array' => false, + 'filters' => ['subQueryTokens'], ], [ '$id' => 'memberships', @@ -1077,8 +1077,8 @@ $collections = [ 'signed' => true, 'required' => false, 'default' => [], - 'array' => true, - 'filters' => ['json'], + 'array' => false, + 'filters' => ['subQueryMemberships'], ], [ '$id' => 'search', @@ -1128,6 +1128,89 @@ $collections = [ ], ], + 'tokens' => [ + '$collection' => Database::METADATA, + '$id' => 'tokens', + 'name' => 'Tokens', + 'attributes' => [ + [ + '$id' => 'userId', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => Database::LENGTH_KEY, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'type', + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'secret', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 512, // https://www.tutorialspoint.com/how-long-is-the-sha256-hash-in-mysql (512 for encryption) + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['encrypt'], + ], + [ + '$id' => 'expire', + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 0, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'userAgent', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 16384, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => 'ip', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 45, // https://stackoverflow.com/a/166157/2299554 + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ] + ], + 'indexes' => [ + [ + '$id' => '_key_user', + 'type' => Database::INDEX_KEY, + 'attributes' => ['userId'], + 'lengths' => [Database::LENGTH_KEY], + 'orders' => [Database::ORDER_ASC], + ], + ], + ], + 'sessions' => [ '$collection' => Database::METADATA, '$id' => 'sessions', @@ -2121,7 +2204,7 @@ $collections = [ 'filters' => [], ], [ - '$id' => 'stdout', + '$id' => 'response', 'type' => Database::VAR_STRING, 'format' => '', 'size' => 16384, diff --git a/app/config/providers.php b/app/config/providers.php index 9a6b5fb50f..1d364a3fcf 100644 --- a/app/config/providers.php +++ b/app/config/providers.php @@ -21,6 +21,16 @@ return [ // Ordered by ABC. 'beta' => true, 'mock' => false, ], + 'auth0' => [ + 'name' => 'Auth0', + 'developers' => 'https://auth0.com/developers', + 'icon' => 'icon-auth0', + 'enabled' => true, + 'sandbox' => false, + 'form' => 'auth0.phtml', + 'beta' => false, + 'mock' => false, + ], 'bitbucket' => [ 'name' => 'BitBucket', 'developers' => 'https://developer.atlassian.com/bitbucket', @@ -141,6 +151,16 @@ return [ // Ordered by ABC. 'beta' => false, 'mock' => false, ], + 'okta' => [ + 'name' => 'Okta', + 'developers' => 'https://developer.okta.com/', + 'icon' => 'icon-okta', + 'enabled' => true, + 'sandbox' => false, + 'form' => 'okta.phtml', + 'beta' => false, + 'mock' => false, + ], 'paypal' => [ 'name' => 'PayPal', 'developers' => 'https://developer.paypal.com/docs/api/overview/', diff --git a/app/config/variables.php b/app/config/variables.php index 2bed472758..5449c9ba93 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -466,7 +466,7 @@ return [ 'name' => '_APP_STORAGE_S3_REGION', 'description' => 'AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console.', 'introduction' => '0.13.0', - 'default' => 'us-eas-1', + 'default' => 'us-east-1', 'required' => false, 'question' => '', ], @@ -498,7 +498,7 @@ return [ 'name' => '_APP_STORAGE_DO_SPACES_REGION', 'description' => 'DigitalOcean spaces region. Required when storage adapter is set to DOSpaces. You can find your region info for your space from DigitalOcean console.', 'introduction' => '0.13.0', - 'default' => 'us-eas-1', + 'default' => 'us-east-1', 'required' => false, 'question' => '', ], diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index 763613894c..b5a7e30679 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -106,8 +106,8 @@ App::post('/v1/account') 'name' => $name, 'prefs' => new \stdClass(), 'sessions' => [], - 'tokens' => [], - 'memberships' => [], + 'tokens' => null, + 'memberships' => null, 'search' => implode(' ', [$userId, $email, $name]), 'deleted' => false ]))); @@ -506,8 +506,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect') 'name' => $name, 'prefs' => new \stdClass(), 'sessions' => [], - 'tokens' => [], - 'memberships' => [], + 'tokens' => null, + 'memberships' => null, 'search' => implode(' ', [$userId, $email, $name]), 'deleted' => false ]))); @@ -681,8 +681,8 @@ App::post('/v1/account/sessions/magic-url') 'reset' => false, 'prefs' => new \stdClass(), 'sessions' => [], - 'tokens' => [], - 'memberships' => [], + 'tokens' => null, + 'memberships' => null, 'search' => implode(' ', [$userId, $email]), 'deleted' => false ]))); @@ -704,13 +704,12 @@ App::post('/v1/account/sessions/magic-url') Authorization::setRole('user:'.$user->getId()); - $user->setAttribute('tokens', $token, Document::SET_TYPE_APPEND); + $token = $dbForProject->createDocument('tokens', $token + ->setAttribute('$read', ['user:'.$user->getId()]) + ->setAttribute('$write', ['user:'.$user->getId()]) + ); - $user = $dbForProject->updateDocument('users', $user->getId(), $user); - - if (false === $user) { - throw new Exception('Failed to save user to DB', 500, Exception::GENERAL_SERVER_ERROR); - } + $dbForProject->deleteCachedDocument('users', $user->getId()); if(empty($url)) { $url = $request->getProtocol().'://'.$request->getHostname().'/auth/magic-url'; @@ -781,7 +780,7 @@ App::put('/v1/account/sessions/magic-url') /** @var Appwrite\Event\Audit $audits */ /** @var Appwrite\Event\Event $events */ - $user = $dbForProject->getDocument('users', $userId); + $user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); if ($user->isEmpty() || $user->getAttribute('deleted')) { throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); @@ -820,23 +819,16 @@ App::put('/v1/account/sessions/magic-url') ->setAttribute('$write', ['user:' . $user->getId()]) ); - $tokens = $user->getAttribute('tokens', []); - /** * We act like we're updating and validating * the recovery token but actually we don't need it anymore. */ - foreach ($tokens as $key => $singleToken) { - if ($token === $singleToken->getId()) { - unset($tokens[$key]); - } - } + $dbForProject->deleteDocument('tokens', $token); + $dbForProject->deleteCachedDocument('users', $user->getId()); $user ->setAttribute('emailVerification', true) - ->setAttribute('sessions', $session, Document::SET_TYPE_APPEND) - ->setAttribute('tokens', $tokens); - + ->setAttribute('sessions', $session, Document::SET_TYPE_APPEND); $user = $dbForProject->updateDocument('users', $user->getId(), $user); @@ -947,8 +939,8 @@ App::post('/v1/account/sessions/anonymous') 'name' => null, 'prefs' => new \stdClass(), 'sessions' => [], - 'tokens' => [], - 'memberships' => [], + 'tokens' => null, + 'memberships' => null, 'search' => $userId, 'deleted' => false ]))); @@ -1864,9 +1856,12 @@ App::post('/v1/account/recovery') Authorization::setRole('user:' . $profile->getId()); - $profile->setAttribute('tokens', $recovery, Document::SET_TYPE_APPEND); + $recovery = $dbForProject->createDocument('tokens', $recovery + ->setAttribute('$read', ['user:'.$profile->getId()]) + ->setAttribute('$write', ['user:'.$profile->getId()]) + ); - $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile); + $dbForProject->deleteCachedDocument('users', $profile->getId()); $url = Template::parseURL($url); $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $profile->getId(), 'secret' => $secret, 'expire' => $expire]); @@ -1956,18 +1951,14 @@ App::put('/v1/account/recovery') ->setAttribute('emailVerification', true) ); + $recoveryDocument = $dbForProject->getDocument('tokens', $recovery); + /** * We act like we're updating and validating * the recovery token but actually we don't need it anymore. */ - foreach ($tokens as $key => $token) { - if ($recovery === $token->getId()) { - $recovery = $token; - unset($tokens[$key]); - } - } - - $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens)); + $dbForProject->deleteDocument('tokens', $recovery); + $dbForProject->deleteCachedDocument('users', $profile->getId()); $audits->setResource('user/' . $profile->getId()); @@ -1975,10 +1966,10 @@ App::put('/v1/account/recovery') $events ->setParam('userId', $profile->getId()) - ->setParam('tokenId', $recovery->getId()) + ->setParam('tokenId', $recoveryDocument->getId()) ; - $response->dynamic($recovery, Response::MODEL_TOKEN); + $response->dynamic($recoveryDocument, Response::MODEL_TOKEN); }); App::post('/v1/account/verification') @@ -2042,9 +2033,12 @@ App::post('/v1/account/verification') Authorization::setRole('user:' . $user->getId()); - $user->setAttribute('tokens', $verification, Document::SET_TYPE_APPEND); + $verification = $dbForProject->createDocument('tokens', $verification + ->setAttribute('$read', ['user:'.$user->getId()]) + ->setAttribute('$write', ['user:'.$user->getId()]) + ); - $user = $dbForProject->updateDocument('users', $user->getId(), $user); + $dbForProject->deleteCachedDocument('users', $user->getId()); $url = Template::parseURL($url); $url['query'] = Template::mergeQuery(((isset($url['query'])) ? $url['query'] : ''), ['userId' => $user->getId(), 'secret' => $verificationSecret, 'expire' => $expire]); @@ -2108,7 +2102,7 @@ App::put('/v1/account/verification') /** @var Appwrite\Stats\Stats $usage */ /** @var Appwrite\Event\Event $events */ - $profile = $dbForProject->getDocument('users', $userId); + $profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId)); if ($profile->isEmpty()) { throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); @@ -2125,18 +2119,14 @@ App::put('/v1/account/verification') $profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true)); + $verificationDocument = $dbForProject->getDocument('tokens', $verification); + /** * We act like we're updating and validating * the verification token but actually we don't need it anymore. */ - foreach ($tokens as $key => $token) { - if ($token->getId() === $verification) { - $verification = $token; - unset($tokens[$key]); - } - } - - $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens)); + $dbForProject->deleteDocument('tokens', $verification); + $dbForProject->deleteCachedDocument('users', $profile->getId()); $audits->setResource('user/' . $user->getId()); @@ -2144,8 +2134,8 @@ App::put('/v1/account/verification') $events ->setParam('userId', $user->getId()) - ->setParam('tokenId', $verification->getId()) + ->setParam('tokenId', $verificationDocument->getId()) ; - $response->dynamic($verification, Response::MODEL_TOKEN); + $response->dynamic($verificationDocument, Response::MODEL_TOKEN); }); diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index 939b5ef96f..d9ff247911 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -1,5 +1,7 @@ setContentType('image/png') ->addHeader('Expires', $date) ->addHeader('X-Appwrite-Cache', 'hit') - ->send($data) - ; + ->send($data); } $image = new Image(\file_get_contents($path)); @@ -95,7 +94,7 @@ App::get('/v1/avatars/credit-cards/:code') ->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true) ->inject('response') - ->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response)); + ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('credit-cards', $code, $width, $height, $quality, $response)); App::get('/v1/avatars/browsers/:code') ->desc('Get Browser Icon') @@ -113,7 +112,7 @@ App::get('/v1/avatars/browsers/:code') ->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true) ->inject('response') - ->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response)); + ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('browsers', $code, $width, $height, $quality, $response)); App::get('/v1/avatars/flags/:code') ->desc('Get Country Flag') @@ -131,7 +130,7 @@ App::get('/v1/avatars/flags/:code') ->param('height', 100, new Range(0, 2000), 'Image height. Pass an integer between 0 to 2000. Defaults to 100.', true) ->param('quality', 100, new Range(0, 100), 'Image quality. Pass an integer between 0 to 100. Defaults to 100.', true) ->inject('response') - ->action(fn($code, $width, $height, $quality, $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response)); + ->action(fn (string $code, int $width, int $height, int $quality, Response $response) => $avatarCallback('flags', $code, $width, $height, $quality, $response)); App::get('/v1/avatars/image') ->desc('Get Image from URL') @@ -148,8 +147,7 @@ App::get('/v1/avatars/image') ->param('width', 400, new Range(0, 2000), 'Resize preview image width, Pass an integer between 0 to 2000. Defaults to 400.', true) ->param('height', 400, new Range(0, 2000), 'Resize preview image height, Pass an integer between 0 to 2000. Defaults to 400.', true) ->inject('response') - ->action(function ($url, $width, $height, $response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (string $url, int $width, int $height, Response $response) { $quality = 80; $output = 'png'; @@ -164,8 +162,7 @@ App::get('/v1/avatars/image') ->setContentType('image/png') ->addHeader('Expires', $date) ->addHeader('X-Appwrite-Cache', 'hit') - ->send($data) - ; + ->send($data); } if (!\extension_loaded('imagick')) { @@ -180,7 +177,7 @@ App::get('/v1/avatars/image') try { $image = new Image($fetch); - } catch (\Exception$exception) { + } catch (\Exception $exception) { throw new Exception('Unable to parse image', 500, Exception::GENERAL_SERVER_ERROR); } @@ -196,8 +193,7 @@ App::get('/v1/avatars/image') ->setContentType('image/png') ->addHeader('Expires', $date) ->addHeader('X-Appwrite-Cache', 'miss') - ->send($data); - ; + ->send($data);; unset($image); }); @@ -215,8 +211,7 @@ App::get('/v1/avatars/favicon') ->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE) ->param('url', '', new URL(['http', 'https']), 'Website URL which you want to fetch the favicon from.') ->inject('response') - ->action(function ($url, $response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (string $url, Response $response) { $width = 56; $height = 56; @@ -233,8 +228,7 @@ App::get('/v1/avatars/favicon') ->setContentType('image/png') ->addHeader('Expires', $date) ->addHeader('X-Appwrite-Cache', 'hit') - ->send($data) - ; + ->send($data); } if (!\extension_loaded('imagick')) { @@ -248,7 +242,8 @@ App::get('/v1/avatars/favicon') CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 3, CURLOPT_URL => $url, - CURLOPT_USERAGENT => \sprintf(APP_USERAGENT, + CURLOPT_USERAGENT => \sprintf( + APP_USERAGENT, App::getEnv('_APP_VERSION', 'UNKNOWN'), App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY) ), @@ -326,8 +321,7 @@ App::get('/v1/avatars/favicon') ->setContentType('image/x-icon') ->addHeader('Expires', $date) ->addHeader('X-Appwrite-Cache', 'miss') - ->send($data) - ; + ->send($data); } $fetch = @\file_get_contents($outputHref, false); @@ -371,8 +365,7 @@ App::get('/v1/avatars/qr') ->param('margin', 1, new Range(0, 10), 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true) ->param('download', false, new Boolean(true), 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.', true) ->inject('response') - ->action(function ($text, $size, $margin, $download, $response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (string $text, int $size, int $margin, bool $download, Response $response) { $download = ($download === '1' || $download === 'true' || $download === 1 || $download === true); $options = new QROptions([ @@ -394,8 +387,7 @@ App::get('/v1/avatars/qr') $response ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache ->setContentType('image/png') - ->send($image->output('png', 9)) - ; + ->send($image->output('png', 9)); }); App::get('/v1/avatars/initials') @@ -416,9 +408,7 @@ App::get('/v1/avatars/initials') ->param('background', '', new HexColor(), 'Changes background color. By default a random color will be picked and stay will persistent to the given name.', true) ->inject('response') ->inject('user') - ->action(function ($name, $width, $height, $color, $background, $response, $user) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $user */ + ->action(function (string $name, int $width, int $height, string $color, string $background, Response $response, Document $user) { $themes = [ ['color' => '#27005e', 'background' => '#e1d2f6'], // VIOLET @@ -438,8 +428,8 @@ App::get('/v1/avatars/initials') $name = (!empty($name)) ? $name : $user->getAttribute('name', $user->getAttribute('email', '')); $words = \explode(' ', \strtoupper($name)); // if there is no space, try to split by `_` underscore - $words = (count($words) == 1 ) ? \explode('_', \strtoupper($name)) : $words; - + $words = (count($words) == 1) ? \explode('_', \strtoupper($name)) : $words; + $initials = null; $code = 0; @@ -478,6 +468,5 @@ App::get('/v1/avatars/initials') $response ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache ->setContentType('image/png') - ->send($image->getImageBlob()) - ; + ->send($image->getImageBlob()); }); diff --git a/app/controllers/api/database.php b/app/controllers/api/database.php index 19f2643910..7277044aec 100644 --- a/app/controllers/api/database.php +++ b/app/controllers/api/database.php @@ -154,7 +154,7 @@ App::post('/v1/database/collections') ->label('sdk.response.model', Response::MODEL_COLLECTION) ->param('collectionId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.') ->param('name', '', new Text(128), 'Collection name. Max length: 128 chars.') - ->param('permission', null, new WhiteList(['document', 'collection']), 'Permissions type model to use for reading documents in this collection. You can use collection-level permission set once on the collection using the `read` and `write` params, or you can set document-level permission where each document read and write params will decide who has access to read and write to each document individually. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') + ->param('permission', null, new WhiteList(['document', 'collection']), 'Specifies the permissions model used in this collection, which accepts either \'collection\' or \'document\'. For \'collection\' level permission, the permissions specified in read and write params are applied to all documents in the collection. For \'document\' level permissions, read and write permissions are specified in each document. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') ->param('read', null, new Permissions(), 'An array of strings with read permissions. By default no user is granted with any read permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') ->param('write', null, new Permissions(), 'An array of strings with write permissions. By default no user is granted with any write permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.') ->inject('response') diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 640686112a..f7de8362a2 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -909,7 +909,7 @@ App::post('/v1/functions/:functionId/executions') 'trigger' => 'http', // http / schedule / event 'status' => 'waiting', // waiting / processing / completed / failed 'statusCode' => 0, - 'stdout' => '', + 'response' => '', 'stderr' => '', 'time' => 0.0, 'search' => implode(' ', [$functionId, $executionId]), @@ -993,7 +993,7 @@ App::post('/v1/functions/:functionId/executions') /** Update execution status */ $execution->setAttribute('status', $executionResponse['status']); $execution->setAttribute('statusCode', $executionResponse['statusCode']); - $execution->setAttribute('stdout', $executionResponse['stdout']); + $execution->setAttribute('response', $executionResponse['response']); $execution->setAttribute('stderr', $executionResponse['stderr']); $execution->setAttribute('time', $executionResponse['time']); } catch (\Throwable $th) { diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index dcb506faeb..1fc24fd0b2 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -1,13 +1,15 @@ desc('Get HTTP') @@ -21,8 +23,7 @@ App::get('/v1/health') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (Response $response) { $output = [ 'status' => 'pass', @@ -40,8 +41,7 @@ App::get('/v1/health/version') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_VERSION) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (Response $response) { $response->dynamic(new Document([ 'version' => APP_VERSION_STABLE ]), Response::MODEL_HEALTH_VERSION); }); @@ -59,9 +59,7 @@ App::get('/v1/health/db') ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) ->inject('response') ->inject('utopia') - ->action(function ($response, $utopia) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\App $utopia */ + ->action(function (Response $response, App $utopia) { $checkStart = \microtime(true); @@ -99,10 +97,7 @@ App::get('/v1/health/cache') ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) ->inject('response') ->inject('utopia') - ->action(function ($response, $utopia) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\App $utopia */ - /** @var Redis */ + ->action(function (Response $response, App $utopia) { $checkStart = \microtime(true); @@ -132,8 +127,7 @@ App::get('/v1/health/time') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_TIME) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (Response $response) { /* * Code from: @see https://www.beliefmedia.com.au/query-ntp-time-server @@ -190,8 +184,7 @@ App::get('/v1/health/queue/webhooks') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (Response $response) { $response->dynamic(new Document([ 'size' => Resque::size(Event::WEBHOOK_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); @@ -208,8 +201,7 @@ App::get('/v1/health/queue/logs') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (Response $response) { $response->dynamic(new Document([ 'size' => Resque::size(Event::AUDITS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); @@ -226,8 +218,7 @@ App::get('/v1/health/queue/certificates') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (Response $response) { $response->dynamic(new Document([ 'size' => Resque::size(Event::CERTIFICATES_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); @@ -244,8 +235,7 @@ App::get('/v1/health/queue/functions') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_QUEUE) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (Response $response) { $response->dynamic(new Document([ 'size' => Resque::size(Event::FUNCTIONS_QUEUE_NAME) ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); @@ -262,8 +252,7 @@ App::get('/v1/health/storage/local') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_STATUS) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (Response $response) { $checkStart = \microtime(true); @@ -304,8 +293,7 @@ App::get('/v1/health/anti-virus') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_HEALTH_ANTIVIRUS) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ + ->action(function (Response $response) { $output = [ 'status' => '', @@ -341,10 +329,7 @@ App::get('/v1/health/stats') // Currently only used internally ->inject('response') ->inject('register') ->inject('deviceFiles') - ->action(function ($response, $register, $deviceFiles) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Registry\Registry $register */ - /** @var Utopia\Storage\Device $deviceFiles */ + ->action(function (Response $response, Registry $register, Device $deviceFiles) { $cache = $register->get('cache'); diff --git a/app/controllers/api/locale.php b/app/controllers/api/locale.php index 34018f6e90..cf7c49566e 100644 --- a/app/controllers/api/locale.php +++ b/app/controllers/api/locale.php @@ -1,9 +1,12 @@ desc('Get User Locale') @@ -20,12 +23,7 @@ App::get('/v1/locale') ->inject('response') ->inject('locale') ->inject('geodb') - ->action(function ($request, $response, $locale, $geodb) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Locale\Locale $locale */ - /** @var MaxMind\Db\Reader $geodb */ - + ->action(function (Request $request, Response $response, Locale $locale, Reader $geodb) { $eu = Config::getParam('locale-eu'); $currencies = Config::getParam('locale-currencies'); $output = []; @@ -40,8 +38,8 @@ App::get('/v1/locale') if ($record) { $output['countryCode'] = $record['country']['iso_code']; - $output['country'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); - $output['continent'] = $locale->getText('continents.'.strtolower($record['continent']['code']), $locale->getText('locale.country.unknown')); + $output['country'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown')); + $output['continent'] = $locale->getText('continents.' . strtolower($record['continent']['code']), $locale->getText('locale.country.unknown')); $output['continent'] = (isset($continents[$record['continent']['code']])) ? $continents[$record['continent']['code']] : $locale->getText('locale.country.unknown'); $output['continentCode'] = $record['continent']['code']; $output['eu'] = (\in_array($record['country']['iso_code'], $eu)) ? true : false; @@ -63,8 +61,8 @@ App::get('/v1/locale') } $response - ->addHeader('Cache-Control', 'public, max-age='.$time) - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache + ->addHeader('Cache-Control', 'public, max-age=' . $time) + ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache ; $response->dynamic(new Document($output), Response::MODEL_LOCALE); }); @@ -82,16 +80,13 @@ App::get('/v1/locale/countries') ->label('sdk.response.model', Response::MODEL_COUNTRY_LIST) ->inject('response') ->inject('locale') - ->action(function ($response, $locale) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Locale\Locale $locale */ - + ->action(function (Response $response, Locale $locale) { $list = Config::getParam('locale-countries'); /* @var $list array */ $output = []; foreach ($list as $value) { $output[] = new Document([ - 'name' => $locale->getText('countries.'.strtolower($value)), + 'name' => $locale->getText('countries.' . strtolower($value)), 'code' => $value, ]); } @@ -116,17 +111,14 @@ App::get('/v1/locale/countries/eu') ->label('sdk.response.model', Response::MODEL_COUNTRY_LIST) ->inject('response') ->inject('locale') - ->action(function ($response, $locale) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Locale\Locale $locale */ - + ->action(function (Response $response, Locale $locale) { $eu = Config::getParam('locale-eu'); $output = []; foreach ($eu as $code) { - if ($locale->getText('countries.'.strtolower($code), false) !== false) { + if ($locale->getText('countries.' . strtolower($code), false) !== false) { $output[] = new Document([ - 'name' => $locale->getText('countries.'.strtolower($code)), + 'name' => $locale->getText('countries.' . strtolower($code)), 'code' => $code, ]); } @@ -152,21 +144,18 @@ App::get('/v1/locale/countries/phones') ->label('sdk.response.model', Response::MODEL_PHONE_LIST) ->inject('response') ->inject('locale') - ->action(function ($response, $locale) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Locale\Locale $locale */ - + ->action(function (Response $response, Locale $locale) { $list = Config::getParam('locale-phones'); /* @var $list array */ $output = []; \asort($list); foreach ($list as $code => $name) { - if ($locale->getText('countries.'.strtolower($code), false) !== false) { + if ($locale->getText('countries.' . strtolower($code), false) !== false) { $output[] = new Document([ - 'code' => '+'.$list[$code], + 'code' => '+' . $list[$code], 'countryCode' => $code, - 'countryName' => $locale->getText('countries.'.strtolower($code)), + 'countryName' => $locale->getText('countries.' . strtolower($code)), ]); } } @@ -187,15 +176,12 @@ App::get('/v1/locale/continents') ->label('sdk.response.model', Response::MODEL_CONTINENT_LIST) ->inject('response') ->inject('locale') - ->action(function ($response, $locale) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Locale\Locale $locale */ + ->action(function (Response $response, Locale $locale) { + $list = Config::getParam('locale-continents'); - $list = Config::getParam('locale-continents'); /* @var $list array */ - - foreach ($list as $key => $value) { + foreach ($list as $value) { $output[] = new Document([ - 'name' => $locale->getText('continents.'.strtolower($value)), + 'name' => $locale->getText('continents.' . strtolower($value)), 'code' => $value, ]); } @@ -219,12 +205,10 @@ App::get('/v1/locale/currencies') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_CURRENCY_LIST) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ - + ->action(function (Response $response) { $list = Config::getParam('locale-currencies'); - $list = array_map(fn($node) => new Document($node), $list); + $list = array_map(fn ($node) => new Document($node), $list); $response->dynamic(new Document(['currencies' => $list, 'total' => \count($list)]), Response::MODEL_CURRENCY_LIST); }); @@ -242,12 +226,10 @@ App::get('/v1/locale/languages') ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_LANGUAGE_LIST) ->inject('response') - ->action(function ($response) { - /** @var Appwrite\Utopia\Response $response */ - + ->action(function (Response $response) { $list = Config::getParam('locale-languages'); $list = array_map(fn ($node) => new Document($node), $list); $response->dynamic(new Document(['languages' => $list, 'total' => \count($list)]), Response::MODEL_LANGUAGE_LIST); - }); \ No newline at end of file + }); diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 1884550b0a..48fafc8f81 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -3,6 +3,7 @@ use Appwrite\Auth\Auth; use Appwrite\Auth\Validator\Password; use Appwrite\Event\Certificate; +use Appwrite\Event\Delete; use Appwrite\Event\Validator\Event; use Appwrite\Network\Validator\CNAME; use Appwrite\Network\Validator\Domain as DomainValidator; @@ -20,6 +21,7 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; +use Utopia\Registry\Registry; use Appwrite\Extend\Exception; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; @@ -28,8 +30,7 @@ use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; -App::init(function ($project) { - /** @var Utopia\Database\Document $project */ +App::init(function (Document $project) { if ($project->getId() !== 'console') { throw new Exception('Access to this API is forbidden.', 401, Exception::GENERAL_ACCESS_FORBIDDEN); @@ -61,10 +62,7 @@ App::post('/v1/projects') ->inject('response') ->inject('dbForConsole') ->inject('dbForProject') - ->action(function ($projectId, $name, $teamId, $description, $logo, $url, $legalName, $legalCountry, $legalState, $legalCity, $legalAddress, $legalTaxId, $response, $dbForConsole, $dbForProject) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var Utopia\Database\Database $dbForProject */ + ->action(function (string $projectId, string $name, string $teamId, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole, Database $dbForProject) { $team = $dbForConsole->getDocument('teams', $teamId); @@ -175,9 +173,7 @@ App::get('/v1/projects') ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true) ->inject('response') ->inject('dbForConsole') - ->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForConsole) { if (!empty($cursor)) { $cursorProject = $dbForConsole->getDocument('projects', $cursor); @@ -215,9 +211,7 @@ App::get('/v1/projects/:projectId') ->param('projectId', '', new UID(), 'Project unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -244,11 +238,7 @@ App::get('/v1/projects/:projectId/usage') ->inject('dbForConsole') ->inject('dbForProject') ->inject('register') - ->action(function ($projectId, $range, $response, $dbForConsole, $dbForProject, $register) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Registry\Registry $register */ + ->action(function (string $projectId, string $range, Response $response, Database $dbForConsole, Database $dbForProject, Registry $register) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -365,9 +355,7 @@ App::patch('/v1/projects/:projectId') ->param('legalTaxId', '', new Text(256), 'Project legal tax ID. Max length: 256 chars.', true) ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $name, $description, $logo, $url, $legalName, $legalCountry, $legalState, $legalCity, $legalAddress, $legalTaxId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $name, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -407,10 +395,7 @@ App::patch('/v1/projects/:projectId/service') ->param('status', null, new Boolean(), 'Service status.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $service, $status, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var Boolean $status */ + ->action(function (string $projectId, string $service, bool $status, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -442,9 +427,7 @@ App::patch('/v1/projects/:projectId/oauth2') ->param('secret', '', new text(512), 'Provider secret key. Max length: 512 chars.', true) ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $provider, $appId, $secret, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $provider, string $appId, string $secret, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -475,9 +458,7 @@ App::patch('/v1/projects/:projectId/auth/limit') ->param('limit', false, new Range(0, APP_LIMIT_USERS), 'Set the max number of users allowed in this project. Use 0 for unlimited.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $limit, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, int $limit, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -510,9 +491,7 @@ App::patch('/v1/projects/:projectId/auth/:method') ->param('status', false, new Boolean(true), 'Set the status of this auth method.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $method, $status, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $method, bool $status, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); $auth = Config::getParam('auth')[$method] ?? []; @@ -546,11 +525,7 @@ App::delete('/v1/projects/:projectId') ->inject('user') ->inject('dbForConsole') ->inject('deletes') - ->action(function ($projectId, $password, $response, $user, $dbForConsole, $deletes) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $user */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var Appwrite\Event\Delete $deletes */ + ->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); @@ -599,9 +574,7 @@ App::post('/v1/projects/:projectId/webhooks') ->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true) ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $name, $events, $url, $security, $httpUser, $httpPass, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $name, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -645,9 +618,7 @@ App::get('/v1/projects/:projectId/webhooks') ->param('projectId', '', new UID(), 'Project unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -679,9 +650,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId') ->param('webhookId', null, new UID(), 'Webhook unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $webhookId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -721,9 +690,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId') ->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true) ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $webhookId, $name, $events, $url, $security, $httpUser, $httpPass, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $webhookId, string $name, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -771,9 +738,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') ->param('webhookId', null, new UID(), 'Webhook unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $webhookId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $webhookId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -814,9 +779,7 @@ App::post('/v1/projects/:projectId/keys') ->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true)), 'Key scopes list.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $name, $scopes, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $name, array $scopes, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -855,9 +818,7 @@ App::get('/v1/projects/:projectId/keys') ->param('projectId', null, new UID(), 'Project unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -889,9 +850,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') ->param('keyId', null, new UID(), 'Key unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $keyId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -927,9 +886,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') ->param('scopes', null, new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true)), 'Key scopes list') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $keyId, $name, $scopes, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $keyId, string $name, array $scopes, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -971,9 +928,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId') ->param('keyId', null, new UID(), 'Key unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $keyId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $keyId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1017,9 +972,7 @@ App::post('/v1/projects/:projectId/platforms') ->param('hostname', '', new Text(256), 'Platform client hostname. Max length: 256 chars.', true) ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $type, $name, $key, $store, $hostname, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $type, string $name, string $key, string $store, string $hostname, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1062,9 +1015,7 @@ App::get('/v1/projects/:projectId/platforms') ->param('projectId', '', new UID(), 'Project unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1096,9 +1047,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') ->param('platformId', null, new UID(), 'Platform unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $platformId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1136,9 +1085,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId') ->param('hostname', '', new Text(256), 'Platform client URL. Max length: 256 chars.', true) ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $platformId, $name, $key, $store, $hostname, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $platformId, string $name, string $key, string $store, string $hostname, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1183,9 +1130,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') ->param('platformId', null, new UID(), 'Platform unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $platformId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $platformId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1225,9 +1170,7 @@ App::post('/v1/projects/:projectId/domains') ->param('domain', null, new DomainValidator(), 'Domain name.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $domain, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $domain, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1286,9 +1229,7 @@ App::get('/v1/projects/:projectId/domains') ->param('projectId', '', new UID(), 'Project unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1320,9 +1261,7 @@ App::get('/v1/projects/:projectId/domains/:domainId') ->param('domainId', null, new UID(), 'Domain unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $domainId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1356,9 +1295,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification') ->param('domainId', null, new UID(), 'Domain unique ID.') ->inject('response') ->inject('dbForConsole') - ->action(function ($projectId, $domainId, $response, $dbForConsole) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ + ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole) { $project = $dbForConsole->getDocument('projects', $projectId); @@ -1420,10 +1357,7 @@ App::delete('/v1/projects/:projectId/domains/:domainId') ->inject('response') ->inject('dbForConsole') ->inject('deletes') - ->action(function ($projectId, $domainId, $response, $dbForConsole, $deletes) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForConsole */ - /** @var Appwrite\Event\Delete $deletes */ + ->action(function (string $projectId, string $domainId, Response $response, Database $dbForConsole, Delete $deletes) { $project = $dbForConsole->getDocument('projects', $projectId); diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 12cc35e17b..4ac1e63905 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -2,8 +2,12 @@ use Appwrite\Auth\Auth; use Appwrite\ClamAV\Network; +use Appwrite\Event\Audit; +use Appwrite\Event\Delete; +use Appwrite\Event\Event; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\OpenSSL\OpenSSL; +use Appwrite\Stats\Stats; use Appwrite\Utopia\Response; use Utopia\App; use Utopia\Cache\Adapter\Filesystem; @@ -21,6 +25,7 @@ use Utopia\Database\Validator\UID; use Appwrite\Extend\Exception; use Utopia\Image\Image; use Utopia\Storage\Compression\Algorithms\GZIP; +use Utopia\Storage\Device; use Utopia\Storage\Device\Local; use Utopia\Storage\Storage; use Utopia\Storage\Validator\File; @@ -34,6 +39,7 @@ use Utopia\Validator\Integer; use Utopia\Validator\Range; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; +use Utopia\Swoole\Request; App::post('/v1/storage/buckets') ->desc('Create bucket') @@ -62,12 +68,7 @@ App::post('/v1/storage/buckets') ->inject('audits') ->inject('usage') ->inject('events') - ->action(function ($bucketId, $name, $permission, $read, $write, $enabled, $maximumFileSize, $allowedFileExtensions, $encryption, $antivirus, $response, $dbForProject, $audits, $usage, $events) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Event\Audit $audits */ - /** @var Appwrite\Stats\Stats $usage */ - /** @var Appwrite\Event\Event $events */ + ->action(function (string $bucketId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Event $audits, Stats $usage, Event $events) { $bucketId = $bucketId === 'unique()' ? $dbForProject->getId() : $bucketId; try { @@ -162,12 +163,9 @@ App::get('/v1/storage/buckets') ->inject('response') ->inject('dbForProject') ->inject('usage') - ->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject, $usage) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Stats\Stats $usage */ + ->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject, Stats $usage) { - $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, $search)] : []; + $queries = ($search) ? [new Query('name', Query::TYPE_SEARCH, [$search])] : []; if (!empty($cursor)) { $cursorBucket = $dbForProject->getDocument('buckets', $cursor); @@ -200,10 +198,7 @@ App::get('/v1/storage/buckets/:bucketId') ->inject('response') ->inject('dbForProject') ->inject('usage') - ->action(function ($bucketId, $response, $dbForProject, $usage) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Stats\Stats $usage */ + ->action(function (string $bucketId, Response $response, Database $dbForProject, Stats $usage) { $bucket = $dbForProject->getDocument('buckets', $bucketId); @@ -243,13 +238,7 @@ App::put('/v1/storage/buckets/:bucketId') ->inject('audits') ->inject('usage') ->inject('events') - ->action(function ($bucketId, $name, $permission, $read, $write, $enabled, $maximumFileSize, $allowedFileExtensions, $encryption, $antivirus, $response, $dbForProject, $audits, $usage, $events) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Event\Audit $audits */ - /** @var Appwrite\Stats\Stats $usage */ - /** @var Appwrite\Event\Event $events */ - + ->action(function (string $bucketId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, ?int $maximumFileSize, array $allowedFileExtensions, bool $encryption, bool $antivirus, Response $response, Database $dbForProject, Audit $audits, Stats $usage, Event $events) { $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { @@ -308,14 +297,7 @@ App::delete('/v1/storage/buckets/:bucketId') ->inject('deletes') ->inject('events') ->inject('usage') - ->action(function ($bucketId, $response, $dbForProject, $audits, $deletes, $events, $usage) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Event\Audit $audits */ - /** @var Appwrite\Event\Delete $deletes */ - /** @var Appwrite\Event\Event $events */ - /** @var Appwrite\Stats\Stats $usage */ - + ->action(function (string $bucketId, Response $response, Database $dbForProject, Audit $audits, Delete $deletes, Event $events, Stats $usage) { $bucket = $dbForProject->getDocument('buckets', $bucketId); if ($bucket->isEmpty()) { @@ -375,18 +357,7 @@ App::post('/v1/storage/buckets/:bucketId/files') ->inject('mode') ->inject('deviceFiles') ->inject('deviceLocal') - ->action(function ($bucketId, $fileId, $file, $read, $write, $request, $response, $dbForProject, $user, $audits, $usage, $events, $mode, $deviceFiles, $deviceLocal) { - /** @var Utopia\Swoole\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Database\Document $user */ - /** @var Appwrite\Event\Audit $audits */ - /** @var Appwrite\Event\Event $events */ - /** @var Appwrite\Stats\Stats $usage */ - /** @var Utopia\Storage\Device $deviceFiles */ - /** @var Utopia\Storage\Device $deviceLocal */ - /** @var string $mode */ - + ->action(function (string $bucketId, string $fileId, array $file, ?array $read, ?array $write, Request $request, Response $response, Database $dbForProject, Document $user, Audit $audits, Stats $usage, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() @@ -714,11 +685,7 @@ App::get('/v1/storage/buckets/:bucketId/files') ->inject('dbForProject') ->inject('usage') ->inject('mode') - ->action(function ($bucketId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject, $usage, $mode) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Stats\Stats $usage */ - /** @var string $mode */ + ->action(function (string $bucketId, string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject, Stats $usage, string $mode) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -794,11 +761,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('dbForProject') ->inject('usage') ->inject('mode') - ->action(function ($bucketId, $fileId, $response, $dbForProject, $usage, $mode) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Stats\Stats $usage */ - /** @var string $mode */ + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Stats $usage, string $mode) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -866,15 +829,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->inject('mode') ->inject('deviceFiles') ->inject('deviceLocal') - ->action(function ($bucketId, $fileId, $width, $height, $gravity, $quality, $borderWidth, $borderColor, $borderRadius, $opacity, $rotation, $background, $output, $request, $response, $project, $dbForProject, $usage, $mode, $deviceFiles, $deviceLocal) { - /** @var Utopia\Swoole\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $project */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Stats\Stats $usage */ - /** @var Utopia\Storage\Device $deviceFiles */ - /** @var Utopia\Storage\Device $deviceLocal */ - /** @var string $mode */ + ->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); @@ -1044,13 +999,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->inject('usage') ->inject('mode') ->inject('deviceFiles') - ->action(function ($bucketId, $fileId, $request, $response, $dbForProject, $usage, $mode, $deviceFiles) { - /** @var Utopia\Swoole\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Stats\Stats $usage */ - /** @var Utopia\Storage\Device $deviceFiles */ - /** @var string $mode */ + ->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, Stats $usage, string $mode, Device $deviceFiles) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -1187,13 +1136,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->inject('usage') ->inject('mode') ->inject('deviceFiles') - ->action(function ($bucketId, $fileId, $response, $request, $dbForProject, $usage, $mode, $deviceFiles) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Swoole\Request $request */ - /** @var Utopia\Database\Database $dbForInternal */ - /** @var Appwrite\Stats\Stats $usage */ - /** @var Utopia\Storage\Device $deviceFiles */ - /** @var string $mode */ + ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, Stats $usage, string $mode, Device $deviceFiles) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -1347,15 +1290,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('usage') ->inject('mode') ->inject('events') - ->action(function ($bucketId, $fileId, $read, $write, $response, $dbForProject, $user, $audits, $usage, $mode, $events) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Database\Document $user */ - /** @var Appwrite\Event\Audit $audits */ - /** @var Appwrite\Stats\Stats $usage */ - /** @var Appwrite\Event\Event $events */ - /** @var string $mode */ - + ->action(function (string $bucketId, string $fileId, ?array $read, ?array $write, Response $response, Database $dbForProject, Document $user, Audit $audits, Stats $usage, string $mode, Event $events) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $read = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; // By default set read permissions for user $write = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? []; @@ -1448,16 +1383,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('mode') ->inject('deviceFiles') ->inject('project') - ->action(function ($bucketId, $fileId, $response, $dbForProject, $events, $audits, $usage, $mode, $deviceFiles, $project) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Event\Event $events */ - /** @var Appwrite\Event\Audit $audits */ - /** @var Appwrite\Stats\Stats $usage */ - /** @var Utopia\Storage\Device $deviceFiles */ - /** @var string $mode */ - + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $events, Audit $audits, Stats $usage, string $mode, Device $deviceFiles, Document $project) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); if ($bucket->isEmpty() @@ -1542,9 +1468,7 @@ App::get('/v1/storage/usage') ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->action(function ($range, $response, $dbForProject) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ + ->action(function (string $range, Response $response, Database $dbForProject) { $usage = []; if (App::getEnv('_APP_USAGE_STATS', 'enabled') === 'enabled') { @@ -1654,9 +1578,7 @@ App::get('/v1/storage/:bucketId/usage') ->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true) ->inject('response') ->inject('dbForProject') - ->action(function ($bucketId, $range, $response, $dbForProject) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ + ->action(function (string $bucketId, string $range, Response $response, Database $dbForProject) { $bucket = $dbForProject->getDocument('buckets', $bucketId); diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 78811a6b19..3739803b30 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -2,13 +2,19 @@ use Appwrite\Auth\Auth; use Appwrite\Detector\Detector; +use Appwrite\Event\Audit as EventAudit; +use Appwrite\Event\Delete; +use Appwrite\Event\Event; +use Appwrite\Event\Mail; +use Appwrite\Extend\Exception; use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\Host; use Appwrite\Template\Template; use Appwrite\Utopia\Database\Validator\CustomId; +use Appwrite\Utopia\Request; use Appwrite\Utopia\Response; +use MaxMind\Db\Reader; use Utopia\App; -use Appwrite\Extend\Exception; use Utopia\Audit\Audit; use Utopia\Config\Config; use Utopia\Database\Database; @@ -19,6 +25,7 @@ use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Key; use Utopia\Database\Validator\UID; +use Utopia\Locale\Locale; use Utopia\Validator\Text; use Utopia\Validator\Range; use Utopia\Validator\ArrayList; @@ -44,12 +51,7 @@ App::post('/v1/teams') ->inject('dbForProject') ->inject('events') ->inject('audits') - ->action(function ($teamId, $name, $roles, $response, $user, $dbForProject, $events, $audits) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $user */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Event\Event $events */ - /** @var Appwrite\Event\Event $audits */ + ->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $events, Event $audits) { $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); $isAppUser = Auth::isAppUser(Authorization::getRoles()); @@ -82,10 +84,7 @@ App::post('/v1/teams') ]); $membership = $dbForProject->createDocument('memberships', $membership); - - // Attach user to team - $user->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND); - $user = $dbForProject->updateDocument('users', $user->getId(), $user); + $dbForProject->deleteCachedDocument('users', $user->getId()); } $events->setParam('teamId', $team->getId()); @@ -123,9 +122,7 @@ App::get('/v1/teams') ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true) ->inject('response') ->inject('dbForProject') - ->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ + ->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) { if (!empty($cursor)) { $cursorTeam = $dbForProject->getDocument('teams', $cursor); @@ -164,9 +161,7 @@ App::get('/v1/teams/:teamId') ->param('teamId', '', new UID(), 'Team ID.') ->inject('response') ->inject('dbForProject') - ->action(function ($teamId, $response, $dbForProject) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ + ->action(function (string $teamId, Response $response, Database $dbForProject) { $team = $dbForProject->getDocument('teams', $teamId); @@ -194,10 +189,7 @@ App::put('/v1/teams/:teamId') ->inject('response') ->inject('dbForProject') ->inject('events') - ->action(function ($teamId, $name, $response, $dbForProject, $events) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Event\Event $events */ + ->action(function (string $teamId, string $name, Response $response, Database $dbForProject, Event $events) { $team = $dbForProject->getDocument('teams', $teamId); @@ -232,11 +224,7 @@ App::delete('/v1/teams/:teamId') ->inject('events') ->inject('deletes') ->inject('audits') - ->action(function ($teamId, $response, $dbForProject, $events, $deletes, $audits) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Event\Event $events */ - /** @var Appwrite\Event\Delete $deletes */ + ->action(function (string $teamId, Response $response, Database $dbForProject, Event $events, Delete $deletes, EventAudit $audits) { $team = $dbForProject->getDocument('teams', $teamId); @@ -304,14 +292,7 @@ App::post('/v1/teams/:teamId/memberships') ->inject('audits') ->inject('mails') ->inject('events') - ->action(function ($teamId, $email, $roles, $url, $name, $response, $project, $user, $dbForProject, $locale, $audits, $mails, $events) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $project */ - /** @var Utopia\Database\Document $user */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Event\Audit $audits */ - /** @var Appwrite\Event\Mail $mails */ - /** @var Appwrite\Event\Event $events */ + ->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, EventAudit $audits, Mail $mails, Event $events) { if(empty(App::getEnv('_APP_SMTP_HOST'))) { throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED); @@ -363,9 +344,10 @@ App::post('/v1/teams/:teamId/memberships') 'name' => $name, 'prefs' => new \stdClass(), 'sessions' => [], - 'tokens' => [], - 'memberships' => [], + 'tokens' => null, + 'memberships' => null, 'search' => implode(' ', [$userId, $email, $name]), + 'deleted' => false ]))); } catch (Duplicate $th) { throw new Exception('Account already exists', 409, Exception::USER_ALREADY_EXISTS); @@ -404,10 +386,7 @@ App::post('/v1/teams/:teamId/memberships') $team->setAttribute('total', $team->getAttribute('total', 0) + 1); $team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team)); - // Attach user to team - $invitee->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND); - - $invitee = Authorization::skip(fn() => $dbForProject->updateDocument('users', $invitee->getId(), $invitee)); + $dbForProject->deleteCachedDocument('users', $invitee->getId()); } else { try { $membership = $dbForProject->createDocument('memberships', $membership); @@ -469,9 +448,7 @@ App::get('/v1/teams/:teamId/memberships') ->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true) ->inject('response') ->inject('dbForProject') - ->action(function ($teamId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ + ->action(function (string $teamId, string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) { $team = $dbForProject->getDocument('teams', $teamId); @@ -543,9 +520,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId') ->param('membershipId', '', new UID(), 'Membership ID.') ->inject('response') ->inject('dbForProject') - ->action(function ($teamId, $membershipId, $response, $dbForProject) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ + ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject) { $team = $dbForProject->getDocument('teams', $teamId); @@ -590,13 +565,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') ->inject('dbForProject') ->inject('audits') ->inject('events') - ->action(function ($teamId, $membershipId, $roles, $request, $response, $user, $dbForProject, $audits, $events) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $user */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Event\Audit $audits */ - /** @var Appwrite\Event\Event $events */ + ->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Database $dbForProject, EventAudit $audits, Event $events) { $team = $dbForProject->getDocument('teams', $teamId); if ($team->isEmpty()) { @@ -630,13 +599,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') /** * Replace membership on profile */ - $memberships = array_filter($profile->getAttribute('memberships'), fn (Document $m) => $m->getId() !== $membership->getId()); - - $profile - ->setAttribute('memberships', $memberships) - ->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND); - - Authorization::skip(fn () => $dbForProject->updateDocument('users', $profile->getId(), $profile)); + $dbForProject->deleteCachedDocument('users', $profile->getId()); $audits->setResource('team/' . $teamId); @@ -675,15 +638,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') ->inject('geodb') ->inject('audits') ->inject('events') - ->action(function ($teamId, $membershipId, $userId, $secret, $request, $response, $user, $dbForProject, $geodb, $audits, $events) { - /** @var Appwrite\Utopia\Request $request */ - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Document $user */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var MaxMind\Db\Reader $geodb */ - /** @var Appwrite\Event\Audit $audits */ - /** @var Appwrite\Event\Event $events */ - + ->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Reader $geodb, EventAudit $audits, Event $events) { $protocol = $request->getProtocol(); $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -729,7 +684,6 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $user ->setAttribute('emailVerification', true) - ->setAttribute('memberships', $membership, Document::SET_TYPE_APPEND) ; // Log user in @@ -763,6 +717,8 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $user = $dbForProject->updateDocument('users', $user->getId(), $user); $membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership); + + $dbForProject->deleteCachedDocument('users', $user->getId()); $team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1))); @@ -807,11 +763,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') ->inject('dbForProject') ->inject('audits') ->inject('events') - ->action(function ($teamId, $membershipId, $response, $dbForProject, $audits, $events) { - /** @var Appwrite\Utopia\Response $response */ - /** @var Utopia\Database\Database $dbForProject */ - /** @var Appwrite\Event\Audit $audits */ - /** @var Appwrite\Event\Event $events */ + ->action(function (string $teamId, string $membershipId, Response $response, Database $dbForProject, EventAudit $audits, Event $events) { $membership = $dbForProject->getDocument('memberships', $membershipId); @@ -843,20 +795,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') throw new Exception('Failed to remove membership from DB', 500, Exception::GENERAL_SERVER_ERROR); } - $memberships = $user->getAttribute('memberships', []); - - foreach ($memberships as $key => $child) { - /** @var Document $child */ - - if ($membershipId == $child->getId()) { - unset($memberships[$key]); - break; - } - } - - $user->setAttribute('memberships', $memberships); - - Authorization::skip(fn() => $dbForProject->updateDocument('users', $user->getId(), $user)); + $dbForProject->deleteCachedDocument('users', $user->getId()); if ($membership->getAttribute('confirm')) { // Count only confirmed members $team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0)); diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index a6743a0626..f62132c100 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -66,8 +66,8 @@ App::post('/v1/users') 'name' => $name, 'prefs' => new \stdClass(), 'sessions' => [], - 'tokens' => [], - 'memberships' => [], + 'tokens' => null, + 'memberships' => null, 'search' => implode(' ', [$userId, $email, $name]), 'deleted' => false ])); @@ -457,7 +457,12 @@ App::patch('/v1/users/:userId/name') throw new Exception('User not found', 404, Exception::USER_NOT_FOUND); } - $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('name', $name)); + $user + ->setAttribute('name', $name) + ->setAttribute('search', \implode(' ', [$user->getId(), $user->getAttribute('email'), $name])); + ; + + $user = $dbForProject->updateDocument('users', $user->getId(), $user); $audits ->setResource('user/'.$user->getId()) @@ -554,8 +559,13 @@ App::patch('/v1/users/:userId/email') $email = \strtolower($email); + $user + ->setAttribute('email', $email) + ->setAttribute('search', \implode(' ', [$user->getId(), $email, $user->getAttribute('name')])) + ; + try { - $user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('email', $email)); + $user = $dbForProject->updateDocument('users', $user->getId(), $user); } catch(Duplicate $th) { throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS); } @@ -764,7 +774,7 @@ App::delete('/v1/users/:userId') ->setAttribute("email", null) ->setAttribute("password", null) ->setAttribute("deleted", true) - ->setAttribute("tokens", []) + ->setAttribute("tokens", null) ->setAttribute("search", null) ; diff --git a/app/executor.php b/app/executor.php index 5183b49e96..9d77d3f4f7 100644 --- a/app/executor.php +++ b/app/executor.php @@ -279,7 +279,7 @@ App::post('/v1/runtimes') $endTime = \time(); $container = array_merge($container, [ 'status' => 'ready', - 'stdout' => \utf8_encode($stdout), + 'response' => \utf8_encode($stdout), 'stderr' => \utf8_encode($stderr), 'startTime' => $startTime, 'endTime' => $endTime, @@ -512,7 +512,7 @@ App::post('/v1/execution') $execution = [ 'status' => $functionStatus, 'statusCode' => $statusCode, - 'stdout' => \utf8_encode(\mb_substr($stdout, -16384)), + 'response' => \utf8_encode(\mb_substr($stdout, -16384)), 'stderr' => \utf8_encode(\mb_substr($stderr, -16384)), 'time' => $executionTime, ]; diff --git a/app/init.php b/app/init.php index cb2847ee31..ad08dff40d 100644 --- a/app/init.php +++ b/app/init.php @@ -305,6 +305,30 @@ Database::addFilter('subQueryWebhooks', } ); +Database::addFilter('subQueryTokens', + function($value) { + return null; + }, + function($value, Document $document, Database $database) { + return Authorization::skip(fn() => $database + ->find('tokens', [ + new Query('userId', Query::TYPE_EQUAL, [$document->getId()]) + ], $database->getIndexLimit(), 0, [])); + } +); + +Database::addFilter('subQueryMemberships', + function($value) { + return null; + }, + function($value, Document $document, Database $database) { + return Authorization::skip(fn() => $database + ->find('memberships', [ + new Query('userId', Query::TYPE_EQUAL, [$document->getId()]) + ], $database->getIndexLimit(), 0, [])); + } +); + Database::addFilter('encrypt', function($value) { $key = App::getEnv('_APP_OPENSSL_KEY_V1'); diff --git a/app/views/console/database/collection.phtml b/app/views/console/database/collection.phtml index eb045c9f2c..48662ead17 100644 --- a/app/views/console/database/collection.phtml +++ b/app/views/console/database/collection.phtml @@ -1040,7 +1040,7 @@ $logs = $this->getParam('logs', null);
- +
diff --git a/app/views/console/database/document.phtml b/app/views/console/database/document.phtml index 8324c680d0..a48d533830 100644 --- a/app/views/console/database/document.phtml +++ b/app/views/console/database/document.phtml @@ -55,17 +55,26 @@ $logs = $this->getParam('logs', null); data-analytics-activity data-analytics-event="submit" data-analytics-category="console" - data-analytics-label="Update Database Document" data-service="{{|documentAction}}" data-name="project-document" data-scope="sdk" data-event="submit" - data-success="trigger,redirect" - data-success-param-trigger-events="database.updateDocument" - data-success-param-redirect-url="/console/database/document?id={{serviceData.$id}}&collection={{project-collection.$id}}&project={{router.params.project}}" data-failure="alert" - data-failure-param-alert-text="Failed to update document" - data-failure-param-alert-classname="error"> + data-failure-param-alert-classname="error" + + data-analytics-label="Create Database Document" + data-success="trigger,redirect" + data-success-param-trigger-events="database.createDocument" + data-success-param-redirect-url="/console/database/collection?id={{project-collection.$id}}&project={{router.params.project}}" + data-failure-param-alert-text="Failed to create document" + + data-analytics-label="Update Database Document" + data-success="trigger,alert" + data-success-param-trigger-events="database.updateDocument" + data-success-param-alert-text="Your document was updated" + data-failure-param-alert-text="Failed to update document" + + > diff --git a/app/views/console/users/oauth/auth0.phtml b/app/views/console/users/oauth/auth0.phtml new file mode 100644 index 0000000000..8509a582b5 --- /dev/null +++ b/app/views/console/users/oauth/auth0.phtml @@ -0,0 +1,12 @@ +getParam('provider', ''); +?> + + + + + + + + + \ No newline at end of file diff --git a/app/views/console/users/oauth/okta.phtml b/app/views/console/users/oauth/okta.phtml new file mode 100644 index 0000000000..2459e1543c --- /dev/null +++ b/app/views/console/users/oauth/okta.phtml @@ -0,0 +1,14 @@ +getParam('provider', ''); +?> + + + + + + + + + + + \ No newline at end of file diff --git a/app/workers/deletes.php b/app/workers/deletes.php index ff83ce639d..b06f2ffa0e 100644 --- a/app/workers/deletes.php +++ b/app/workers/deletes.php @@ -208,17 +208,12 @@ class DeletesV1 extends Worker */ $userId = $document->getId(); - $user = $this->getProjectDB($projectId)->getDocument('users', $userId); // Delete all sessions of this user from the sessions table and update the sessions field of the user record $this->deleteByGroup('sessions', [ new Query('userId', Query::TYPE_EQUAL, [$userId]) ], $this->getProjectDB($projectId)); - $user->setAttribute('sessions', []); - - $this->getProjectDB($projectId)->updateDocument('users', $userId, $user); - // Delete Memberships and decrement team membership counts $this->deleteByGroup('memberships', [ new Query('userId', Query::TYPE_EQUAL, [$userId]) @@ -234,6 +229,11 @@ class DeletesV1 extends Worker } } }); + + // Delete tokens + $this->deleteByGroup('tokens', [ + new Query('userId', Query::TYPE_EQUAL, [$userId]) + ], $this->getProjectDB($projectId)); } /** diff --git a/app/workers/functions.php b/app/workers/functions.php index d96fd5e1bb..fb2b683c29 100644 --- a/app/workers/functions.php +++ b/app/workers/functions.php @@ -241,7 +241,7 @@ class FunctionsV1 extends Worker 'trigger' => $trigger, 'status' => 'waiting', 'statusCode' => 0, - 'stdout' => '', + 'response' => '', 'stderr' => '', 'time' => 0.0, 'search' => implode(' ', [$functionId, $executionId]), @@ -292,7 +292,7 @@ class FunctionsV1 extends Worker $execution ->setAttribute('status', $executionResponse['status']) ->setAttribute('statusCode', $executionResponse['statusCode']) - ->setAttribute('stdout', $executionResponse['stdout']) + ->setAttribute('response', $executionResponse['response']) ->setAttribute('stderr', $executionResponse['stderr']) ->setAttribute('time', $executionResponse['time']); } catch (\Throwable $th) { diff --git a/docs/sdks/python/GETTING_STARTED.md b/docs/sdks/python/GETTING_STARTED.md index 4473088b36..46a3001ab9 100644 --- a/docs/sdks/python/GETTING_STARTED.md +++ b/docs/sdks/python/GETTING_STARTED.md @@ -1,7 +1,7 @@ ## Getting Started ### Init your SDK -Initialize your SDK with your Appwrite server API endpoint and project ID which can be found in your project settings page and your new API secret Key from project's API keys section. +Initialize your SDK with your Appwrite server API endpoint and project ID which can be found on your project settings page and your new API secret Key from project's API keys section. ```python from appwrite.client import Client diff --git a/public/dist/scripts/app-all.js b/public/dist/scripts/app-all.js index 3434dd45a9..b4d100f09b 100644 --- a/public/dist/scripts/app-all.js +++ b/public/dist/scripts/app-all.js @@ -3845,8 +3845,8 @@ list["filters-"+filter.key]=params[key][i];}}}} return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');} -console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantID":"oauth2MicrosoftTenantId"},"Apple":{"keyID":"oauth2AppleKeyId","teamID":"oauth2AppleTeamId","p8":"oauth2AppleP8"}} -let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unkown")} +console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantID":"oauth2MicrosoftTenantId"},"Apple":{"keyID":"oauth2AppleKeyId","teamID":"oauth2AppleTeamId","p8":"oauth2AppleP8"},"Okta":{"clientSecret":"oauth2OktaClientSecret","oktaDomain":"oauth2OktaDomain","authorizationServerId":"oauth2OktaAuthorizationServerId"},"Auth0":{"clientSecret":"oauth2Auth0ClientSecret","auth0Domain":"oauth2Auth0Domain"}} +let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unknown")} let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}} function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}} element.value=JSON.stringify(json);} diff --git a/public/dist/scripts/app.js b/public/dist/scripts/app.js index f92091dbf8..6ac434d045 100644 --- a/public/dist/scripts/app.js +++ b/public/dist/scripts/app.js @@ -792,8 +792,8 @@ list["filters-"+filter.key]=params[key][i];}}}} return list;};let apply=function(params){let cached=container.get(name);cached=cached?cached.params:[];params=Object.assign(cached,params);container.set(name,{name:name,params:params,query:serialize(params),forward:parseInt(params.offset)+parseInt(params.limit),backward:parseInt(params.offset)-parseInt(params.limit),keys:flatten(params)},true,name);document.dispatchEvent(new CustomEvent(name+"-changed",{bubbles:false,cancelable:true}));};switch(element.tagName){case"INPUT":break;case"TEXTAREA":break;case"BUTTON":element.addEventListener("click",function(){apply(JSON.parse(expression.parse(element.dataset["params"]||"{}")));});break;case"FORM":element.addEventListener("input",function(){apply(form.toJson(element));});element.addEventListener("change",function(){apply(form.toJson(element));});element.addEventListener("reset",function(){setTimeout(function(){apply(form.toJson(element));},0);});events=events.trim().split(",");for(let y=0;y=distance)&&(distance>=0)){if(minLink){minLink.classList.remove('selected');} -console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantID":"oauth2MicrosoftTenantId"},"Apple":{"keyID":"oauth2AppleKeyId","teamID":"oauth2AppleTeamId","p8":"oauth2AppleP8"}} -let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unkown")} +console.log('old',minLink);minDistance=distance;minElement=title;minLink=links[i];minLink.classList.add('selected');console.log('new',minLink);}}};window.addEventListener('scroll',check);check();}});})(window);(function(window){"use strict";window.ls.container.get("view").add({selector:"data-forms-oauth-custom",controller:function(element){let providers={"Microsoft":{"clientSecret":"oauth2MicrosoftClientSecret","tenantID":"oauth2MicrosoftTenantId"},"Apple":{"keyID":"oauth2AppleKeyId","teamID":"oauth2AppleTeamId","p8":"oauth2AppleP8"},"Okta":{"clientSecret":"oauth2OktaClientSecret","oktaDomain":"oauth2OktaDomain","authorizationServerId":"oauth2OktaAuthorizationServerId"},"Auth0":{"clientSecret":"oauth2Auth0ClientSecret","auth0Domain":"oauth2Auth0Domain"}} +let provider=element.getAttribute("data-forms-oauth-custom");if(!provider||!providers.hasOwnProperty(provider)){console.error("Provider for custom form not set or unknown")} let config=providers[provider];element.addEventListener('change',sync);let elements={};for(const key in config){if(Object.hasOwnProperty.call(config,key)){elements[key]=document.getElementById(config[key]);elements[key].addEventListener('change',update);}} function update(){let json={};for(const key in elements){if(Object.hasOwnProperty.call(elements,key)){json[key]=elements[key].value}} element.value=JSON.stringify(json);} diff --git a/public/images/users/auth0.png b/public/images/users/auth0.png new file mode 100644 index 0000000000..d0800c6294 Binary files /dev/null and b/public/images/users/auth0.png differ diff --git a/public/images/users/okta.png b/public/images/users/okta.png new file mode 100644 index 0000000000..bdf7907f46 Binary files /dev/null and b/public/images/users/okta.png differ diff --git a/public/scripts/views/forms/oauth-custom.js b/public/scripts/views/forms/oauth-custom.js index fd8bd855d0..ca2d3b2759 100644 --- a/public/scripts/views/forms/oauth-custom.js +++ b/public/scripts/views/forms/oauth-custom.js @@ -16,10 +16,19 @@ "keyID": "oauth2AppleKeyId", "teamID": "oauth2AppleTeamId", "p8": "oauth2AppleP8" + }, + "Okta": { + "clientSecret": "oauth2OktaClientSecret", + "oktaDomain": "oauth2OktaDomain", + "authorizationServerId": "oauth2OktaAuthorizationServerId" + }, + "Auth0": { + "clientSecret": "oauth2Auth0ClientSecret", + "auth0Domain": "oauth2Auth0Domain" } } let provider = element.getAttribute("data-forms-oauth-custom"); - if (!provider || !providers.hasOwnProperty(provider)) { console.error("Provider for custom form not set or unkown") } + if (!provider || !providers.hasOwnProperty(provider)) { console.error("Provider for custom form not set or unknown") } let config = providers[provider]; // Add Change Listeners for element diff --git a/src/Appwrite/Auth/OAuth2/Auth0.php b/src/Appwrite/Auth/OAuth2/Auth0.php new file mode 100644 index 0000000000..b1c9c8ce1f --- /dev/null +++ b/src/Appwrite/Auth/OAuth2/Auth0.php @@ -0,0 +1,210 @@ +getAuth0Domain().'/authorize?'.\http_build_query([ + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'state'=> \json_encode($this->state), + 'scope'=> \implode(' ', $this->getScopes()), + 'response_type' => 'code' + ]); + } + + /** + * @param string $code + * + * @return array + */ + protected function getTokens(string $code): array + { + if(empty($this->tokens)) { + $headers = ['Content-Type: application/x-www-form-urlencoded']; + $this->tokens = \json_decode($this->request( + 'POST', + 'https://'.$this->getAuth0Domain().'/oauth/token', + $headers, + \http_build_query([ + 'code' => $code, + 'client_id' => $this->appID, + 'client_secret' => $this->getClientSecret(), + 'redirect_uri' => $this->callback, + 'scope' => \implode(' ', $this->getScopes()), + 'grant_type' => 'authorization_code' + ]) + ), true); + } + + return $this->tokens; + } + + + /** + * @param string $refreshToken + * + * @return array + */ + public function refreshTokens(string $refreshToken): array + { + $headers = ['Content-Type: application/x-www-form-urlencoded']; + $this->tokens = \json_decode($this->request( + 'POST', + 'https://'.$this->getAuth0Domain().'/oauth/token', + $headers, + \http_build_query([ + 'refresh_token' => $refreshToken, + 'client_id' => $this->appID, + 'client_secret' => $this->getClientSecret(), + 'grant_type' => 'refresh_token' + ]) + ), true); + + if(empty($this->tokens['refresh_token'])) { + $this->tokens['refresh_token'] = $refreshToken; + } + + return $this->tokens; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserID(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['sub'])) { + return $user['sub']; + } + + return ''; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserEmail(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['email'])) { + return $user['email']; + } + + return ''; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserName(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['name'])) { + return $user['name']; + } + + return ''; + } + + /** + * @param string $accessToken + * + * @return array + */ + protected function getUser(string $accessToken): array + { + if (empty($this->user)) { + $headers = ['Authorization: Bearer '. \urlencode($accessToken)]; + $user = $this->request('GET', 'https://'.$this->getAuth0Domain().'/userinfo', $headers); + $this->user = \json_decode($user, true); + } + + return $this->user; + } + + /** + * Extracts the Client Secret from the JSON stored in appSecret + * + * @return string + */ + protected function getClientSecret(): string + { + $secret = $this->getAppSecret(); + + return (isset($secret['clientSecret'])) ? $secret['clientSecret'] : ''; + } + + /** + * Extracts the Auth0 Domain from the JSON stored in appSecret + * + * @return string + */ + protected function getAuth0Domain(): string + { + $secret = $this->getAppSecret(); + return (isset($secret['auth0Domain'])) ? $secret['auth0Domain'] : ''; + } + + /** + * Decode the JSON stored in appSecret + * + * @return array + */ + protected function getAppSecret(): array + { + try { + $secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR); + } catch (\Throwable $th) { + throw new \Exception('Invalid secret'); + } + return $secret; + } +} \ No newline at end of file diff --git a/src/Appwrite/Auth/OAuth2/Okta.php b/src/Appwrite/Auth/OAuth2/Okta.php new file mode 100644 index 0000000000..7b1b0d19e1 --- /dev/null +++ b/src/Appwrite/Auth/OAuth2/Okta.php @@ -0,0 +1,221 @@ +getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/authorize?'.\http_build_query([ + 'client_id' => $this->appID, + 'redirect_uri' => $this->callback, + 'state'=> \json_encode($this->state), + 'scope'=> \implode(' ', $this->getScopes()), + 'response_type' => 'code' + ]); + } + + /** + * @param string $code + * + * @return array + */ + protected function getTokens(string $code): array + { + if(empty($this->tokens)) { + $headers = ['Content-Type: application/x-www-form-urlencoded']; + $this->tokens = \json_decode($this->request( + 'POST', + 'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/token', + $headers, + \http_build_query([ + 'code' => $code, + 'client_id' => $this->appID, + 'client_secret' => $this->getClientSecret(), + 'redirect_uri' => $this->callback, + 'scope' => \implode(' ', $this->getScopes()), + 'grant_type' => 'authorization_code' + ]) + ), true); + } + + return $this->tokens; + } + + + /** + * @param string $refreshToken + * + * @return array + */ + public function refreshTokens(string $refreshToken): array + { + $headers = ['Content-Type: application/x-www-form-urlencoded']; + $this->tokens = \json_decode($this->request( + 'POST', + 'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/token', + $headers, + \http_build_query([ + 'refresh_token' => $refreshToken, + 'client_id' => $this->appID, + 'client_secret' => $this->getClientSecret(), + 'grant_type' => 'refresh_token' + ]) + ), true); + + if(empty($this->tokens['refresh_token'])) { + $this->tokens['refresh_token'] = $refreshToken; + } + + return $this->tokens; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserID(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['sub'])) { + return $user['sub']; + } + + return ''; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserEmail(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['email'])) { + return $user['email']; + } + + return ''; + } + + /** + * @param string $accessToken + * + * @return string + */ + public function getUserName(string $accessToken): string + { + $user = $this->getUser($accessToken); + + if (isset($user['name'])) { + return $user['name']; + } + + return ''; + } + + /** + * @param string $accessToken + * + * @return array + */ + protected function getUser(string $accessToken): array + { + if (empty($this->user)) { + $headers = ['Authorization: Bearer '. \urlencode($accessToken)]; + $user = $this->request('GET', 'https://'.$this->getOktaDomain().'/oauth2/'.$this->getAuthorizationServerId().'/v1/userinfo', $headers); + $this->user = \json_decode($user, true); + } + + return $this->user; + } + + /** + * Extracts the Client Secret from the JSON stored in appSecret + * + * @return string + */ + protected function getClientSecret(): string + { + $secret = $this->getAppSecret(); + + return (isset($secret['clientSecret'])) ? $secret['clientSecret'] : ''; + } + + /** + * Extracts the Okta Domain from the JSON stored in appSecret + * + * @return string + */ + protected function getOktaDomain(): string + { + $secret = $this->getAppSecret(); + return (isset($secret['oktaDomain'])) ? $secret['oktaDomain'] : ''; + } + + /** + * Extracts the Okta Authorization Server ID from the JSON stored in appSecret + * + * @return string + */ + protected function getAuthorizationServerId(): string + { + $secret = $this->getAppSecret(); + return (isset($secret['authorizationServerId'])) ? $secret['authorizationServerId'] : 'default'; + } + + /** + * Decode the JSON stored in appSecret + * + * @return array + */ + protected function getAppSecret(): array + { + try { + $secret = \json_decode($this->appSecret, true, 512, JSON_THROW_ON_ERROR); + } catch (\Throwable $th) { + throw new \Exception('Invalid secret'); + } + return $secret; + } +} diff --git a/src/Appwrite/Event/Validator/Event.php b/src/Appwrite/Event/Validator/Event.php index d5ad85475d..ce0f3e0067 100644 --- a/src/Appwrite/Event/Validator/Event.php +++ b/src/Appwrite/Event/Validator/Event.php @@ -87,11 +87,14 @@ class Event extends Validator } if ($attribute ?? false) { - if ( - !\array_key_exists($attribute, $events[$type][$action]) || - (($subType ?? false) && !\array_key_exists($attribute, $events[$type][$subType][$action])) - ) { - return false; + if (($subType ?? false)) { + if (!\array_key_exists($attribute, $events[$type][$subType][$action])) { + return false; + } + } else { + if (!\array_key_exists($attribute, $events[$type][$action])) { + return false; + } } } diff --git a/src/Appwrite/Utopia/Response/Model/Execution.php b/src/Appwrite/Utopia/Response/Model/Execution.php index 31d32ffbb0..a8031bb0cd 100644 --- a/src/Appwrite/Utopia/Response/Model/Execution.php +++ b/src/Appwrite/Utopia/Response/Model/Execution.php @@ -53,9 +53,9 @@ class Execution extends Model 'default' => 0, 'example' => 0, ]) - ->addRule('stdout', [ + ->addRule('response', [ 'type' => self::TYPE_STRING, - 'description' => 'The script stdout output string. Logs the last 4,000 characters of the execution stdout output.', + 'description' => 'The script response output string. Logs the last 4,000 characters of the execution response output.', 'default' => '', 'example' => '', ]) diff --git a/tests/e2e/Services/Account/AccountBase.php b/tests/e2e/Services/Account/AccountBase.php index 8c9377a1b3..301ffff8ac 100644 --- a/tests/e2e/Services/Account/AccountBase.php +++ b/tests/e2e/Services/Account/AccountBase.php @@ -445,7 +445,7 @@ trait AccountBase { $email = $data['email'] ?? ''; $session = $data['session'] ?? ''; - $newName = 'New Name'; + $newName = 'Lorem'; /** * Test for SUCCESS @@ -477,7 +477,7 @@ trait AccountBase ])); $this->assertEquals($response['headers']['status-code'], 401); - + $response = $this->client->call(Client::METHOD_PATCH, '/account/name', array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', @@ -485,7 +485,7 @@ trait AccountBase 'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session, ]), [ ]); - + $this->assertEquals($response['headers']['status-code'], 400); $response = $this->client->call(Client::METHOD_PATCH, '/account/name', array_merge([ @@ -496,7 +496,7 @@ trait AccountBase ]), [ 'name' => 'ocSRq1d3QphHivJyUmYY7WMnrxyjdk5YvVwcDqx2zS0coxESN8RmsQwLWw5Whnf0WbVohuFWTRAaoKgCOO0Y0M7LwgFnZmi8881Y72222222222222222222222222222' ]); - + $this->assertEquals($response['headers']['status-code'], 400); $data['name'] = $newName; @@ -532,7 +532,6 @@ trait AccountBase $this->assertNotEmpty($response['body']['$id']); $this->assertIsNumeric($response['body']['registration']); $this->assertEquals($response['body']['email'], $email); - $this->assertEquals($response['body']['name'], 'New Name'); $response = $this->client->call(Client::METHOD_POST, '/account/sessions', array_merge([ 'origin' => 'http://localhost', @@ -625,7 +624,6 @@ trait AccountBase $this->assertNotEmpty($response['body']['$id']); $this->assertIsNumeric($response['body']['registration']); $this->assertEquals($response['body']['email'], $newEmail); - $this->assertEquals($response['body']['name'], 'New Name'); /** * Test for FAILURE @@ -637,7 +635,7 @@ trait AccountBase ])); $this->assertEquals($response['headers']['status-code'], 401); - + $response = $this->client->call(Client::METHOD_PATCH, '/account/email', array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', @@ -645,7 +643,7 @@ trait AccountBase 'cookie' => 'a_session_'.$this->getProject()['$id'].'=' . $session, ]), [ ]); - + $this->assertEquals($response['headers']['status-code'], 400); // Test if we can create a new account with the old email diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 2b06828a6e..30a01b31b9 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -510,4 +510,85 @@ class AccountCustomClientTest extends Scope $this->assertEquals($response['headers']['status-code'], 404); } -} \ No newline at end of file + + /** + * @depends testUpdateAccountName + */ + public function testUpdateAccountNameSearch($data): void + { + $id = $data['id'] ?? ''; + $email = $data['email'] ?? ''; + $newName = 'Lorem'; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/users', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'search' => $newName + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['email'], $email); + + $response = $this->client->call(Client::METHOD_GET, '/users', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'search' => $id + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['email'], $email); + } + + /** + * @depends testUpdateAccountEmail + */ + public function testUpdateAccountEmailSearch($data): void + { + $id = $data['id'] ?? ''; + $email = $data['email'] ?? ''; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/users', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'search' => '"' . $email . '"' + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['email'], $email); + + $response = $this->client->call(Client::METHOD_GET, '/users', [ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ], [ + 'search' => $id + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['email'], $email); + } +} diff --git a/tests/e2e/Services/Account/AccountCustomServerTest.php b/tests/e2e/Services/Account/AccountCustomServerTest.php index fabbc5b77f..832dd34389 100644 --- a/tests/e2e/Services/Account/AccountCustomServerTest.php +++ b/tests/e2e/Services/Account/AccountCustomServerTest.php @@ -33,7 +33,7 @@ class AccountCustomServerTest extends Scope ]); $this->assertEquals(401, $response['headers']['status-code']); - + return []; } } \ No newline at end of file diff --git a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php index 10645a25ca..8927302819 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomClientTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomClientTest.php @@ -208,7 +208,7 @@ class FunctionsCustomClientTest extends Scope 'x-appwrite-key' => $apikey, ]); - $output = json_decode($executions['body']['stdout'], true); + $output = json_decode($executions['body']['response'], true); $this->assertEquals(200, $executions['headers']['status-code']); $this->assertEquals('completed', $executions['body']['status']); $this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']); @@ -383,7 +383,7 @@ class FunctionsCustomClientTest extends Scope 'async' => false ]); - $output = json_decode($execution['body']['stdout'], true); + $output = json_decode($execution['body']['response'], true); $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']); diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 263da52aa1..a766b269ed 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -491,7 +491,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($data['functionId'], $execution['body']['functionId']); $this->assertEquals('waiting', $execution['body']['status']); $this->assertEquals(0, $execution['body']['statusCode']); - $this->assertEquals('', $execution['body']['stdout']); + $this->assertEquals('', $execution['body']['response']); $this->assertEquals('', $execution['body']['stderr']); $this->assertEquals(0, $execution['body']['time']); @@ -508,13 +508,13 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($data['functionId'], $execution['body']['functionId']); $this->assertEquals('completed', $execution['body']['status']); $this->assertEquals(200, $execution['body']['statusCode']); - $this->assertStringContainsString($execution['body']['functionId'], $execution['body']['stdout']); - $this->assertStringContainsString($data['deploymentId'], $execution['body']['stdout']); - $this->assertStringContainsString('Test1', $execution['body']['stdout']); - $this->assertStringContainsString('http', $execution['body']['stdout']); - $this->assertStringContainsString('PHP', $execution['body']['stdout']); - $this->assertStringContainsString('8.0', $execution['body']['stdout']); - // $this->assertStringContainsString('êä', $execution['body']['stdout']); // tests unknown utf-8 chars + $this->assertStringContainsString($execution['body']['functionId'], $execution['body']['response']); + $this->assertStringContainsString($data['deploymentId'], $execution['body']['response']); + $this->assertStringContainsString('Test1', $execution['body']['response']); + $this->assertStringContainsString('http', $execution['body']['response']); + $this->assertStringContainsString('PHP', $execution['body']['response']); + $this->assertStringContainsString('8.0', $execution['body']['response']); + // $this->assertStringContainsString('êä', $execution['body']['response']); // tests unknown utf-8 chars $this->assertEquals('', $execution['body']['stderr']); $this->assertLessThan(0.500, $execution['body']['time']); @@ -597,11 +597,11 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals(201, $execution['headers']['status-code']); $this->assertEquals('completed', $execution['body']['status']); - $this->assertStringContainsString($data['deploymentId'], $execution['body']['stdout']); - $this->assertStringContainsString('Test1', $execution['body']['stdout']); - $this->assertStringContainsString('http', $execution['body']['stdout']); - $this->assertStringContainsString('PHP', $execution['body']['stdout']); - $this->assertStringContainsString('8.0', $execution['body']['stdout']); + $this->assertStringContainsString($data['deploymentId'], $execution['body']['response']); + $this->assertStringContainsString('Test1', $execution['body']['response']); + $this->assertStringContainsString('http', $execution['body']['response']); + $this->assertStringContainsString('PHP', $execution['body']['response']); + $this->assertStringContainsString('8.0', $execution['body']['response']); // $this->assertStringContainsString('êä', $execution['body']['sdtout']); // tests unknown utf-8 chars $this->assertLessThan(0.500, $execution['body']['time']); @@ -765,7 +765,7 @@ class FunctionsCustomServerTest extends Scope $this->assertEquals($executions['body']['executions'][0]['statusCode'], 124); $this->assertGreaterThan(2, $executions['body']['executions'][0]['time']); $this->assertLessThan(3, $executions['body']['executions'][0]['time']); - $this->assertEquals($executions['body']['executions'][0]['stdout'], ''); + $this->assertEquals($executions['body']['executions'][0]['response'], ''); $this->assertEquals($executions['body']['executions'][0]['stderr'], 'Execution timed out.'); // Cleanup : Delete function @@ -848,7 +848,7 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $output = json_decode($executions['body']['stdout'], true); + $output = json_decode($executions['body']['response'], true); $this->assertEquals(200, $executions['headers']['status-code']); $this->assertEquals('completed', $executions['body']['status']); @@ -876,7 +876,7 @@ class FunctionsCustomServerTest extends Scope $this->assertCount(1, $executions['body']['executions']); $this->assertEquals($executions['body']['executions'][0]['$id'], $executionId); $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); - $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']); + $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['response']); // Cleanup : Delete function $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ @@ -953,7 +953,7 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $output = json_decode($executions['body']['stdout'], true); + $output = json_decode($executions['body']['response'], true); $this->assertEquals(200, $executions['headers']['status-code']); $this->assertEquals('completed', $executions['body']['status']); @@ -982,7 +982,7 @@ class FunctionsCustomServerTest extends Scope $this->assertCount(1, $executions['body']['executions']); $this->assertEquals($executions['body']['executions'][0]['$id'], $executionId); $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); - $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']); + $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['response']); // Cleanup : Delete function $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ @@ -1058,7 +1058,7 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $output = json_decode($executions['body']['stdout'], true); + $output = json_decode($executions['body']['response'], true); $this->assertEquals(200, $executions['headers']['status-code']); $this->assertEquals('completed', $executions['body']['status']); @@ -1087,7 +1087,7 @@ class FunctionsCustomServerTest extends Scope $this->assertCount(1, $executions['body']['executions']); $this->assertEquals($executions['body']['executions'][0]['$id'], $executionId); $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); - $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']); + $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['response']); // Cleanup : Delete function $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ @@ -1163,7 +1163,7 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $output = json_decode($executions['body']['stdout'], true); + $output = json_decode($executions['body']['response'], true); $this->assertEquals(200, $executions['headers']['status-code']); $this->assertEquals('completed', $executions['body']['status']); @@ -1192,7 +1192,7 @@ class FunctionsCustomServerTest extends Scope $this->assertCount(1, $executions['body']['executions']); $this->assertEquals($executions['body']['executions'][0]['$id'], $executionId); $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); - $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']); + $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['response']); // Cleanup : Delete function $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ @@ -1268,7 +1268,7 @@ class FunctionsCustomServerTest extends Scope 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders())); - $output = json_decode($executions['body']['stdout'], true); + $output = json_decode($executions['body']['response'], true); $this->assertEquals(200, $executions['headers']['status-code']); $this->assertEquals('completed', $executions['body']['status']); @@ -1297,7 +1297,7 @@ class FunctionsCustomServerTest extends Scope $this->assertCount(1, $executions['body']['executions']); $this->assertEquals($executions['body']['executions'][0]['$id'], $executionId); $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); - $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']); + $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['response']); // Cleanup : Delete function $response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ @@ -1373,7 +1373,7 @@ class FunctionsCustomServerTest extends Scope // 'x-appwrite-project' => $this->getProject()['$id'], // ], $this->getHeaders())); - // $output = json_decode($executions['body']['stdout'], true); + // $output = json_decode($executions['body']['response'], true); // $this->assertEquals(200, $executions['headers']['status-code']); // $this->assertEquals('completed', $executions['body']['status']); @@ -1402,7 +1402,7 @@ class FunctionsCustomServerTest extends Scope // $this->assertCount(1, $executions['body']['executions']); // $this->assertEquals($executions['body']['executions'][0]['$id'], $executionId); // $this->assertEquals($executions['body']['executions'][0]['trigger'], 'http'); - // $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']); + // $this->assertStringContainsString('foobar', $executions['body']['executions'][0]['response']); // // Cleanup : Delete function // $response = $this->client->call(Client::METHOD_DELETE, '/functions/'. $functionId, [ diff --git a/tests/e2e/Services/Users/UsersBase.php b/tests/e2e/Services/Users/UsersBase.php index 2e61917d2d..1f5f2922d8 100644 --- a/tests/e2e/Services/Users/UsersBase.php +++ b/tests/e2e/Services/Users/UsersBase.php @@ -281,6 +281,44 @@ trait UsersBase return $data; } + /** + * @depends testUpdateUserName + */ + public function testUpdateUserNameSearch($data): void + { + $id = $data['userId'] ?? ''; + $newName = 'Updated name'; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $newName + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $id); + + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $id + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $id); + } + /** * @depends testGetUser */ @@ -310,6 +348,44 @@ trait UsersBase return $data; } + /** + * @depends testUpdateUserEmail + */ + public function testUpdateUserEmailSearch($data): void + { + $id = $data['userId'] ?? ''; + $newEmail = '"users.service@updated.com"'; + + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $newEmail + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $id); + + $response = $this->client->call(Client::METHOD_GET, '/users', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'search' => $id + ]); + + $this->assertEquals($response['headers']['status-code'], 200); + $this->assertNotEmpty($response['body']); + $this->assertNotEmpty($response['body']['users']); + $this->assertCount(1, $response['body']['users']); + $this->assertEquals($response['body']['users'][0]['$id'], $id); + } + /** * @depends testUpdateUserEmail */ diff --git a/tests/unit/Event/Validator/EventValidatorTest.php b/tests/unit/Event/Validator/EventValidatorTest.php index f0886ae5a7..e523999599 100644 --- a/tests/unit/Event/Validator/EventValidatorTest.php +++ b/tests/unit/Event/Validator/EventValidatorTest.php @@ -44,6 +44,7 @@ class EventValidatorTest extends TestCase $this->assertTrue($this->object->isValid('buckets.*')); $this->assertTrue($this->object->isValid('teams.*')); $this->assertTrue($this->object->isValid('users.*')); + $this->assertTrue($this->object->isValid('teams.*.memberships.*.update.status')); /** * Test for FAILURE @@ -57,5 +58,6 @@ class EventValidatorTest extends TestCase $this->assertFalse($this->object->isValid('collections.*.documents.*.unknown')); $this->assertFalse($this->object->isValid('users.torsten.unknown')); $this->assertFalse($this->object->isValid('users.torsten.delete.email')); + $this->assertFalse($this->object->isValid('teams.*.memberships.*.update.unknown')); } }