diff --git a/.env b/.env index f89386b5cf..09abb07be2 100644 --- a/.env +++ b/.env @@ -79,7 +79,7 @@ _APP_MAINTENANCE_RETENTION_CACHE=2592000 _APP_MAINTENANCE_RETENTION_EXECUTION=1209600 _APP_MAINTENANCE_RETENTION_ABUSE=86400 _APP_MAINTENANCE_RETENTION_AUDIT=1209600 -_APP_USAGE_AGGREGATION_INTERVAL=60000 +_APP_USAGE_AGGREGATION_INTERVAL=30 _APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000 _APP_MAINTENANCE_RETENTION_SCHEDULES=86400 _APP_USAGE_STATS=enabled diff --git a/Dockerfile b/Dockerfile index 1075f9a12a..9d8e26b299 100755 --- a/Dockerfile +++ b/Dockerfile @@ -105,7 +105,8 @@ RUN chmod +x /usr/local/bin/doctor && \ chmod +x /usr/local/bin/worker-migrations && \ chmod +x /usr/local/bin/worker-webhooks && \ chmod +x /usr/local/bin/worker-hamster && \ - chmod +x /usr/local/bin/worker-usage + chmod +x /usr/local/bin/worker-usage && \ + chmod +x /usr/local/bin/worker-usage-dump # Cloud Executabless diff --git a/app/config/collections.php b/app/config/collections.php index e8a24c910e..1ab9c42fa2 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -34,6 +34,28 @@ $commonCollections = [ 'array' => false, 'filters' => [], ], + [ + '$id' => 'resourceType', + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('mimeType'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, // https://tools.ietf.org/html/rfc4288#section-4.2 + 'signed' => true, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => [], + ], [ '$id' => 'accessedAt', 'type' => Database::VAR_DATETIME, @@ -2113,6 +2135,13 @@ $commonCollections = [ 'lengths' => [], 'orders' => [], ], + [ + '$id' => ID::custom('_unique_target_topic'), + 'type' => Database::INDEX_UNIQUE, + 'attributes' => ['targetInternalId', 'topicInternalId'], + 'lengths' => [], + 'orders' => [], + ], [ '$id' => ID::custom('_fulltext_search'), 'type' => Database::INDEX_FULLTEXT, @@ -4362,17 +4391,6 @@ $consoleCollections = array_merge([ 'array' => false, 'filters' => [], ], - [ - '$id' => ID::custom('resourceCollection'), - 'type' => Database::VAR_STRING, - 'format' => '', - 'size' => Database::LENGTH_KEY, - 'signed' => true, - 'required' => true, - 'default' => null, - 'array' => false, - 'filters' => [], - ], [ '$id' => ID::custom('resourceInternalId'), 'type' => Database::VAR_STRING, diff --git a/app/config/errors.php b/app/config/errors.php index c0efc2097d..67182c7e6e 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -272,6 +272,11 @@ return [ 'description' => 'User phone is already verified', 'code' => 409 ], + Exception::USER_DELETION_PROHIBITED => [ + 'name' => Exception::USER_DELETION_PROHIBITED, + 'description' => 'User deletion is not allowed for users with active memberships. Please delete all confirmed memberships before deleting the account.', + 'code' => 400 + ], Exception::USER_TARGET_NOT_FOUND => [ 'name' => Exception::USER_TARGET_NOT_FOUND, 'description' => 'The target could not be found.', @@ -420,6 +425,11 @@ return [ 'description' => 'The value for x-appwrite-id header is invalid. Please check the value of the x-appwrite-id header is a valid id and not unique().', 'code' => 400, ], + Exception::STORAGE_FILE_NOT_PUBLIC => [ + 'name' => Exception::STORAGE_FILE_NOT_PUBLIC, + 'description' => 'The requested file is not publicly readable.', + 'code' => 403, + ], /** VCS */ Exception::INSTALLATION_NOT_FOUND => [ diff --git a/app/config/platforms.php b/app/config/platforms.php index 83d777c8ac..26513fbc39 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -303,7 +303,7 @@ return [ [ 'key' => 'ruby', 'name' => 'Ruby', - 'version' => '10.1.0', + 'version' => '10.1.1', 'url' => 'https://github.com/appwrite/sdk-for-ruby', 'package' => 'https://rubygems.org/gems/appwrite', 'enabled' => true, diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index dd009864a7..4b3948be75 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -266,7 +266,7 @@ App::post('/v1/account/sessions/email') 'secret' => Auth::hash($secret), // One way hash encryption to protect DB leak 'userAgent' => $request->getUserAgent('UNKNOWN'), 'ip' => $request->getIP(), - 'factors' => ['email'], + 'factors' => ['password'], 'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--', 'expire' => DateTime::addSeconds(new \DateTime(), $duration) ], @@ -1773,10 +1773,10 @@ App::post('/v1/account/tokens/phone') ]); $queueForMessaging + ->setType(MESSAGE_SEND_TYPE_INTERNAL) ->setMessage($messageDoc) ->setRecipients([$phone]) - ->setProviderType(MESSAGE_TYPE_SMS) - ->trigger(); + ->setProviderType(MESSAGE_TYPE_SMS); $queueForEvents->setPayload( $response->output( @@ -2124,7 +2124,7 @@ App::get('/v1/account/logs') } $response->dynamic(new Document([ - 'total' => $audit->countLogsByUser($user->getId()), + 'total' => $audit->countLogsByUser($user->getInternalId()), 'logs' => $output, ]), Response::MODEL_LOG_LIST); }); @@ -3314,10 +3314,10 @@ App::post('/v1/account/verification/phone') ]); $queueForMessaging + ->setType(MESSAGE_SEND_TYPE_INTERNAL) ->setMessage($messageDoc) ->setRecipients([$user->getAttribute('phone')]) - ->setProviderType(MESSAGE_TYPE_SMS) - ->trigger(); + ->setProviderType(MESSAGE_TYPE_SMS); $queueForEvents ->setParam('userId', $user->getId()) @@ -3677,14 +3677,14 @@ App::post('/v1/account/mfa/challenge') } $queueForMessaging + ->setType(MESSAGE_SEND_TYPE_INTERNAL) ->setMessage(new Document([ '$id' => $challenge->getId(), 'data' => [ 'content' => $code, ], ])) - ->setRecipients([$user->getAttribute('phone')]) - ->trigger(); + ->setRecipients([$user->getAttribute('phone')]); break; case 'email': if (empty(App::getEnv('_APP_SMTP_HOST'))) { @@ -3791,15 +3791,27 @@ App::delete('/v1/account') ->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT) ->label('sdk.response.model', Response::MODEL_NONE) ->inject('user') + ->inject('project') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') ->inject('queueForDeletes') - ->action(function (Document $user, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes) { + ->action(function (Document $user, Document $project, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes) { if ($user->isEmpty()) { throw new Exception(Exception::USER_NOT_FOUND); } + if ($project->getId() === 'console') { + // get all memberships + $memberships = $user->getAttribute('memberships', []); + foreach ($memberships as $membership) { + // prevent deletion if at least one active membership + if ($membership->getAttribute('confirm', false)) { + throw new Exception(Exception::USER_DELETION_PROHIBITED); + } + } + } + $dbForProject->deleteDocument('users', $user->getId()); $queueForDeletes diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php index ffcfb8f10a..fa4b206373 100644 --- a/app/controllers/api/avatars.php +++ b/app/controllers/api/avatars.php @@ -76,7 +76,7 @@ $getUserGitHub = function (string $userId, Document $project, Database $dbForPro } if (empty($gitHubSession)) { - throw new Exception(Exception::GENERAL_UNKNOWN, 'GitHub session not found.'); + throw new Exception(Exception::USER_SESSION_NOT_FOUND, 'GitHub session not found.'); } $provider = $gitHubSession->getAttribute('provider', ''); diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index 66b995adae..8c4eb8b09a 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -121,9 +121,7 @@ $redeployVcs = function (Request $request, Document $function, Document $project ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) ->setDeployment($deployment) - ->setTemplate($template) - ->setProject($project) - ->trigger(); + ->setTemplate($template); }; App::post('/v1/functions') @@ -235,7 +233,6 @@ App::post('/v1/functions') fn () => $dbForConsole->createDocument('schedules', new Document([ 'region' => App::getEnv('_APP_REGION', 'default'), // Todo replace with projects region 'resourceType' => 'function', - 'resourceCollection' => 'functions', 'resourceId' => $function->getId(), 'resourceInternalId' => $function->getInternalId(), 'resourceUpdatedAt' => DateTime::now(), @@ -847,8 +844,8 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download') ->inject('response') ->inject('request') ->inject('dbForProject') - ->inject('deviceFunctions') - ->action(function (string $functionId, string $deploymentId, Response $response, Request $request, Database $dbForProject, Device $deviceFunctions) { + ->inject('deviceForFunctions') + ->action(function (string $functionId, string $deploymentId, Response $response, Request $request, Database $dbForProject, Device $deviceForFunctions) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -865,7 +862,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download') } $path = $deployment->getAttribute('path', ''); - if (!$deviceFunctions->exists($path)) { + if (!$deviceForFunctions->exists($path)) { throw new Exception(Exception::DEPLOYMENT_NOT_FOUND); } @@ -875,7 +872,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download') ->addHeader('X-Peak', \memory_get_peak_usage()) ->addHeader('Content-Disposition', 'attachment; filename="' . $deploymentId . '.tar.gz"'); - $size = $deviceFunctions->getFileSize($path); + $size = $deviceForFunctions->getFileSize($path); $rangeHeader = $request->getHeader('range'); if (!empty($rangeHeader)) { @@ -897,13 +894,13 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download') ->addHeader('Content-Length', $end - $start + 1) ->setStatusCode(Response::STATUS_CODE_PARTIALCONTENT); - $response->send($deviceFunctions->read($path, $start, ($end - $start + 1))); + $response->send($deviceForFunctions->read($path, $start, ($end - $start + 1))); } if ($size > APP_STORAGE_READ_BUFFER) { for ($i = 0; $i < ceil($size / MAX_OUTPUT_CHUNK_SIZE); $i++) { $response->chunk( - $deviceFunctions->read( + $deviceForFunctions->read( $path, ($i * MAX_OUTPUT_CHUNK_SIZE), min(MAX_OUTPUT_CHUNK_SIZE, $size - ($i * MAX_OUTPUT_CHUNK_SIZE)) @@ -912,7 +909,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download') ); } } else { - $response->send($deviceFunctions->read($path)); + $response->send($deviceForFunctions->read($path)); } }); @@ -1051,10 +1048,10 @@ App::post('/v1/functions/:functionId/deployments') ->inject('dbForProject') ->inject('queueForEvents') ->inject('project') - ->inject('deviceFunctions') - ->inject('deviceLocal') + ->inject('deviceForFunctions') + ->inject('deviceForLocal') ->inject('queueForBuilds') - ->action(function (string $functionId, ?string $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceFunctions, Device $deviceLocal, Build $queueForBuilds) { + ->action(function (string $functionId, ?string $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceForFunctions, Device $deviceForLocal, Build $queueForBuilds) { $activate = filter_var($activate, FILTER_VALIDATE_BOOLEAN); @@ -1135,11 +1132,11 @@ App::post('/v1/functions/:functionId/deployments') } // Save to storage - $fileSize ??= $deviceLocal->getFileSize($fileTmpName); - $path = $deviceFunctions->getPath($deploymentId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION)); + $fileSize ??= $deviceForLocal->getFileSize($fileTmpName); + $path = $deviceForFunctions->getPath($deploymentId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION)); $deployment = $dbForProject->getDocument('deployments', $deploymentId); - $metadata = ['content_type' => $deviceLocal->getFileMimeType($fileTmpName)]; + $metadata = ['content_type' => $deviceForLocal->getFileMimeType($fileTmpName)]; if (!$deployment->isEmpty()) { $chunks = $deployment->getAttribute('chunksTotal', 1); $metadata = $deployment->getAttribute('metadata', []); @@ -1148,7 +1145,7 @@ App::post('/v1/functions/:functionId/deployments') } } - $chunksUploaded = $deviceFunctions->upload($fileTmpName, $path, $chunk, $chunks, $metadata); + $chunksUploaded = $deviceForFunctions->upload($fileTmpName, $path, $chunk, $chunks, $metadata); if (empty($chunksUploaded)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed moving file'); @@ -1171,7 +1168,7 @@ App::post('/v1/functions/:functionId/deployments') } } - $fileSize = $deviceFunctions->getFileSize($path); + $fileSize = $deviceForFunctions->getFileSize($path); if ($deployment->isEmpty()) { $deployment = $dbForProject->createDocument('deployments', new Document([ @@ -1202,9 +1199,7 @@ App::post('/v1/functions/:functionId/deployments') $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) - ->setDeployment($deployment) - ->setProject($project) - ->trigger(); + ->setDeployment($deployment); } else { if ($deployment->isEmpty()) { $deployment = $dbForProject->createDocument('deployments', new Document([ @@ -1382,8 +1377,8 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') ->inject('dbForProject') ->inject('queueForDeletes') ->inject('queueForEvents') - ->inject('deviceFunctions') - ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Device $deviceFunctions) { + ->inject('deviceForFunctions') + ->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Device $deviceForFunctions) { $function = $dbForProject->getDocument('functions', $functionId); if ($function->isEmpty()) { @@ -1404,7 +1399,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId') } if (!empty($deployment->getAttribute('path', ''))) { - if (!($deviceFunctions->delete($deployment->getAttribute('path', '')))) { + if (!($deviceForFunctions->delete($deployment->getAttribute('path', '')))) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove deployment from storage'); } } @@ -1484,9 +1479,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId') $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) - ->setDeployment($deployment) - ->setProject($project) - ->trigger(); + ->setDeployment($deployment); $queueForEvents ->setParam('functionId', $function->getId()) diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 4e0b094926..3ea96c9744 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -792,8 +792,8 @@ App::get('/v1/health/stats') // Currently only used internally ->label('docs', false) ->inject('response') ->inject('register') - ->inject('deviceFiles') - ->action(function (Response $response, Registry $register, Device $deviceFiles) { + ->inject('deviceForFiles') + ->action(function (Response $response, Registry $register, Device $deviceForFiles) { $cache = $register->get('cache'); @@ -802,9 +802,9 @@ App::get('/v1/health/stats') // Currently only used internally $response ->json([ 'storage' => [ - 'used' => Storage::human($deviceFiles->getDirectorySize($deviceFiles->getRoot() . '/')), - 'partitionTotal' => Storage::human($deviceFiles->getPartitionTotalSpace()), - 'partitionFree' => Storage::human($deviceFiles->getPartitionFreeSpace()), + 'used' => Storage::human($deviceForFiles->getDirectorySize($deviceForFiles->getRoot() . '/')), + 'partitionTotal' => Storage::human($deviceForFiles->getPartitionTotalSpace()), + 'partitionFree' => Storage::human($deviceForFiles->getPartitionFreeSpace()), ], 'cache' => [ 'uptime' => $cacheStats['uptime_in_seconds'] ?? 0, diff --git a/app/controllers/api/messaging.php b/app/controllers/api/messaging.php index 8e6c73f3bc..1c30e07ba5 100644 --- a/app/controllers/api/messaging.php +++ b/app/controllers/api/messaging.php @@ -10,6 +10,7 @@ use Appwrite\Messaging\Status as MessageStatus; use Appwrite\Network\Validator\Email; use Appwrite\Permission; use Appwrite\Role; +use Appwrite\Utopia\Database\Validator\CompoundUID; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Database\Validator\Queries\Messages; use Appwrite\Utopia\Database\Validator\Queries\Providers; @@ -27,11 +28,13 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\Authorization; use Utopia\Database\Validator\Datetime as DatetimeValidator; +use Utopia\Database\Validator\Key; use Utopia\Database\Validator\Queries; use Utopia\Database\Validator\Query\Limit; use Utopia\Database\Validator\Query\Offset; use Utopia\Database\Validator\Roles; use Utopia\Database\Validator\UID; +use Utopia\Domains\Domain; use Utopia\Locale\Locale; use Utopia\Validator\ArrayList; use Utopia\Validator\Boolean; @@ -216,14 +219,14 @@ App::post('/v1/messaging/providers/smtp') ->label('scope', 'providers.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') - ->label('sdk.method', 'createSMTPProvider') + ->label('sdk.method', 'createSmtpProvider') ->label('sdk.description', '/docs/references/messaging/create-smtp-provider.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PROVIDER) ->param('providerId', '', new CustomId(), 'Provider ID. Choose a custom ID or generate a random ID with `ID.unique()`. 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), 'Provider name.') - ->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host by using this format: [hostname:port] (e.g. "smtp1.example.com:25;smtp2.example.com"). You can also specify encryption type, for example: (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). Hosts will be tried in order.') + ->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host such as `smtp1.example.com:25;smtp2.example.com`. You can also specify encryption type, for example: `tls://smtp1.example.com:587;ssl://smtp2.example.com:465"`. Hosts will be tried in order.') ->param('port', 587, new Range(1, 65535), 'The default SMTP server port.', true) ->param('username', '', new Text(0), 'Authentication username.', true) ->param('password', '', new Text(0), 'Authentication password.', true) @@ -689,7 +692,7 @@ App::post('/v1/messaging/providers/fcm') ->label('scope', 'providers.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') - ->label('sdk.method', 'createFCMProvider') + ->label('sdk.method', 'createFcmProvider') ->label('sdk.description', '/docs/references/messaging/create-fcm-provider.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) @@ -752,7 +755,7 @@ App::post('/v1/messaging/providers/apns') ->label('scope', 'providers.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') - ->label('sdk.method', 'createAPNSProvider') + ->label('sdk.method', 'createApnsProvider') ->label('sdk.description', '/docs/references/messaging/create-apns-provider.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) @@ -881,7 +884,7 @@ App::get('/v1/messaging/providers/:providerId/logs') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') ->label('sdk.method', 'listProviderLogs') - ->label('sdk.description', '/docs/references/messaging/providers/get-logs.md') + ->label('sdk.description', '/docs/references/messaging/list-provider-logs.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_LOG_LIST) @@ -1192,14 +1195,14 @@ App::patch('/v1/messaging/providers/smtp/:providerId') ->label('scope', 'providers.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') - ->label('sdk.method', 'updateSMTPProvider') + ->label('sdk.method', 'updateSmtpProvider') ->label('sdk.description', '/docs/references/messaging/update-smtp-provider.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_PROVIDER) ->param('providerId', '', new UID(), 'Provider ID.') ->param('name', '', new Text(128), 'Provider name.', true) - ->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host by using this format: [hostname:port] (e.g. "smtp1.example.com:25;smtp2.example.com"). You can also specify encryption type, for example: (e.g. "tls://smtp1.example.com:587;ssl://smtp2.example.com:465"). Hosts will be tried in order.', true) + ->param('host', '', new Text(0), 'SMTP hosts. Either a single hostname or multiple semicolon-delimited hostnames. You can also specify a different port for each host such as `smtp1.example.com:25;smtp2.example.com`. You can also specify encryption type, for example: `tls://smtp1.example.com:587;ssl://smtp2.example.com:465"`. Hosts will be tried in order.', true) ->param('port', null, new Range(1, 65535), 'SMTP port.', true) ->param('username', '', new Text(0), 'Authentication username.', true) ->param('password', '', new Text(0), 'Authentication password.', true) @@ -1724,7 +1727,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId') ->label('scope', 'providers.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') - ->label('sdk.method', 'updateFCMProvider') + ->label('sdk.method', 'updateFcmProvider') ->label('sdk.description', '/docs/references/messaging/update-fcm-provider.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) @@ -1793,7 +1796,7 @@ App::patch('/v1/messaging/providers/apns/:providerId') ->label('scope', 'providers.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') - ->label('sdk.method', 'updateAPNSProvider') + ->label('sdk.method', 'updateApnsProvider') ->label('sdk.description', '/docs/references/messaging/update-apns-provider.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) @@ -1906,7 +1909,7 @@ App::delete('/v1/messaging/providers/:providerId') }); App::post('/v1/messaging/topics') - ->desc('Create a topic.') + ->desc('Create a topic') ->groups(['api', 'messaging']) ->label('audits.event', 'topic.create') ->label('audits.resource', 'topic/{response.$id}') @@ -1949,7 +1952,7 @@ App::post('/v1/messaging/topics') }); App::get('/v1/messaging/topics') - ->desc('List topics.') + ->desc('List topics') ->groups(['api', 'messaging']) ->label('scope', 'topics.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) @@ -2006,7 +2009,7 @@ App::get('/v1/messaging/topics/:topicId/logs') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') ->label('sdk.method', 'listTopicLogs') - ->label('sdk.description', '/docs/references/messaging/topics/get-logs.md') + ->label('sdk.description', '/docs/references/messaging/list-topic-logs.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_LOG_LIST) @@ -2089,7 +2092,7 @@ App::get('/v1/messaging/topics/:topicId/logs') }); App::get('/v1/messaging/topics/:topicId') - ->desc('Get a topic.') + ->desc('Get a topic') ->groups(['api', 'messaging']) ->label('scope', 'topics.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) @@ -2116,7 +2119,7 @@ App::get('/v1/messaging/topics/:topicId') }); App::patch('/v1/messaging/topics/:topicId') - ->desc('Update a topic.') + ->desc('Update a topic') ->groups(['api', 'messaging']) ->label('audits.event', 'topic.update') ->label('audits.resource', 'topic/{response.$id}') @@ -2155,7 +2158,7 @@ App::patch('/v1/messaging/topics/:topicId') }); App::delete('/v1/messaging/topics/:topicId') - ->desc('Delete a topic.') + ->desc('Delete a topic') ->groups(['api', 'messaging']) ->label('audits.event', 'topic.delete') ->label('audits.resource', 'topic/{request.$topicId}') @@ -2195,7 +2198,7 @@ App::delete('/v1/messaging/topics/:topicId') }); App::post('/v1/messaging/topics/:topicId/subscribers') - ->desc('Create a subscriber.') + ->desc('Create a subscriber') ->groups(['api', 'messaging']) ->label('audits.event', 'subscriber.create') ->label('audits.resource', 'subscriber/{response.$id}') @@ -2291,7 +2294,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers') }); App::get('/v1/messaging/topics/:topicId/subscribers') - ->desc('List subscribers.') + ->desc('List subscribers') ->groups(['api', 'messaging']) ->label('scope', 'subscribers.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) @@ -2371,7 +2374,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') ->label('sdk.method', 'listSubscriberLogs') - ->label('sdk.description', '/docs/references/messaging/subscribers/get-logs.md') + ->label('sdk.description', '/docs/references/messaging/list-subscriber-logs.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_LOG_LIST) @@ -2454,7 +2457,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs') }); App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') - ->desc('Get a subscriber.') + ->desc('Get a subscriber') ->groups(['api', 'messaging']) ->label('scope', 'subscribers.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) @@ -2493,7 +2496,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId') }); App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') - ->desc('Delete a subscriber.') + ->desc('Delete a subscriber') ->groups(['api', 'messaging']) ->label('audits.event', 'subscriber.delete') ->label('audits.resource', 'subscriber/{request.$subscriberId}') @@ -2552,7 +2555,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId') }); App::post('/v1/messaging/messages/email') - ->desc('Create an email.') + ->desc('Create an email') ->groups(['api', 'messaging']) ->label('audits.event', 'message.create') ->label('audits.resource', 'message/{response.$id}') @@ -2573,6 +2576,7 @@ App::post('/v1/messaging/messages/email') ->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true) ->param('cc', [], new ArrayList(new UID()), 'Array of target IDs to be added as CC.', true) ->param('bcc', [], new ArrayList(new UID()), 'Array of target IDs to be added as BCC.', true) + ->param('attachments', [], new ArrayList(new CompoundUID()), 'Array of compound bucket IDs to file IDs to be attached to the email.', true) ->param('status', MessageStatus::DRAFT, new WhiteList([MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]), 'Message Status. Value must be one of: ' . implode(', ', [MessageStatus::DRAFT, MessageStatus::SCHEDULED, MessageStatus::PROCESSING]) . '.', true) ->param('html', false, new Boolean(), 'Is content of type HTML', true) ->param('scheduledAt', null, new DatetimeValidator(requireDateInFuture: true), 'Scheduled delivery time for message in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future.', true) @@ -2582,7 +2586,7 @@ App::post('/v1/messaging/messages/email') ->inject('project') ->inject('queueForMessaging') ->inject('response') - ->action(function (string $messageId, string $subject, string $content, array $topics, array $users, array $targets, array $cc, array $bcc, string $status, bool $html, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) { + ->action(function (string $messageId, string $subject, string $content, array $topics, array $users, array $targets, array $cc, array $bcc, array $attachments, string $status, bool $html, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) { $messageId = $messageId == 'unique()' ? ID::unique() : $messageId; @@ -2615,6 +2619,29 @@ App::post('/v1/messaging/messages/email') } } + if (!empty($attachments)) { + foreach ($attachments as &$attachment) { + [$bucketId, $fileId] = CompoundUID::parse($attachment); + + $bucket = $dbForProject->getDocument('buckets', $bucketId); + + if ($bucket->isEmpty()) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } + + $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + + if ($file->isEmpty()) { + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); + } + + $attachment = [ + 'bucketId' => $bucketId, + 'fileId' => $fileId, + ]; + } + } + $message = $dbForProject->createDocument('messages', new Document([ '$id' => $messageId, 'providerType' => MESSAGE_TYPE_EMAIL, @@ -2628,6 +2655,7 @@ App::post('/v1/messaging/messages/email') 'html' => $html, 'cc' => $cc, 'bcc' => $bcc, + 'attachments' => $attachments, ], 'status' => $status, ])); @@ -2635,14 +2663,13 @@ App::post('/v1/messaging/messages/email') switch ($status) { case MessageStatus::PROCESSING: $queueForMessaging - ->setMessageId($message->getId()) - ->trigger(); + ->setType(MESSAGE_SEND_TYPE_EXTERNAL) + ->setMessageId($message->getId()); break; case MessageStatus::SCHEDULED: $schedule = $dbForConsole->createDocument('schedules', new Document([ 'region' => App::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', - 'resourceCollection' => 'messages', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), 'resourceUpdatedAt' => DateTime::now(), @@ -2672,7 +2699,7 @@ App::post('/v1/messaging/messages/email') }); App::post('/v1/messaging/messages/sms') - ->desc('Create an SMS.') + ->desc('Create an SMS') ->groups(['api', 'messaging']) ->label('audits.event', 'message.create') ->label('audits.resource', 'message/{response.$id}') @@ -2680,7 +2707,7 @@ App::post('/v1/messaging/messages/sms') ->label('scope', 'messages.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') - ->label('sdk.method', 'createSMS') + ->label('sdk.method', 'createSms') ->label('sdk.description', '/docs/references/messaging/create-sms.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) @@ -2744,14 +2771,13 @@ App::post('/v1/messaging/messages/sms') switch ($status) { case MessageStatus::PROCESSING: $queueForMessaging - ->setMessageId($message->getId()) - ->trigger(); + ->setType(MESSAGE_SEND_TYPE_EXTERNAL) + ->setMessageId($message->getId()); break; case MessageStatus::SCHEDULED: $schedule = $dbForConsole->createDocument('schedules', new Document([ 'region' => App::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', - 'resourceCollection' => 'messages', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), 'resourceUpdatedAt' => DateTime::now(), @@ -2781,7 +2807,7 @@ App::post('/v1/messaging/messages/sms') }); App::post('/v1/messaging/messages/push') - ->desc('Create a push notification.') + ->desc('Create a push notification') ->groups(['api', 'messaging']) ->label('audits.event', 'message.create') ->label('audits.resource', 'message/{response.$id}') @@ -2790,7 +2816,7 @@ App::post('/v1/messaging/messages/push') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') ->label('sdk.method', 'createPush') - ->label('sdk.description', '/docs/references/messaging/create-push-notification.md') + ->label('sdk.description', '/docs/references/messaging/create-push.md') ->label('sdk.response.code', Response::STATUS_CODE_CREATED) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_MESSAGE) @@ -2802,6 +2828,7 @@ App::post('/v1/messaging/messages/push') ->param('targets', [], new ArrayList(new UID()), 'List of Targets IDs.', true) ->param('data', null, new JSON(), 'Additional Data for push notification.', true) ->param('action', '', new Text(256), 'Action for push notification.', true) + ->param('image', '', new CompoundUID(), 'Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage.', true) ->param('icon', '', new Text(256), 'Icon for push notification. Available only for Android and Web Platform.', true) ->param('sound', '', new Text(256), 'Sound for push notification. Available only for Android and IOS Platform.', true) ->param('color', '', new Text(256), 'Color for push notification. Available only for Android Platform.', true) @@ -2815,7 +2842,7 @@ App::post('/v1/messaging/messages/push') ->inject('project') ->inject('queueForMessaging') ->inject('response') - ->action(function (string $messageId, string $title, string $body, array $topics, array $users, array $targets, ?array $data, string $action, string $icon, string $sound, string $color, string $tag, string $badge, string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) { + ->action(function (string $messageId, string $title, string $body, array $topics, array $users, array $targets, ?array $data, string $action, string $image, string $icon, string $sound, string $color, string $tag, string $badge, string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) { $messageId = $messageId == 'unique()' ? ID::unique() : $messageId; @@ -2846,9 +2873,41 @@ App::post('/v1/messaging/messages/push') } } + if (!empty($image)) { + [$bucketId, $fileId] = CompoundUID::parse($image); + + $bucket = $dbForProject->getDocument('buckets', $bucketId); + if ($bucket->isEmpty()) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } + + $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + if ($file->isEmpty()) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } + + if (!\in_array(Permission::read(Role::any()), \array_merge($file->getRead(), $bucket->getRead()))) { + throw new Exception(Exception::STORAGE_FILE_NOT_PUBLIC); + } + + if (!\in_array($file->getAttribute('mimeType'), ['image/png', 'image/jpeg'])) { + throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED); + } + + $host = App::getEnv('_APP_DOMAIN', 'localhost'); + $domain = new Domain(\parse_url($host, PHP_URL_HOST)); + $protocol = App::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https'; + + if (!$domain->isKnown()) { + throw new Exception(Exception::STORAGE_FILE_NOT_PUBLIC); + } + + $image = "{$protocol}://{$host}/v1/storage/buckets/{$bucket->getId()}/files/{$file->getId()}/view?project={$project->getId()}"; + } + $pushData = []; - $keys = ['title', 'body', 'data', 'action', 'icon', 'sound', 'color', 'tag', 'badge']; + $keys = ['title', 'body', 'data', 'action', 'image', 'icon', 'sound', 'color', 'tag', 'badge']; foreach ($keys as $key) { if (!empty($$key)) { @@ -2870,14 +2929,13 @@ App::post('/v1/messaging/messages/push') switch ($status) { case MessageStatus::PROCESSING: $queueForMessaging - ->setMessageId($message->getId()) - ->trigger(); + ->setType(MESSAGE_SEND_TYPE_EXTERNAL) + ->setMessageId($message->getId()); break; case MessageStatus::SCHEDULED: $schedule = $dbForConsole->createDocument('schedules', new Document([ 'region' => App::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', - 'resourceCollection' => 'messages', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), 'resourceUpdatedAt' => DateTime::now(), @@ -2964,7 +3022,7 @@ App::get('/v1/messaging/messages/:messageId/logs') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') ->label('sdk.method', 'listMessageLogs') - ->label('sdk.description', '/docs/references/messaging/messages/get-logs.md') + ->label('sdk.description', '/docs/references/messaging/list-message-logs.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_LOG_LIST) @@ -3136,7 +3194,7 @@ App::get('/v1/messaging/messages/:messageId') }); App::patch('/v1/messaging/messages/email/:messageId') - ->desc('Update an email.') + ->desc('Update an email') ->groups(['api', 'messaging']) ->label('audits.event', 'message.update') ->label('audits.resource', 'message/{response.$id}') @@ -3231,7 +3289,6 @@ App::patch('/v1/messaging/messages/email/:messageId') $schedule = $dbForConsole->createDocument('schedules', new Document([ 'region' => App::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', - 'resourceCollection' => 'messages', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), 'resourceUpdatedAt' => DateTime::now(), @@ -3263,8 +3320,8 @@ App::patch('/v1/messaging/messages/email/:messageId') if ($status === MessageStatus::PROCESSING) { $queueForMessaging - ->setMessageId($message->getId()) - ->trigger(); + ->setType(MESSAGE_SEND_TYPE_EXTERNAL) + ->setMessageId($message->getId()); } $queueForEvents @@ -3275,7 +3332,7 @@ App::patch('/v1/messaging/messages/email/:messageId') }); App::patch('/v1/messaging/messages/sms/:messageId') - ->desc('Update an SMS.') + ->desc('Update an SMS') ->groups(['api', 'messaging']) ->label('audits.event', 'message.update') ->label('audits.resource', 'message/{response.$id}') @@ -3283,7 +3340,7 @@ App::patch('/v1/messaging/messages/sms/:messageId') ->label('scope', 'messages.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') - ->label('sdk.method', 'updateSMS') + ->label('sdk.method', 'updateSms') ->label('sdk.description', '/docs/references/messaging/update-email.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) @@ -3350,7 +3407,6 @@ App::patch('/v1/messaging/messages/sms/:messageId') $schedule = $dbForConsole->createDocument('schedules', new Document([ 'region' => App::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', - 'resourceCollection' => 'messages', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), 'resourceUpdatedAt' => DateTime::now(), @@ -3382,8 +3438,8 @@ App::patch('/v1/messaging/messages/sms/:messageId') if ($status === MessageStatus::PROCESSING) { $queueForMessaging - ->setMessageId($message->getId()) - ->trigger(); + ->setType(MESSAGE_SEND_TYPE_EXTERNAL) + ->setMessageId($message->getId()); } $queueForEvents @@ -3394,7 +3450,7 @@ App::patch('/v1/messaging/messages/sms/:messageId') }); App::patch('/v1/messaging/messages/push/:messageId') - ->desc('Update a push notification.') + ->desc('Update a push notification') ->groups(['api', 'messaging']) ->label('audits.event', 'message.update') ->label('audits.resource', 'message/{response.$id}') @@ -3403,7 +3459,7 @@ App::patch('/v1/messaging/messages/push/:messageId') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN, APP_AUTH_TYPE_KEY]) ->label('sdk.namespace', 'messaging') ->label('sdk.method', 'updatePush') - ->label('sdk.description', '/docs/references/messaging/update-push-notification.md') + ->label('sdk.description', '/docs/references/messaging/update-push.md') ->label('sdk.response.code', Response::STATUS_CODE_OK) ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) ->label('sdk.response.model', Response::MODEL_MESSAGE) @@ -3415,6 +3471,7 @@ App::patch('/v1/messaging/messages/push/:messageId') ->param('body', null, new Text(64230), 'Body for push notification.', true) ->param('data', null, new JSON(), 'Additional Data for push notification.', true) ->param('action', null, new Text(256), 'Action for push notification.', true) + ->param('image', null, new CompoundUID(), 'Image for push notification. Must be a compound bucket ID to file ID of a jpeg, png, or bmp image in Appwrite Storage.', true) ->param('icon', null, new Text(256), 'Icon for push notification. Available only for Android and Web platforms.', true) ->param('sound', null, new Text(256), 'Sound for push notification. Available only for Android and iOS platforms.', true) ->param('color', null, new Text(256), 'Color for push notification. Available only for Android platforms.', true) @@ -3428,7 +3485,7 @@ App::patch('/v1/messaging/messages/push/:messageId') ->inject('project') ->inject('queueForMessaging') ->inject('response') - ->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $title, ?string $body, ?array $data, ?string $action, ?string $icon, ?string $sound, ?string $color, ?string $tag, ?int $badge, ?string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) { + ->action(function (string $messageId, ?array $topics, ?array $users, ?array $targets, ?string $title, ?string $body, ?array $data, ?string $action, ?string $image, ?string $icon, ?string $sound, ?string $color, ?string $tag, ?int $badge, ?string $status, ?string $scheduledAt, Event $queueForEvents, Database $dbForProject, Database $dbForConsole, Document $project, Messaging $queueForMessaging, Response $response) { $message = $dbForProject->getDocument('messages', $messageId); if ($message->isEmpty()) { @@ -3498,6 +3555,38 @@ App::patch('/v1/messaging/messages/push/:messageId') $pushData['badge'] = $badge; } + if (!\is_null($image)) { + [$bucketId, $fileId] = CompoundUID::parse($image); + + $bucket = $dbForProject->getDocument('buckets', $bucketId); + if ($bucket->isEmpty()) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } + + $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + if ($file->isEmpty()) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } + + if (!\in_array(Permission::read(Role::any()), \array_merge($file->getRead(), $bucket->getRead()))) { + throw new Exception(Exception::STORAGE_FILE_NOT_PUBLIC); + } + + if (!\in_array($file->getAttribute('mimeType'), ['image/png', 'image/jpeg'])) { + throw new Exception(Exception::STORAGE_FILE_TYPE_UNSUPPORTED); + } + + $host = App::getEnv('_APP_DOMAIN', 'localhost'); + $domain = new Domain(\parse_url($host, PHP_URL_HOST)); + $protocol = App::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https'; + + if (!$domain->isKnown()) { + throw new Exception(Exception::STORAGE_FILE_NOT_PUBLIC); + } + + $pushData['image'] = "{$protocol}://{$host}/v1/storage/buckets/{$bucket->getId()}/files/{$file->getId()}/view?project={$project->getId()}"; + } + $message->setAttribute('data', $pushData); if (!\is_null($status)) { @@ -3509,7 +3598,6 @@ App::patch('/v1/messaging/messages/push/:messageId') $schedule = $dbForConsole->createDocument('schedules', new Document([ 'region' => App::getEnv('_APP_REGION', 'default'), 'resourceType' => 'message', - 'resourceCollection' => 'messages', 'resourceId' => $message->getId(), 'resourceInternalId' => $message->getInternalId(), 'resourceUpdatedAt' => DateTime::now(), @@ -3541,8 +3629,8 @@ App::patch('/v1/messaging/messages/push/:messageId') if ($status === MessageStatus::PROCESSING) { $queueForMessaging - ->setMessageId($message->getId()) - ->trigger(); + ->setType(MESSAGE_SEND_TYPE_EXTERNAL) + ->setMessageId($message->getId()); } $queueForEvents diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 8dd6f8b82e..a067c45588 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -144,9 +144,30 @@ App::get('/v1/project/usage') ]; }, $dbForProject->find('buckets')); + // merge network inbound + outbound + $projectBandwidth = []; + foreach ($usage[METRIC_NETWORK_INBOUND] as $item) { + $projectBandwidth[$item['date']] ??= 0; + $projectBandwidth[$item['date']] += $item['value']; + } + + foreach ($usage[METRIC_NETWORK_OUTBOUND] as $item) { + $projectBandwidth[$item['date']] ??= 0; + $projectBandwidth[$item['date']] += $item['value']; + } + + + $network = []; + foreach ($projectBandwidth as $date => $value) { + $network[] = [ + 'date' => $date, + 'value' => $value + ]; + } + $response->dynamic(new Document([ 'requests' => ($usage[METRIC_NETWORK_REQUESTS]), - 'network' => ($usage[METRIC_NETWORK_INBOUND] + $usage[METRIC_NETWORK_OUTBOUND]), + 'network' => $network, 'users' => ($usage[METRIC_USERS]), 'executions' => ($usage[METRIC_EXECUTIONS]), 'executionsTotal' => $total[METRIC_EXECUTIONS], diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index 7fe2580aec..afd0d9fa53 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -360,9 +360,9 @@ App::post('/v1/storage/buckets/:bucketId/files') ->inject('user') ->inject('queueForEvents') ->inject('mode') - ->inject('deviceFiles') - ->inject('deviceLocal') - ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceFiles, Device $deviceLocal) { + ->inject('deviceForFiles') + ->inject('deviceForLocal') + ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode, Device $deviceForFiles, Device $deviceForLocal) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -493,13 +493,13 @@ App::post('/v1/storage/buckets/:bucketId/files') } // Save to storage - $fileSize ??= $deviceLocal->getFileSize($fileTmpName); - $path = $deviceFiles->getPath($fileId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION)); - $path = str_ireplace($deviceFiles->getRoot(), $deviceFiles->getRoot() . DIRECTORY_SEPARATOR . $bucket->getId(), $path); // Add bucket id to path after root + $fileSize ??= $deviceForLocal->getFileSize($fileTmpName); + $path = $deviceForFiles->getPath($fileId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION)); + $path = str_ireplace($deviceForFiles->getRoot(), $deviceForFiles->getRoot() . DIRECTORY_SEPARATOR . $bucket->getId(), $path); // Add bucket id to path after root $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); - $metadata = ['content_type' => $deviceLocal->getFileMimeType($fileTmpName)]; + $metadata = ['content_type' => $deviceForLocal->getFileMimeType($fileTmpName)]; if (!$file->isEmpty()) { $chunks = $file->getAttribute('chunksTotal', 1); $uploaded = $file->getAttribute('chunksUploaded', 0); @@ -514,32 +514,32 @@ App::post('/v1/storage/buckets/:bucketId/files') } } - $chunksUploaded = $deviceFiles->upload($fileTmpName, $path, $chunk, $chunks, $metadata); + $chunksUploaded = $deviceForFiles->upload($fileTmpName, $path, $chunk, $chunks, $metadata); if (empty($chunksUploaded)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed uploading file'); } if ($chunksUploaded === $chunks) { - if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antivirus', true) && $fileSize <= APP_LIMIT_ANTIVIRUS && $deviceFiles->getType() === Storage::DEVICE_LOCAL) { + if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antivirus', true) && $fileSize <= APP_LIMIT_ANTIVIRUS && $deviceForFiles->getType() === Storage::DEVICE_LOCAL) { $antivirus = new Network( App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'), (int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310) ); if (!$antivirus->fileScan($path)) { - $deviceFiles->delete($path); + $deviceForFiles->delete($path); throw new Exception(Exception::STORAGE_INVALID_FILE); } } - $mimeType = $deviceFiles->getFileMimeType($path); // Get mime-type before compression and encryption - $fileHash = $deviceFiles->getFileHash($path); // Get file hash before compression and encryption + $mimeType = $deviceForFiles->getFileMimeType($path); // Get mime-type before compression and encryption + $fileHash = $deviceForFiles->getFileHash($path); // Get file hash before compression and encryption $data = ''; // Compression $algorithm = $bucket->getAttribute('compression', Compression::NONE); if ($fileSize <= APP_STORAGE_READ_BUFFER && $algorithm != Compression::NONE) { - $data = $deviceFiles->read($path); + $data = $deviceForFiles->read($path); switch ($algorithm) { case Compression::ZSTD: $compressor = new Zstd(); @@ -559,7 +559,7 @@ App::post('/v1/storage/buckets/:bucketId/files') if ($bucket->getAttribute('encryption', true) && $fileSize <= APP_STORAGE_READ_BUFFER) { if (empty($data)) { - $data = $deviceFiles->read($path); + $data = $deviceForFiles->read($path); } $key = App::getEnv('_APP_OPENSSL_KEY_V1'); $iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM)); @@ -567,12 +567,12 @@ App::post('/v1/storage/buckets/:bucketId/files') } if (!empty($data)) { - if (!$deviceFiles->write($path, $data, $mimeType)) { + if (!$deviceForFiles->write($path, $data, $mimeType)) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to save file'); } } - $sizeActual = $deviceFiles->getFileSize($path); + $sizeActual = $deviceForFiles->getFileSize($path); $openSSLVersion = null; $openSSLCipher = null; @@ -872,9 +872,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->inject('project') ->inject('dbForProject') ->inject('mode') - ->inject('deviceFiles') - ->inject('deviceLocal') - ->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, string $mode, Device $deviceFiles, Device $deviceLocal) { + ->inject('deviceForFiles') + ->inject('deviceForLocal') + ->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, string $mode, Device $deviceForFiles, Device $deviceForLocal) { if (!\extension_loaded('imagick')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); @@ -931,10 +931,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $cipher = null; $background = (empty($background)) ? 'eceff1' : $background; $type = \strtolower(\pathinfo($path, PATHINFO_EXTENSION)); - $deviceFiles = $deviceLocal; + $deviceForFiles = $deviceForLocal; } - if (!$deviceFiles->exists($path)) { + if (!$deviceForFiles->exists($path)) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); } @@ -950,7 +950,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $output = empty($type) ? (array_search($mime, $outputs) ?? 'jpg') : $type; } - $source = $deviceFiles->read($path); + $source = $deviceForFiles->read($path); if (!empty($cipher)) { // Decrypt $source = OpenSSL::decrypt( @@ -1033,8 +1033,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ->inject('response') ->inject('dbForProject') ->inject('mode') - ->inject('deviceFiles') - ->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceFiles) { + ->inject('deviceForFiles') + ->action(function (string $bucketId, string $fileId, Request $request, Response $response, Database $dbForProject, string $mode, Device $deviceForFiles) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); @@ -1064,7 +1064,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') $path = $file->getAttribute('path', ''); - if (!$deviceFiles->exists($path)) { + if (!$deviceForFiles->exists($path)) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path); } @@ -1100,7 +1100,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') $source = ''; if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt - $source = $deviceFiles->read($path); + $source = $deviceForFiles->read($path); $source = OpenSSL::decrypt( $source, $file->getAttribute('openSSLCipher'), @@ -1114,14 +1114,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') switch ($file->getAttribute('algorithm', Compression::NONE)) { case Compression::ZSTD: if (empty($source)) { - $source = $deviceFiles->read($path); + $source = $deviceForFiles->read($path); } $compressor = new Zstd(); $source = $compressor->decompress($source); break; case Compression::GZIP: if (empty($source)) { - $source = $deviceFiles->read($path); + $source = $deviceForFiles->read($path); } $compressor = new GZIP(); $source = $compressor->decompress($source); @@ -1136,13 +1136,13 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') } if (!empty($rangeHeader)) { - $response->send($deviceFiles->read($path, $start, ($end - $start + 1))); + $response->send($deviceForFiles->read($path, $start, ($end - $start + 1))); } if ($size > APP_STORAGE_READ_BUFFER) { for ($i = 0; $i < ceil($size / MAX_OUTPUT_CHUNK_SIZE); $i++) { $response->chunk( - $deviceFiles->read( + $deviceForFiles->read( $path, ($i * MAX_OUTPUT_CHUNK_SIZE), min(MAX_OUTPUT_CHUNK_SIZE, $size - ($i * MAX_OUTPUT_CHUNK_SIZE)) @@ -1151,7 +1151,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download') ); } } else { - $response->send($deviceFiles->read($path)); + $response->send($deviceForFiles->read($path)); } }); @@ -1173,8 +1173,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ->inject('request') ->inject('dbForProject') ->inject('mode') - ->inject('deviceFiles') - ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceFiles) { + ->inject('deviceForFiles') + ->action(function (string $bucketId, string $fileId, Response $response, Request $request, Database $dbForProject, string $mode, Device $deviceForFiles) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); @@ -1205,7 +1205,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $path = $file->getAttribute('path', ''); - if (!$deviceFiles->exists($path)) { + if (!$deviceForFiles->exists($path)) { throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path); } @@ -1249,7 +1249,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') $source = ''; if (!empty($file->getAttribute('openSSLCipher'))) { // Decrypt - $source = $deviceFiles->read($path); + $source = $deviceForFiles->read($path); $source = OpenSSL::decrypt( $source, $file->getAttribute('openSSLCipher'), @@ -1263,14 +1263,14 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') switch ($file->getAttribute('algorithm', Compression::NONE)) { case Compression::ZSTD: if (empty($source)) { - $source = $deviceFiles->read($path); + $source = $deviceForFiles->read($path); } $compressor = new Zstd(); $source = $compressor->decompress($source); break; case Compression::GZIP: if (empty($source)) { - $source = $deviceFiles->read($path); + $source = $deviceForFiles->read($path); } $compressor = new GZIP(); $source = $compressor->decompress($source); @@ -1286,15 +1286,15 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') } if (!empty($rangeHeader)) { - $response->send($deviceFiles->read($path, $start, ($end - $start + 1))); + $response->send($deviceForFiles->read($path, $start, ($end - $start + 1))); return; } - $size = $deviceFiles->getFileSize($path); + $size = $deviceForFiles->getFileSize($path); if ($size > APP_STORAGE_READ_BUFFER) { for ($i = 0; $i < ceil($size / MAX_OUTPUT_CHUNK_SIZE); $i++) { $response->chunk( - $deviceFiles->read( + $deviceForFiles->read( $path, ($i * MAX_OUTPUT_CHUNK_SIZE), min(MAX_OUTPUT_CHUNK_SIZE, $size - ($i * MAX_OUTPUT_CHUNK_SIZE)) @@ -1303,7 +1303,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view') ); } } else { - $response->send($deviceFiles->read($path)); + $response->send($deviceForFiles->read($path)); } }); @@ -1438,9 +1438,9 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') ->inject('dbForProject') ->inject('queueForEvents') ->inject('mode') - ->inject('deviceFiles') + ->inject('deviceForFiles') ->inject('queueForDeletes') - ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceFiles, Delete $queueForDeletes) { + ->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Device $deviceForFiles, Delete $queueForDeletes) { $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); @@ -1471,17 +1471,18 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId') $deviceDeleted = false; if ($file->getAttribute('chunksTotal') !== $file->getAttribute('chunksUploaded')) { - $deviceDeleted = $deviceFiles->abort( + $deviceDeleted = $deviceForFiles->abort( $file->getAttribute('path'), ($file->getAttribute('metadata', [])['uploadId'] ?? '') ); } else { - $deviceDeleted = $deviceFiles->delete($file->getAttribute('path')); + $deviceDeleted = $deviceForFiles->delete($file->getAttribute('path')); } if ($deviceDeleted) { $queueForDeletes ->setType(DELETE_TYPE_CACHE_BY_RESOURCE) + ->setResourceType('bucket/' . $bucket->getId()) ->setResource('file/' . $fileId) ; diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 775fc27bb7..c1fcb9d8c2 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -658,10 +658,10 @@ App::post('/v1/teams/:teamId/memberships') ]); $queueForMessaging + ->setType(MESSAGE_SEND_TYPE_INTERNAL) ->setMessage($messageDoc) ->setRecipients([$phone]) - ->setProviderType('SMS') - ->trigger(); + ->setProviderType('SMS'); } } @@ -995,7 +995,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status') $dbForProject->purgeCachedDocument('users', $user->getId()); - $team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1))); + Authorization::skip(fn() => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); $queueForEvents ->setParam('teamId', $team->getId()) @@ -1075,8 +1075,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId') $dbForProject->purgeCachedDocument('users', $user->getId()); if ($membership->getAttribute('confirm')) { // Count only confirmed members - $team->setAttribute('total', \max($team->getAttribute('total', 0) - 1, 0)); - Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team)); + Authorization::skip(fn() => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0)); } $queueForEvents diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index 55ad758637..e2c41d9ed1 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -192,7 +192,6 @@ App::post('/v1/users') ->inject('hooks') ->action(function (string $userId, ?string $email, ?string $phone, ?string $password, string $name, Response $response, Document $project, Database $dbForProject, Event $queueForEvents, Hooks $hooks) { $user = createUser('plaintext', '{}', $userId, $email, $password, $phone, $name, $project, $dbForProject, $queueForEvents, $hooks); - $response ->setStatusCode(Response::STATUS_CODE_CREATED) ->dynamic($user, Response::MODEL_USER); @@ -806,6 +805,9 @@ App::get('/v1/users/:userId/logs') $output[$i] = new Document([ 'event' => $log['event'], + 'userId' => ID::custom($log['data']['userId']), + 'userEmail' => $log['data']['userEmail'] ?? null, + 'userName' => $log['data']['userName'] ?? null, 'ip' => $log['ip'], 'time' => $log['time'], 'osCode' => $os['osCode'], @@ -834,7 +836,7 @@ App::get('/v1/users/:userId/logs') } $response->dynamic(new Document([ - 'total' => $audit->countLogsByUser($user->getId()), + 'total' => $audit->countLogsByUser($user->getInternalId()), 'logs' => $output, ]), Response::MODEL_LOG_LIST); }); diff --git a/app/controllers/api/vcs.php b/app/controllers/api/vcs.php index 2bc94885b1..09ec5d7690 100644 --- a/app/controllers/api/vcs.php +++ b/app/controllers/api/vcs.php @@ -238,9 +238,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId $queueForBuilds ->setType(BUILD_TYPE_DEPLOYMENT) ->setResource($function) - ->setDeployment($deployment) - ->setProject($project) - ->trigger(); + ->setDeployment($deployment); //TODO: Add event? } diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 810d778a21..b9429fab63 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -2,6 +2,7 @@ use Appwrite\Auth\Auth; use Appwrite\Event\Audit; +use Appwrite\Event\Build; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Delete; use Appwrite\Event\Event; @@ -305,10 +306,11 @@ App::init() ->inject('queueForAudits') ->inject('queueForDeletes') ->inject('queueForDatabase') + ->inject('queueForBuilds') ->inject('queueForUsage') ->inject('dbForProject') ->inject('mode') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Usage $queueForUsage, Database $dbForProject, string $mode) use ($databaseListener) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, string $mode) use ($databaseListener) { $route = $utopia->getRoute(); @@ -382,9 +384,6 @@ App::init() ->setProject($project) ->setUser($user); - $queueForMessaging - ->setProject($project); - $queueForAudits ->setMode($mode) ->setUserAgent($request->getUserAgent('')) @@ -393,9 +392,10 @@ App::init() ->setProject($project) ->setUser($user); - $queueForDeletes->setProject($project); $queueForDatabase->setProject($project); + $queueForBuilds->setProject($project); + $queueForMessaging->setProject($project); $dbForProject ->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $databaseListener($event, $document, $project, $queueForUsage, $dbForProject)) @@ -403,24 +403,22 @@ App::init() ; $useCache = $route->getLabel('cache', false); - if ($useCache) { $key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER); + $cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key)); $cache = new Cache( new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId()) ); $timestamp = 60 * 60 * 24 * 30; $data = $cache->load($key, $timestamp); - if (!empty($data)) { - $data = json_decode($data, true); - $parts = explode('/', $data['resourceType']); + if (!empty($data) && !$cacheLog->isEmpty()) { + $parts = explode('/', $cacheLog->getAttribute('resourceType')); $type = $parts[0] ?? null; if ($type === 'bucket') { $bucketId = $parts[1] ?? null; - - $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); + $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -432,11 +430,12 @@ App::init() $fileSecurity = $bucket->getAttribute('fileSecurity', false); $validator = new Authorization(Database::PERMISSION_READ); $valid = $validator->isValid($bucket->getRead()); + if (!$fileSecurity && !$valid) { throw new Exception(Exception::USER_UNAUTHORIZED); } - $parts = explode('/', $data['resource']); + $parts = explode('/', $cacheLog->getAttribute('resource')); $fileId = $parts[1] ?? null; if ($fileSecurity && !$valid) { @@ -453,8 +452,8 @@ App::init() $response ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $timestamp) . ' GMT') ->addHeader('X-Appwrite-Cache', 'hit') - ->setContentType($data['contentType']) - ->send(base64_decode($data['payload'])) + ->setContentType($cacheLog->getAttribute('mimeType')) + ->send($data) ; } else { $response->addHeader('X-Appwrite-Cache', 'miss'); @@ -513,11 +512,13 @@ App::shutdown() ->inject('queueForUsage') ->inject('queueForDeletes') ->inject('queueForDatabase') + ->inject('queueForBuilds') + ->inject('queueForMessaging') ->inject('dbForProject') ->inject('queueForFunctions') ->inject('mode') ->inject('dbForConsole') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Database $dbForProject, Func $queueForFunctions, string $mode, Database $dbForConsole) use ($parseLabel) { $responsePayload = $response->getPayload(); @@ -618,6 +619,14 @@ App::shutdown() $queueForDatabase->trigger(); } + if (!empty($queueForBuilds->getType())) { + $queueForBuilds->trigger(); + } + + if (!empty($queueForMessaging->getType())) { + $queueForMessaging->trigger(); + } + /** * Cache label */ @@ -625,7 +634,6 @@ App::shutdown() if ($useCache) { $resource = $resourceType = null; $data = $response->getPayload(); - if (!empty($data['payload'])) { $pattern = $route->getLabel('cache.resource', null); if (!empty($pattern)) { @@ -637,24 +645,19 @@ App::shutdown() $resourceType = $parseLabel($pattern, $responsePayload, $requestParams, $user); } - $key = md5($request->getURI() . implode('*', $request->getParams()) . '*' . APP_CACHE_BUSTER); - $data = json_encode([ - 'resourceType' => $resourceType, - 'resource' => $resource, - 'contentType' => $response->getContentType(), - 'payload' => base64_encode($data['payload']), - ]); - - $signature = md5($data); - $cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key)); + $key = md5($request->getURI() . '*' . implode('*', $request->getParams())) . '*' . APP_CACHE_BUSTER; + $signature = md5($data['payload']); + $cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key)); $accessedAt = $cacheLog->getAttribute('accessedAt', ''); $now = DateTime::now(); if ($cacheLog->isEmpty()) { Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([ - '$id' => $key, - 'resource' => $resource, - 'accessedAt' => $now, - 'signature' => $signature, + '$id' => $key, + 'resource' => $resource, + 'resourceType' => $resourceType, + 'mimeType' => $response->getContentType(), + 'accessedAt' => $now, + 'signature' => $signature, ]))); } elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) { $cacheLog->setAttribute('accessedAt', $now); @@ -665,7 +668,7 @@ App::shutdown() $cache = new Cache( new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId()) ); - $cache->save($key, $data); + $cache->save($key, $data['payload']); } } } diff --git a/app/init.php b/app/init.php index 4e75b5198b..17b4a4321d 100644 --- a/app/init.php +++ b/app/init.php @@ -142,9 +142,11 @@ const APP_SOCIAL_DEV = 'https://dev.to/appwrite'; const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite'; const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1'; const APP_HOSTNAME_INTERNAL = 'appwrite'; + // Database Reconnect const DATABASE_RECONNECT_SLEEP = 2; const DATABASE_RECONNECT_MAX_ATTEMPTS = 10; + // Database Worker Types const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute'; const DATABASE_TYPE_CREATE_INDEX = 'createIndex'; @@ -152,9 +154,11 @@ const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute'; const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex'; const DATABASE_TYPE_DELETE_COLLECTION = 'deleteCollection'; const DATABASE_TYPE_DELETE_DATABASE = 'deleteDatabase'; + // Build Worker Types const BUILD_TYPE_DEPLOYMENT = 'deployment'; const BUILD_TYPE_RETRY = 'retry'; + // Deletion Types const DELETE_TYPE_DATABASES = 'databases'; const DELETE_TYPE_DOCUMENT = 'document'; @@ -180,6 +184,10 @@ const DELETE_TYPE_TOPIC = 'topic'; const DELETE_TYPE_TARGET = 'target'; const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets'; const DELETE_TYPE_SESSION_TARGETS = 'session_targets'; + +// Message types +const MESSAGE_SEND_TYPE_INTERNAL = 'internal'; +const MESSAGE_SEND_TYPE_EXTERNAL = 'external'; // Mail Types const MAIL_TYPE_VERIFICATION = 'verification'; const MAIL_TYPE_MAGIC_SESSION = 'magicSession'; @@ -1375,19 +1383,19 @@ App::setResource('cache', function (Group $pools) { return new Cache(new Sharding($adapters)); }, ['pools']); -App::setResource('deviceLocal', function () { +App::setResource('deviceForLocal', function () { return new Local(); }); -App::setResource('deviceFiles', function ($project) { +App::setResource('deviceForFiles', function ($project) { return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); }, ['project']); -App::setResource('deviceFunctions', function ($project) { +App::setResource('deviceForFunctions', function ($project) { return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); }, ['project']); -App::setResource('deviceBuilds', function ($project) { +App::setResource('deviceForBuilds', function ($project) { return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); }, ['project']); diff --git a/app/worker.php b/app/worker.php index 2080970acb..d213bb07ee 100644 --- a/app/worker.php +++ b/app/worker.php @@ -15,6 +15,7 @@ use Appwrite\Event\Messaging; use Appwrite\Event\Migration; use Appwrite\Event\Phone; use Appwrite\Event\Usage; +use Appwrite\Event\UsageDump; use Appwrite\Platform\Appwrite; use Swoole\Runtime; use Utopia\App; @@ -34,6 +35,7 @@ use Utopia\Logger\Log; use Utopia\Logger\Logger; use Utopia\Pools\Group; use Utopia\Queue\Connection; +use Utopia\Storage\Device\Local; Authorization::disable(); Runtime::enableCoroutine(SWOOLE_HOOK_ALL); @@ -45,8 +47,7 @@ Server::setResource('dbForConsole', function (Cache $cache, Registry $register) $database = $pools ->get('console') ->pop() - ->getResource() - ; + ->getResource(); $adapter = new Database($database, $cache); $adapter->setNamespace('_console'); @@ -54,26 +55,6 @@ Server::setResource('dbForConsole', function (Cache $cache, Registry $register) return $adapter; }, ['cache', 'register']); -Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Database $dbForConsole) { - $payload = $message->getPayload() ?? []; - $project = new Document($payload['project'] ?? []); - - if ($project->isEmpty() || $project->getId() === 'console') { - return $dbForConsole; - } - - $pools = $register->get('pools'); - $database = $pools - ->get($project->getAttribute('database')) - ->pop() - ->getResource() - ; - - $adapter = new Database($database, $cache); - $adapter->setNamespace('_' . $project->getInternalId()); - return $adapter; -}, ['cache', 'register', 'message', 'dbForConsole']); - Server::setResource('project', function (Message $message, Database $dbForConsole) { $payload = $message->getPayload() ?? []; $project = new Document($payload['project'] ?? []); @@ -81,10 +62,26 @@ Server::setResource('project', function (Message $message, Database $dbForConsol if ($project->getId() === 'console') { return $project; } + return $dbForConsole->getDocument('projects', $project->getId()); - ; }, ['message', 'dbForConsole']); +Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForConsole) { + if ($project->isEmpty() || $project->getId() === 'console') { + return $dbForConsole; + } + + $pools = $register->get('pools'); + $database = $pools + ->get($project->getAttribute('database')) + ->pop() + ->getResource(); + + $adapter = new Database($database, $cache); + $adapter->setNamespace('_' . $project->getInternalId()); + return $adapter; +}, ['cache', 'register', 'message', 'project', 'dbForConsole']); + Server::setResource('getProjectDB', function (Group $pools, Database $dbForConsole, $cache) { $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools @@ -143,72 +140,92 @@ Server::setResource('cache', function (Registry $register) { return new Cache(new Sharding($adapters)); }, ['register']); + Server::setResource('log', fn() => new Log()); + Server::setResource('queueForUsage', function (Connection $queue) { return new Usage($queue); }, ['queue']); + +Server::setResource('queueForUsageDump', function (Connection $queue) { + return new UsageDump($queue); +}, ['queue']); + Server::setResource('queue', function (Group $pools) { return $pools->get('queue')->pop()->getResource(); }, ['pools']); + Server::setResource('queueForDatabase', function (Connection $queue) { return new EventDatabase($queue); }, ['queue']); + Server::setResource('queueForMessaging', function (Connection $queue) { return new Messaging($queue); }, ['queue']); + Server::setResource('queueForMails', function (Connection $queue) { return new Mail($queue); }, ['queue']); + Server::setResource('queueForBuilds', function (Connection $queue) { return new Build($queue); }, ['queue']); + Server::setResource('queueForDeletes', function (Connection $queue) { return new Delete($queue); }, ['queue']); + Server::setResource('queueForEvents', function (Connection $queue) { return new Event($queue); }, ['queue']); + Server::setResource('queueForAudits', function (Connection $queue) { return new Audit($queue); }, ['queue']); + Server::setResource('queueForFunctions', function (Connection $queue) { return new Func($queue); }, ['queue']); + Server::setResource('queueForCertificates', function (Connection $queue) { return new Certificate($queue); }, ['queue']); + Server::setResource('queueForMigrations', function (Connection $queue) { return new Migration($queue); }, ['queue']); + Server::setResource('queueForHamster', function (Connection $queue) { return new Hamster($queue); }, ['queue']); + Server::setResource('logger', function (Registry $register) { return $register->get('logger'); }, ['register']); + Server::setResource('pools', function (Registry $register) { return $register->get('pools'); }, ['register']); -Server::setResource('getFunctionsDevice', function () { - return function (string $projectId) { - return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $projectId); - }; -}); -Server::setResource('getFilesDevice', function () { - return function (string $projectId) { - return getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId); - }; -}); -Server::setResource('getBuildsDevice', function () { - return function (string $projectId) { - return getDevice(APP_STORAGE_BUILDS . '/app-' . $projectId); - }; -}); -Server::setResource('getCacheDevice', function () { - return function (string $projectId) { - return getDevice(APP_STORAGE_CACHE . '/app-' . $projectId); - }; -}); + +Server::setResource('deviceForFunctions', function (Document $project) { + return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()); +}, ['project']); + +Server::setResource('deviceForFiles', function (Document $project) { + return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); +}, ['project']); + +Server::setResource('deviceForBuilds', function (Document $project) { + return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()); +}, ['project']); + +Server::setResource('deviceForCache', function (Document $project) { + return getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId()); +}, ['project']); + +Server::setResource('deviceForLocalFiles', function (Document $project) { + return new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId()); +}, ['project']); $pools = $register->get('pools'); $platform = new Appwrite(); @@ -303,12 +320,9 @@ $worker Console::error('[Error] Line: ' . $error->getLine()); }); -try { - $workerStart = $worker->getWorkerStart(); -} catch (\Throwable $error) { - $worker->workerStart(); -} finally { - Console::info("Worker $workerName started"); -} +$worker->workerStart() + ->action(function () use ($workerName) { + Console::info("Worker $workerName started"); + }); $worker->start(); diff --git a/bin/worker-usage-dump b/bin/worker-usage-dump new file mode 100644 index 0000000000..43ca87fcb3 --- /dev/null +++ b/bin/worker-usage-dump @@ -0,0 +1,3 @@ +#!/bin/sh + +php /usr/src/code/app/worker.php usage-dump $@ \ No newline at end of file diff --git a/composer.json b/composer.json index a31599ce32..3131c14b8d 100644 --- a/composer.json +++ b/composer.json @@ -1,4 +1,5 @@ { + "name": "appwrite/server-ce", "description": "End to end backend server for frontend and mobile apps.", "type": "project", @@ -56,7 +57,7 @@ "utopia-php/image": "0.6.*", "utopia-php/locale": "0.4.*", "utopia-php/logger": "0.3.*", - "utopia-php/messaging": "0.9.*", + "utopia-php/messaging": "0.10.*", "utopia-php/migration": "0.3.*", "utopia-php/orchestration": "0.9.*", "utopia-php/platform": "0.5.*", @@ -70,7 +71,7 @@ "utopia-php/websocket": "0.1.*", "matomo/device-detector": "6.1.*", "dragonmantank/cron-expression": "3.3.2", - "phpmailer/phpmailer": "6.8.0", + "phpmailer/phpmailer": "6.9.1", "chillerlan/php-qrcode": "4.3.4", "adhocore/jwt": "1.1.2", "spomky-labs/otphp": "^10.0", diff --git a/composer.lock b/composer.lock index 86c9472447..c008f3d599 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "609062319cc652e2760367f39604ac77", + "content-hash": "a65e4309e3fd851aec97be2cf5b83cb4", "packages": [ { "name": "adhocore/jwt", @@ -885,16 +885,16 @@ }, { "name": "phpmailer/phpmailer", - "version": "v6.8.0", + "version": "v6.9.1", "source": { "type": "git", "url": "https://github.com/PHPMailer/PHPMailer.git", - "reference": "df16b615e371d81fb79e506277faea67a1be18f1" + "reference": "039de174cd9c17a8389754d3b877a2ed22743e18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/df16b615e371d81fb79e506277faea67a1be18f1", - "reference": "df16b615e371d81fb79e506277faea67a1be18f1", + "url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/039de174cd9c17a8389754d3b877a2ed22743e18", + "reference": "039de174cd9c17a8389754d3b877a2ed22743e18", "shasum": "" }, "require": { @@ -904,16 +904,17 @@ "php": ">=5.5.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.2", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", "doctrine/annotations": "^1.2.6 || ^1.13.3", "php-parallel-lint/php-console-highlighter": "^1.0.0", "php-parallel-lint/php-parallel-lint": "^1.3.2", "phpcompatibility/php-compatibility": "^9.3.5", "roave/security-advisories": "dev-latest", - "squizlabs/php_codesniffer": "^3.7.1", + "squizlabs/php_codesniffer": "^3.7.2", "yoast/phpunit-polyfills": "^1.0.4" }, "suggest": { + "decomplexity/SendOauth2": "Adapter for using XOAUTH2 authentication", "ext-mbstring": "Needed to send email in multibyte encoding charset or decode encoded addresses", "ext-openssl": "Needed for secure SMTP sending and DKIM signing", "greew/oauth2-azure-provider": "Needed for Microsoft Azure XOAUTH2 authentication", @@ -953,7 +954,7 @@ "description": "PHPMailer is a full-featured email creation and transfer class for PHP", "support": { "issues": "https://github.com/PHPMailer/PHPMailer/issues", - "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.8.0" + "source": "https://github.com/PHPMailer/PHPMailer/tree/v6.9.1" }, "funding": [ { @@ -961,7 +962,7 @@ "type": "github" } ], - "time": "2023-03-06T14:43:22+00:00" + "time": "2023-11-25T22:23:28+00:00" }, { "name": "spomky-labs/otphp", @@ -1551,16 +1552,16 @@ }, { "name": "utopia-php/database", - "version": "0.48.2", + "version": "0.48.3", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4" + "reference": "c7dd97d92f52a0aec9951e0b02309a100f3f24a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4", - "reference": "0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4", + "url": "https://api.github.com/repos/utopia-php/database/zipball/c7dd97d92f52a0aec9951e0b02309a100f3f24a9", + "reference": "c7dd97d92f52a0aec9951e0b02309a100f3f24a9", "shasum": "" }, "require": { @@ -1568,7 +1569,7 @@ "ext-pdo": "*", "php": ">=8.0", "utopia-php/cache": "0.9.*", - "utopia-php/framework": "0.*.*", + "utopia-php/framework": "0.33.*", "utopia-php/mongo": "0.3.*" }, "require-dev": { @@ -1601,9 +1602,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.48.2" + "source": "https://github.com/utopia-php/database/tree/0.48.3" }, - "time": "2024-02-02T14:10:14+00:00" + "time": "2024-02-21T08:32:09+00:00" }, { "name": "utopia-php/domains", @@ -1911,28 +1912,28 @@ }, { "name": "utopia-php/messaging", - "version": "0.9.1", + "version": "0.10.0", "source": { "type": "git", "url": "https://github.com/utopia-php/messaging.git", - "reference": "7beec07684e9e1dfcf4ab5b1ba731fa396dccbdf" + "reference": "71dce00ad43eb278a877cb2c329f7b8d677adfeb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/messaging/zipball/7beec07684e9e1dfcf4ab5b1ba731fa396dccbdf", - "reference": "7beec07684e9e1dfcf4ab5b1ba731fa396dccbdf", + "url": "https://api.github.com/repos/utopia-php/messaging/zipball/71dce00ad43eb278a877cb2c329f7b8d677adfeb", + "reference": "71dce00ad43eb278a877cb2c329f7b8d677adfeb", "shasum": "" }, "require": { "ext-curl": "*", "ext-openssl": "*", - "php": ">=8.0.0" + "php": ">=8.0.0", + "phpmailer/phpmailer": "6.9.1" }, "require-dev": { - "laravel/pint": "1.13.*", - "phpmailer/phpmailer": "6.8.*", - "phpstan/phpstan": "1.10.*", - "phpunit/phpunit": "9.6.10" + "laravel/pint": "1.13.11", + "phpstan/phpstan": "1.10.58", + "phpunit/phpunit": "10.5.10" }, "type": "library", "autoload": { @@ -1955,9 +1956,9 @@ ], "support": { "issues": "https://github.com/utopia-php/messaging/issues", - "source": "https://github.com/utopia-php/messaging/tree/0.9.1" + "source": "https://github.com/utopia-php/messaging/tree/0.10.0" }, - "time": "2024-02-15T03:44:44+00:00" + "time": "2024-02-20T07:30:15+00:00" }, { "name": "utopia-php/migration", @@ -2779,16 +2780,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.36.3", + "version": "0.36.4", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "8d308f7f492545da3e51ea5b91c0778392c40b93" + "reference": "8d932098009d62d37dda73cfe4ebc11f83e21405" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/8d308f7f492545da3e51ea5b91c0778392c40b93", - "reference": "8d308f7f492545da3e51ea5b91c0778392c40b93", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/8d932098009d62d37dda73cfe4ebc11f83e21405", + "reference": "8d932098009d62d37dda73cfe4ebc11f83e21405", "shasum": "" }, "require": { @@ -2824,9 +2825,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/0.36.3" + "source": "https://github.com/appwrite/sdk-generator/tree/0.36.4" }, - "time": "2024-02-14T06:33:38+00:00" + "time": "2024-02-20T16:36:15+00:00" }, { "name": "doctrine/deprecations", @@ -3409,16 +3410,16 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.0", + "version": "1.8.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "fad452781b3d774e3337b0c0b245dd8e5a4455fc" + "reference": "bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/fad452781b3d774e3337b0c0b245dd8e5a4455fc", - "reference": "fad452781b3d774e3337b0c0b245dd8e5a4455fc", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353", + "reference": "bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353", "shasum": "" }, "require": { @@ -3461,9 +3462,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.1" }, - "time": "2024-01-11T11:49:22+00:00" + "time": "2024-01-18T19:15:27+00:00" }, { "name": "phpspec/prophecy", diff --git a/docker-compose.yml b/docker-compose.yml index 0a806929d7..f5754b4b09 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -582,7 +582,6 @@ services: - _APP_REDIS_PORT - _APP_REDIS_USER - _APP_REDIS_PASS - - _APP_SMS_PROJECTS_DENY_LIST - _APP_DB_HOST - _APP_DB_PORT - _APP_DB_SCHEMA @@ -592,6 +591,7 @@ services: - _APP_LOGGING_CONFIG - _APP_SMS_FROM - _APP_SMS_PROVIDER + - _APP_SMS_PROJECTS_DENY_LIST appwrite-worker-migrations: entrypoint: worker-migrations @@ -696,6 +696,37 @@ services: - _APP_LOGGING_CONFIG - _APP_USAGE_AGGREGATION_INTERVAL + appwrite-worker-usage-dump: + entrypoint: worker-usage-dump + <<: *x-logging + container_name: appwrite-worker-usage-dump + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_PROVIDER + - _APP_LOGGING_CONFIG + - _APP_USAGE_AGGREGATION_INTERVAL + appwrite-scheduler-functions: entrypoint: schedule-functions <<: *x-logging @@ -941,6 +972,7 @@ services: # - appwrite # volumes: # - appwrite-uploads:/storage/uploads + # Dev Tools Start ------------------------------------------------------------------------------------------ # # The Appwrite Team uses the following tools to help debug, monitor and diagnose the Appwrite stack @@ -949,8 +981,9 @@ services: # # MailCatcher - An SMTP server. Catches all system emails and displays them in a nice UI. # RequestCatcher - An HTTP server. Catches all system https calls and displays them using a simple HTTP API. Used to debug & tests webhooks and HTTP tasks - # RedisCommander - A nice UI for exploring Redis data - # Webgrind - A nice UI for exploring and debugging code-level stuff + # Redis Insight - A nice UI for exploring Redis data + # Adminer - A nice UI for exploring MariaDB data + # GraphQl Explorer - A nice UI for exploring GraphQL API maildev: # used mainly for dev tests image: appwrite/mailcatcher:1.0.0 @@ -980,21 +1013,15 @@ services: networks: - appwrite - # redis-commander: - # image: rediscommander/redis-commander:latest - # restart: unless-stopped - # networks: - # - appwrite - # environment: - # - REDIS_HOSTS=redis - # ports: - # - "8081:8081" - # webgrind: - # image: 'jokkedk/webgrind:latest' - # volumes: - # - './debug:/tmp' - # ports: - # - '3001:80' + redis-insight: + image: redis/redisinsight:latest + restart: unless-stopped + networks: + - appwrite + environment: + - REDIS_HOSTS=redis + ports: + - "8081:5540" graphql-explorer: container_name: appwrite-graphql-explorer diff --git a/docs/examples/1.4.x/server-ruby/examples/account/create-phone-verification.md b/docs/examples/1.4.x/server-ruby/examples/account/create-phone-verification.md index 7ec0a1c0c9..60e88d38eb 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/create-phone-verification.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/create-phone-verification.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.create_phone_verification() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/create-recovery.md b/docs/examples/1.4.x/server-ruby/examples/account/create-recovery.md index 5c6d13c724..5bc3e8166b 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/create-recovery.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/create-recovery.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.create_recovery(email: 'email@example.com', url: 'https://example.com') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/create-verification.md b/docs/examples/1.4.x/server-ruby/examples/account/create-verification.md index dcd9722e47..2693bb6b1e 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/create-verification.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/create-verification.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.create_verification(url: 'https://example.com') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/delete-identity.md b/docs/examples/1.4.x/server-ruby/examples/account/delete-identity.md index 1dcb89a030..0aff3bfeaa 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/delete-identity.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/delete-identity.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.delete_identity(identity_id: '[IDENTITY_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/delete-session.md b/docs/examples/1.4.x/server-ruby/examples/account/delete-session.md index 3357b2ba20..89589c0763 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/delete-session.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/delete-session.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.delete_session(session_id: '[SESSION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/delete-sessions.md b/docs/examples/1.4.x/server-ruby/examples/account/delete-sessions.md index b218a536db..a1a6a2c14a 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/delete-sessions.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/delete-sessions.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.delete_sessions() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/get-prefs.md b/docs/examples/1.4.x/server-ruby/examples/account/get-prefs.md index 24092b922b..d2f684e3f0 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/get-prefs.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/get-prefs.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.get_prefs() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/get-session.md b/docs/examples/1.4.x/server-ruby/examples/account/get-session.md index 2a8209031d..12d048abef 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/get-session.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/get-session.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.get_session(session_id: '[SESSION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/get.md b/docs/examples/1.4.x/server-ruby/examples/account/get.md index 61237b2215..29ca620791 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/get.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/get.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.get() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/list-identities.md b/docs/examples/1.4.x/server-ruby/examples/account/list-identities.md index a7173abf11..dec816d991 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/list-identities.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/list-identities.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.list_identities() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/list-logs.md b/docs/examples/1.4.x/server-ruby/examples/account/list-logs.md index 8a08eb96be..874ea7e443 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/list-logs.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/list-logs.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.list_logs() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/list-sessions.md b/docs/examples/1.4.x/server-ruby/examples/account/list-sessions.md index a6bcb6816d..961142ebc2 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/list-sessions.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/list-sessions.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.list_sessions() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/update-email.md b/docs/examples/1.4.x/server-ruby/examples/account/update-email.md index cd2e10cbbe..c9088d0ffc 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/update-email.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/update-email.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.update_email(email: 'email@example.com', password: 'password') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/update-name.md b/docs/examples/1.4.x/server-ruby/examples/account/update-name.md index 2685e88cf6..e75e9a1f07 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/update-name.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/update-name.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.update_name(name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/update-password.md b/docs/examples/1.4.x/server-ruby/examples/account/update-password.md index 6ec26f407a..c453a12b46 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/update-password.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/update-password.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.update_password(password: '') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/update-phone-verification.md b/docs/examples/1.4.x/server-ruby/examples/account/update-phone-verification.md index 103b32c00d..9a38320e7d 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/update-phone-verification.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/update-phone-verification.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.update_phone_verification(user_id: '[USER_ID]', secret: '[SECRET]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/update-phone.md b/docs/examples/1.4.x/server-ruby/examples/account/update-phone.md index 377c578dea..81bbb322d0 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/update-phone.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/update-phone.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.update_phone(phone: '+12065550100', password: 'password') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/update-prefs.md b/docs/examples/1.4.x/server-ruby/examples/account/update-prefs.md index 338a9704f1..4a379d9bf0 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/update-prefs.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/update-prefs.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.update_prefs(prefs: {}) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/update-recovery.md b/docs/examples/1.4.x/server-ruby/examples/account/update-recovery.md index fa901de857..bbedfa1742 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/update-recovery.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/update-recovery.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.update_recovery(user_id: '[USER_ID]', secret: '[SECRET]', password: 'password', password_again: 'password') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/update-session.md b/docs/examples/1.4.x/server-ruby/examples/account/update-session.md index 31dde2e94f..724251e85a 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/update-session.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/update-session.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.update_session(session_id: '[SESSION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/update-status.md b/docs/examples/1.4.x/server-ruby/examples/account/update-status.md index ea7a3857b5..5f3bb37610 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/update-status.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/update-status.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.update_status() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/account/update-verification.md b/docs/examples/1.4.x/server-ruby/examples/account/update-verification.md index b8f674ec77..416ab5184d 100644 --- a/docs/examples/1.4.x/server-ruby/examples/account/update-verification.md +++ b/docs/examples/1.4.x/server-ruby/examples/account/update-verification.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ account = Account.new(client) response = account.update_verification(user_id: '[USER_ID]', secret: '[SECRET]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/avatars/get-browser.md b/docs/examples/1.4.x/server-ruby/examples/avatars/get-browser.md index 2a66b46b92..969eb5bb6c 100644 --- a/docs/examples/1.4.x/server-ruby/examples/avatars/get-browser.md +++ b/docs/examples/1.4.x/server-ruby/examples/avatars/get-browser.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ avatars = Avatars.new(client) response = avatars.get_browser(code: 'aa') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/avatars/get-credit-card.md b/docs/examples/1.4.x/server-ruby/examples/avatars/get-credit-card.md index d5bcaa824f..81a81ceeb2 100644 --- a/docs/examples/1.4.x/server-ruby/examples/avatars/get-credit-card.md +++ b/docs/examples/1.4.x/server-ruby/examples/avatars/get-credit-card.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ avatars = Avatars.new(client) response = avatars.get_credit_card(code: 'amex') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/avatars/get-favicon.md b/docs/examples/1.4.x/server-ruby/examples/avatars/get-favicon.md index c995fb2036..b1a574522a 100644 --- a/docs/examples/1.4.x/server-ruby/examples/avatars/get-favicon.md +++ b/docs/examples/1.4.x/server-ruby/examples/avatars/get-favicon.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ avatars = Avatars.new(client) response = avatars.get_favicon(url: 'https://example.com') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/avatars/get-flag.md b/docs/examples/1.4.x/server-ruby/examples/avatars/get-flag.md index 7633756425..ed53143b2f 100644 --- a/docs/examples/1.4.x/server-ruby/examples/avatars/get-flag.md +++ b/docs/examples/1.4.x/server-ruby/examples/avatars/get-flag.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ avatars = Avatars.new(client) response = avatars.get_flag(code: 'af') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/avatars/get-image.md b/docs/examples/1.4.x/server-ruby/examples/avatars/get-image.md index 4ea52a60e4..59fb6c36c5 100644 --- a/docs/examples/1.4.x/server-ruby/examples/avatars/get-image.md +++ b/docs/examples/1.4.x/server-ruby/examples/avatars/get-image.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ avatars = Avatars.new(client) response = avatars.get_image(url: 'https://example.com') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/avatars/get-initials.md b/docs/examples/1.4.x/server-ruby/examples/avatars/get-initials.md index 62a8ff855d..93263baccb 100644 --- a/docs/examples/1.4.x/server-ruby/examples/avatars/get-initials.md +++ b/docs/examples/1.4.x/server-ruby/examples/avatars/get-initials.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ avatars = Avatars.new(client) response = avatars.get_initials() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/avatars/get-q-r.md b/docs/examples/1.4.x/server-ruby/examples/avatars/get-q-r.md index 03a1f14aa9..bdaa14f892 100644 --- a/docs/examples/1.4.x/server-ruby/examples/avatars/get-q-r.md +++ b/docs/examples/1.4.x/server-ruby/examples/avatars/get-q-r.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ avatars = Avatars.new(client) response = avatars.get_qr(text: '[TEXT]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-boolean-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-boolean-attribute.md index d617d8e322..524c01a2e3 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-boolean-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-boolean-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_boolean_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-collection.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-collection.md index d27e43ad66..396fa2b6cc 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-collection.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-collection.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_collection(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-datetime-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-datetime-attribute.md index 127d2ac2d7..7e306bf5e2 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-datetime-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-datetime-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_datetime_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-document.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-document.md index 8177fc4b3b..e1faebd92e 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-document.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-document.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_document(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', document_id: '[DOCUMENT_ID]', data: {}) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-email-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-email-attribute.md index c7e1aac86b..c81b176ac7 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-email-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-email-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_email_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-enum-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-enum-attribute.md index 36f8ee06d4..1505c9db20 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-enum-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-enum-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_enum_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', elements: [], required: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-float-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-float-attribute.md index 771d281ccf..b1d7e981df 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-float-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-float-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_float_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-index.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-index.md index 5488e36f74..456e293f6e 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-index.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-index.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_index(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', type: 'key', attributes: []) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-integer-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-integer-attribute.md index 40649c25f8..55079da7c6 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-integer-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-integer-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_integer_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-ip-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-ip-attribute.md index ef9e0feba5..c414d631d5 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-ip-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-ip-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_ip_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-relationship-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-relationship-attribute.md index 573ab10ddc..73ca419ff8 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-relationship-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-relationship-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_relationship_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', related_collection_id: '[RELATED_COLLECTION_ID]', type: 'oneToOne') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-string-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-string-attribute.md index 9901c84b02..9027496caa 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-string-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-string-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_string_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', size: 1, required: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create-url-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/create-url-attribute.md index 475421fe53..6c7d356e8b 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create-url-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create-url-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create_url_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/create.md b/docs/examples/1.4.x/server-ruby/examples/databases/create.md index 1ce19d3125..494ca1503b 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/create.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/create.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.create(database_id: '[DATABASE_ID]', name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/delete-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/delete-attribute.md index ae2e32d8bd..de35bb2756 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/delete-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/delete-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.delete_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/delete-collection.md b/docs/examples/1.4.x/server-ruby/examples/databases/delete-collection.md index f118667b6e..07e3a439f2 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/delete-collection.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/delete-collection.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.delete_collection(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/delete-document.md b/docs/examples/1.4.x/server-ruby/examples/databases/delete-document.md index 0b7baa89ed..0e94edd3fa 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/delete-document.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/delete-document.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.delete_document(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', document_id: '[DOCUMENT_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/delete-index.md b/docs/examples/1.4.x/server-ruby/examples/databases/delete-index.md index 829e606135..63436e5700 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/delete-index.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/delete-index.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.delete_index(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/delete.md b/docs/examples/1.4.x/server-ruby/examples/databases/delete.md index 7e0c4211f1..ee34774cee 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/delete.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/delete.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.delete(database_id: '[DATABASE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/get-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/get-attribute.md index 525c8f14cd..585c311ff7 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/get-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/get-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.get_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/get-collection.md b/docs/examples/1.4.x/server-ruby/examples/databases/get-collection.md index 897e40d109..3c53903ebb 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/get-collection.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/get-collection.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.get_collection(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/get-document.md b/docs/examples/1.4.x/server-ruby/examples/databases/get-document.md index d5853aba56..342812e659 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/get-document.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/get-document.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.get_document(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', document_id: '[DOCUMENT_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/get-index.md b/docs/examples/1.4.x/server-ruby/examples/databases/get-index.md index 3217507541..c32ec0565d 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/get-index.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/get-index.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.get_index(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/get.md b/docs/examples/1.4.x/server-ruby/examples/databases/get.md index ced51d6ad1..ab1f098b6d 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/get.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/get.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.get(database_id: '[DATABASE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/list-attributes.md b/docs/examples/1.4.x/server-ruby/examples/databases/list-attributes.md index 1b57bda217..183d99d193 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/list-attributes.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/list-attributes.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.list_attributes(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/list-collections.md b/docs/examples/1.4.x/server-ruby/examples/databases/list-collections.md index 3d646c47e7..472800a4c1 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/list-collections.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/list-collections.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.list_collections(database_id: '[DATABASE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/list-documents.md b/docs/examples/1.4.x/server-ruby/examples/databases/list-documents.md index 6f57bb150c..920946501d 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/list-documents.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/list-documents.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.list_documents(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/list-indexes.md b/docs/examples/1.4.x/server-ruby/examples/databases/list-indexes.md index d64b3bfc68..63b1e75ec2 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/list-indexes.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/list-indexes.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.list_indexes(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/list.md b/docs/examples/1.4.x/server-ruby/examples/databases/list.md index 84baa108a3..eb810096f5 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/list.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/list.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.list() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-boolean-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-boolean-attribute.md index 7a7f01a854..020d97689c 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-boolean-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-boolean-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_boolean_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false, default: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-collection.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-collection.md index eee4058531..e185aef2ae 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-collection.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-collection.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_collection(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-datetime-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-datetime-attribute.md index c9f9aa759b..2e931947d0 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-datetime-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-datetime-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_datetime_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false, default: '') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-document.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-document.md index dbe1b0d71a..8b7680b85f 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-document.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-document.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_document(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', document_id: '[DOCUMENT_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-email-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-email-attribute.md index de2d4c0749..3d0dc5c8a4 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-email-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-email-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_email_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false, default: 'email@example.com') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-enum-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-enum-attribute.md index e248a5bcf7..feb3871125 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-enum-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-enum-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_enum_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', elements: [], required: false, default: '[DEFAULT]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-float-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-float-attribute.md index 555355d6cb..ec14217766 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-float-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-float-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_float_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false, min: null, max: null, default: null) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-integer-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-integer-attribute.md index 154daf330e..976bad938d 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-integer-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-integer-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_integer_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false, min: null, max: null, default: null) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-ip-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-ip-attribute.md index a6fe4d95bc..24d96b9d89 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-ip-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-ip-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_ip_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false, default: '') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-relationship-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-relationship-attribute.md index 9c51daa3a5..345aea9deb 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-relationship-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-relationship-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_relationship_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-string-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-string-attribute.md index e5f2f8f09d..1801723c16 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-string-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-string-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_string_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false, default: '[DEFAULT]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update-url-attribute.md b/docs/examples/1.4.x/server-ruby/examples/databases/update-url-attribute.md index da21dcbfb0..429f690ed1 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update-url-attribute.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update-url-attribute.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update_url_attribute(database_id: '[DATABASE_ID]', collection_id: '[COLLECTION_ID]', key: '', required: false, default: 'https://example.com') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/databases/update.md b/docs/examples/1.4.x/server-ruby/examples/databases/update.md index 0900ab3428..f727f582d1 100644 --- a/docs/examples/1.4.x/server-ruby/examples/databases/update.md +++ b/docs/examples/1.4.x/server-ruby/examples/databases/update.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ databases = Databases.new(client) response = databases.update(database_id: '[DATABASE_ID]', name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/create-build.md b/docs/examples/1.4.x/server-ruby/examples/functions/create-build.md index 9c7397bf26..344f92a9ec 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/create-build.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/create-build.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.create_build(function_id: '[FUNCTION_ID]', deployment_id: '[DEPLOYMENT_ID]', build_id: '[BUILD_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/create-deployment.md b/docs/examples/1.4.x/server-ruby/examples/functions/create-deployment.md index 33a74f8ee6..491a80846e 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/create-deployment.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/create-deployment.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.create_deployment(function_id: '[FUNCTION_ID]', code: InputFile.from_path('dir/file.png'), activate: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/create-execution.md b/docs/examples/1.4.x/server-ruby/examples/functions/create-execution.md index 97f4d74ba1..f14283b9d3 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/create-execution.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/create-execution.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.create_execution(function_id: '[FUNCTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/create-variable.md b/docs/examples/1.4.x/server-ruby/examples/functions/create-variable.md index 003fb4743a..ffb03ce042 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/create-variable.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/create-variable.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.create_variable(function_id: '[FUNCTION_ID]', key: '[KEY]', value: '[VALUE]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/create.md b/docs/examples/1.4.x/server-ruby/examples/functions/create.md index af0f686cc0..92ced89620 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/create.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/create.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.create(function_id: '[FUNCTION_ID]', name: '[NAME]', runtime: 'node-18.0') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/delete-deployment.md b/docs/examples/1.4.x/server-ruby/examples/functions/delete-deployment.md index 64feea1370..49004f60de 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/delete-deployment.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/delete-deployment.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.delete_deployment(function_id: '[FUNCTION_ID]', deployment_id: '[DEPLOYMENT_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/delete-variable.md b/docs/examples/1.4.x/server-ruby/examples/functions/delete-variable.md index 598e694141..ef37dd5abb 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/delete-variable.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/delete-variable.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.delete_variable(function_id: '[FUNCTION_ID]', variable_id: '[VARIABLE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/delete.md b/docs/examples/1.4.x/server-ruby/examples/functions/delete.md index 48ea5c7a73..5e7d622fb7 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/delete.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/delete.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.delete(function_id: '[FUNCTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/download-deployment.md b/docs/examples/1.4.x/server-ruby/examples/functions/download-deployment.md index 7bcb4dda58..f6186a3037 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/download-deployment.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/download-deployment.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.download_deployment(function_id: '[FUNCTION_ID]', deployment_id: '[DEPLOYMENT_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/get-deployment.md b/docs/examples/1.4.x/server-ruby/examples/functions/get-deployment.md index c19163e7f5..35c2f01108 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/get-deployment.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/get-deployment.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.get_deployment(function_id: '[FUNCTION_ID]', deployment_id: '[DEPLOYMENT_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/get-execution.md b/docs/examples/1.4.x/server-ruby/examples/functions/get-execution.md index 3deee91574..c464fbc2fc 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/get-execution.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/get-execution.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.get_execution(function_id: '[FUNCTION_ID]', execution_id: '[EXECUTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/get-variable.md b/docs/examples/1.4.x/server-ruby/examples/functions/get-variable.md index 835fc30d81..7559482af9 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/get-variable.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/get-variable.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.get_variable(function_id: '[FUNCTION_ID]', variable_id: '[VARIABLE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/get.md b/docs/examples/1.4.x/server-ruby/examples/functions/get.md index cfcf8411e5..54d61840c9 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/get.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/get.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.get(function_id: '[FUNCTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/list-deployments.md b/docs/examples/1.4.x/server-ruby/examples/functions/list-deployments.md index 539301c4bb..d11e42e8d6 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/list-deployments.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/list-deployments.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.list_deployments(function_id: '[FUNCTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/list-executions.md b/docs/examples/1.4.x/server-ruby/examples/functions/list-executions.md index c0c1f46cf1..792d606f9b 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/list-executions.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/list-executions.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.list_executions(function_id: '[FUNCTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/list-runtimes.md b/docs/examples/1.4.x/server-ruby/examples/functions/list-runtimes.md index b679da2d8b..a8c8fea4a3 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/list-runtimes.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/list-runtimes.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.list_runtimes() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/list-variables.md b/docs/examples/1.4.x/server-ruby/examples/functions/list-variables.md index 4e5bfab891..d7d088890a 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/list-variables.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/list-variables.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.list_variables(function_id: '[FUNCTION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/list.md b/docs/examples/1.4.x/server-ruby/examples/functions/list.md index 2cc71a5c04..06f9ae7ae3 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/list.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/list.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.list() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/update-deployment.md b/docs/examples/1.4.x/server-ruby/examples/functions/update-deployment.md index c9d9f49d63..c768e662ec 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/update-deployment.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/update-deployment.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.update_deployment(function_id: '[FUNCTION_ID]', deployment_id: '[DEPLOYMENT_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/update-variable.md b/docs/examples/1.4.x/server-ruby/examples/functions/update-variable.md index 28d7651a46..07be591e9e 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/update-variable.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/update-variable.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.update_variable(function_id: '[FUNCTION_ID]', variable_id: '[VARIABLE_ID]', key: '[KEY]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/functions/update.md b/docs/examples/1.4.x/server-ruby/examples/functions/update.md index 8503a50724..f45fc1b312 100644 --- a/docs/examples/1.4.x/server-ruby/examples/functions/update.md +++ b/docs/examples/1.4.x/server-ruby/examples/functions/update.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ functions = Functions.new(client) response = functions.update(function_id: '[FUNCTION_ID]', name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/graphql/mutation.md b/docs/examples/1.4.x/server-ruby/examples/graphql/mutation.md index 234dfe6202..ad4d03770b 100644 --- a/docs/examples/1.4.x/server-ruby/examples/graphql/mutation.md +++ b/docs/examples/1.4.x/server-ruby/examples/graphql/mutation.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ graphql = Graphql.new(client) response = graphql.mutation(query: {}) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/graphql/query.md b/docs/examples/1.4.x/server-ruby/examples/graphql/query.md index 1304213caf..a11b2881af 100644 --- a/docs/examples/1.4.x/server-ruby/examples/graphql/query.md +++ b/docs/examples/1.4.x/server-ruby/examples/graphql/query.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ graphql = Graphql.new(client) response = graphql.query(query: {}) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-antivirus.md b/docs/examples/1.4.x/server-ruby/examples/health/get-antivirus.md index b1f16bb0b9..fbd9049441 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-antivirus.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-antivirus.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_antivirus() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-cache.md b/docs/examples/1.4.x/server-ruby/examples/health/get-cache.md index 4fb77ec68d..3de43bf763 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-cache.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-cache.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_cache() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-d-b.md b/docs/examples/1.4.x/server-ruby/examples/health/get-d-b.md index 3317acd6fa..8d37dec0bc 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-d-b.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-d-b.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_db() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-pub-sub.md b/docs/examples/1.4.x/server-ruby/examples/health/get-pub-sub.md index 98e834be9e..dac20b2231 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-pub-sub.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-pub-sub.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_pub_sub() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-builds.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-builds.md index 56ece06c36..4fad17f30a 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-builds.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-builds.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue_builds() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-certificates.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-certificates.md index a4c5c7016b..42e0863f9a 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-certificates.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-certificates.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue_certificates() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-databases.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-databases.md index 74c879ce1b..7536fc7768 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-databases.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-databases.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue_databases() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-deletes.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-deletes.md index 6110cce045..ae96a911e4 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-deletes.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-deletes.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue_deletes() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-functions.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-functions.md index 8edb310a5e..e350779f79 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-functions.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-functions.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue_functions() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-logs.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-logs.md index 74823fcd6c..3fd4570d2f 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-logs.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-logs.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue_logs() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-mails.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-mails.md index c6d8263e8f..cb184182f5 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-mails.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-mails.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue_mails() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-messaging.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-messaging.md index 579a58fa08..936d9fdffa 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-messaging.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-messaging.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue_messaging() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-migrations.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-migrations.md index 5c2f40e567..2d8a32b6a7 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-migrations.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-migrations.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue_migrations() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-webhooks.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-webhooks.md index 53b7f33cfe..0c10a19211 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue-webhooks.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue-webhooks.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue_webhooks() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-queue.md b/docs/examples/1.4.x/server-ruby/examples/health/get-queue.md index dfa3c966a4..e67a90b11c 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-queue.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-queue.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_queue() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-storage-local.md b/docs/examples/1.4.x/server-ruby/examples/health/get-storage-local.md index 3a85bd3e12..4cede4153c 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-storage-local.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-storage-local.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_storage_local() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get-time.md b/docs/examples/1.4.x/server-ruby/examples/health/get-time.md index 9956a33d1f..21750d7b96 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get-time.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get-time.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get_time() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/health/get.md b/docs/examples/1.4.x/server-ruby/examples/health/get.md index d23a9ad675..395b039cec 100644 --- a/docs/examples/1.4.x/server-ruby/examples/health/get.md +++ b/docs/examples/1.4.x/server-ruby/examples/health/get.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ health = Health.new(client) response = health.get() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/locale/get.md b/docs/examples/1.4.x/server-ruby/examples/locale/get.md index e5922f4e7f..6a4ecb7df2 100644 --- a/docs/examples/1.4.x/server-ruby/examples/locale/get.md +++ b/docs/examples/1.4.x/server-ruby/examples/locale/get.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ locale = Locale.new(client) response = locale.get() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/locale/list-codes.md b/docs/examples/1.4.x/server-ruby/examples/locale/list-codes.md index 3463252405..d32be91776 100644 --- a/docs/examples/1.4.x/server-ruby/examples/locale/list-codes.md +++ b/docs/examples/1.4.x/server-ruby/examples/locale/list-codes.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ locale = Locale.new(client) response = locale.list_codes() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/locale/list-continents.md b/docs/examples/1.4.x/server-ruby/examples/locale/list-continents.md index 0416baf77a..c154e3e74a 100644 --- a/docs/examples/1.4.x/server-ruby/examples/locale/list-continents.md +++ b/docs/examples/1.4.x/server-ruby/examples/locale/list-continents.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ locale = Locale.new(client) response = locale.list_continents() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/locale/list-countries-e-u.md b/docs/examples/1.4.x/server-ruby/examples/locale/list-countries-e-u.md index 7faa85143c..dbbeeace38 100644 --- a/docs/examples/1.4.x/server-ruby/examples/locale/list-countries-e-u.md +++ b/docs/examples/1.4.x/server-ruby/examples/locale/list-countries-e-u.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ locale = Locale.new(client) response = locale.list_countries_eu() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/locale/list-countries-phones.md b/docs/examples/1.4.x/server-ruby/examples/locale/list-countries-phones.md index 1194f27e0c..d44f0c6a7f 100644 --- a/docs/examples/1.4.x/server-ruby/examples/locale/list-countries-phones.md +++ b/docs/examples/1.4.x/server-ruby/examples/locale/list-countries-phones.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ locale = Locale.new(client) response = locale.list_countries_phones() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/locale/list-countries.md b/docs/examples/1.4.x/server-ruby/examples/locale/list-countries.md index b78fba47fd..0431a5444e 100644 --- a/docs/examples/1.4.x/server-ruby/examples/locale/list-countries.md +++ b/docs/examples/1.4.x/server-ruby/examples/locale/list-countries.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ locale = Locale.new(client) response = locale.list_countries() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/locale/list-currencies.md b/docs/examples/1.4.x/server-ruby/examples/locale/list-currencies.md index 14b2722ae8..610b45901c 100644 --- a/docs/examples/1.4.x/server-ruby/examples/locale/list-currencies.md +++ b/docs/examples/1.4.x/server-ruby/examples/locale/list-currencies.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ locale = Locale.new(client) response = locale.list_currencies() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/locale/list-languages.md b/docs/examples/1.4.x/server-ruby/examples/locale/list-languages.md index 4d88bc4723..dc09d481c2 100644 --- a/docs/examples/1.4.x/server-ruby/examples/locale/list-languages.md +++ b/docs/examples/1.4.x/server-ruby/examples/locale/list-languages.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ locale = Locale.new(client) response = locale.list_languages() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/create-bucket.md b/docs/examples/1.4.x/server-ruby/examples/storage/create-bucket.md index d6299db79f..be1229ddd6 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/create-bucket.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/create-bucket.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.create_bucket(bucket_id: '[BUCKET_ID]', name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/create-file.md b/docs/examples/1.4.x/server-ruby/examples/storage/create-file.md index 851ed57a48..de207f1b64 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/create-file.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/create-file.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.create_file(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]', file: InputFile.from_path('dir/file.png')) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/delete-bucket.md b/docs/examples/1.4.x/server-ruby/examples/storage/delete-bucket.md index 3fd51bef5e..bcb1b23a42 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/delete-bucket.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/delete-bucket.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.delete_bucket(bucket_id: '[BUCKET_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/delete-file.md b/docs/examples/1.4.x/server-ruby/examples/storage/delete-file.md index 4d0e3fcbff..4f0585773f 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/delete-file.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/delete-file.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.delete_file(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/get-bucket.md b/docs/examples/1.4.x/server-ruby/examples/storage/get-bucket.md index ba88debc61..1ee6843c8c 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/get-bucket.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/get-bucket.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.get_bucket(bucket_id: '[BUCKET_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/get-file-download.md b/docs/examples/1.4.x/server-ruby/examples/storage/get-file-download.md index a22f4cb68f..4fe5798e6b 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/get-file-download.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/get-file-download.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.get_file_download(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/get-file-preview.md b/docs/examples/1.4.x/server-ruby/examples/storage/get-file-preview.md index c5b7c06db1..e76168c3ea 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/get-file-preview.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/get-file-preview.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.get_file_preview(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/get-file-view.md b/docs/examples/1.4.x/server-ruby/examples/storage/get-file-view.md index c05770b999..91dcbb5905 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/get-file-view.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/get-file-view.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.get_file_view(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/get-file.md b/docs/examples/1.4.x/server-ruby/examples/storage/get-file.md index a33835c9e2..2aea60a8da 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/get-file.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/get-file.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.get_file(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/list-buckets.md b/docs/examples/1.4.x/server-ruby/examples/storage/list-buckets.md index 7009d0a105..3201633236 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/list-buckets.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/list-buckets.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.list_buckets() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/list-files.md b/docs/examples/1.4.x/server-ruby/examples/storage/list-files.md index 196831a1b3..247426cc11 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/list-files.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/list-files.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.list_files(bucket_id: '[BUCKET_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/update-bucket.md b/docs/examples/1.4.x/server-ruby/examples/storage/update-bucket.md index f990ab3566..97238c1eb0 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/update-bucket.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/update-bucket.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.update_bucket(bucket_id: '[BUCKET_ID]', name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/storage/update-file.md b/docs/examples/1.4.x/server-ruby/examples/storage/update-file.md index 8108d3cc28..2a1fe43bc9 100644 --- a/docs/examples/1.4.x/server-ruby/examples/storage/update-file.md +++ b/docs/examples/1.4.x/server-ruby/examples/storage/update-file.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ storage = Storage.new(client) response = storage.update_file(bucket_id: '[BUCKET_ID]', file_id: '[FILE_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/create-membership.md b/docs/examples/1.4.x/server-ruby/examples/teams/create-membership.md index b96333d6c6..cb2ddfb056 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/create-membership.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/create-membership.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.create_membership(team_id: '[TEAM_ID]', roles: []) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/create.md b/docs/examples/1.4.x/server-ruby/examples/teams/create.md index d2dff77f41..5f0f1f4827 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/create.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/create.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.create(team_id: '[TEAM_ID]', name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/delete-membership.md b/docs/examples/1.4.x/server-ruby/examples/teams/delete-membership.md index d41175d4a8..2b78ca8ce1 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/delete-membership.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/delete-membership.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.delete_membership(team_id: '[TEAM_ID]', membership_id: '[MEMBERSHIP_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/delete.md b/docs/examples/1.4.x/server-ruby/examples/teams/delete.md index c1b9b8d8bb..4d71b38ea9 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/delete.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/delete.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.delete(team_id: '[TEAM_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/get-membership.md b/docs/examples/1.4.x/server-ruby/examples/teams/get-membership.md index b06101dd84..864ff8b8ec 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/get-membership.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/get-membership.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.get_membership(team_id: '[TEAM_ID]', membership_id: '[MEMBERSHIP_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/get-prefs.md b/docs/examples/1.4.x/server-ruby/examples/teams/get-prefs.md index d4317e8dba..22135af9ff 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/get-prefs.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/get-prefs.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.get_prefs(team_id: '[TEAM_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/get.md b/docs/examples/1.4.x/server-ruby/examples/teams/get.md index 7cee601b56..a0e2aeb2ab 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/get.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/get.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.get(team_id: '[TEAM_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/list-memberships.md b/docs/examples/1.4.x/server-ruby/examples/teams/list-memberships.md index 885a857d72..c742c4520b 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/list-memberships.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/list-memberships.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.list_memberships(team_id: '[TEAM_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/list.md b/docs/examples/1.4.x/server-ruby/examples/teams/list.md index e8b581fd91..9f12e7f9ed 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/list.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/list.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.list() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/update-membership-status.md b/docs/examples/1.4.x/server-ruby/examples/teams/update-membership-status.md index aaf52cbb9c..6c47214abc 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/update-membership-status.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/update-membership-status.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.update_membership_status(team_id: '[TEAM_ID]', membership_id: '[MEMBERSHIP_ID]', user_id: '[USER_ID]', secret: '[SECRET]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/update-membership.md b/docs/examples/1.4.x/server-ruby/examples/teams/update-membership.md index 4117aa0aed..d94291bcd1 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/update-membership.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/update-membership.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.update_membership(team_id: '[TEAM_ID]', membership_id: '[MEMBERSHIP_ID]', roles: []) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/update-name.md b/docs/examples/1.4.x/server-ruby/examples/teams/update-name.md index 41766d6872..61914f7d5a 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/update-name.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/update-name.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.update_name(team_id: '[TEAM_ID]', name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/teams/update-prefs.md b/docs/examples/1.4.x/server-ruby/examples/teams/update-prefs.md index 7656edcd8b..20e3942131 100644 --- a/docs/examples/1.4.x/server-ruby/examples/teams/update-prefs.md +++ b/docs/examples/1.4.x/server-ruby/examples/teams/update-prefs.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ teams = Teams.new(client) response = teams.update_prefs(team_id: '[TEAM_ID]', prefs: {}) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/create-argon2user.md b/docs/examples/1.4.x/server-ruby/examples/users/create-argon2user.md index 9c4fc9ea97..7249612c5f 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/create-argon2user.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/create-argon2user.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.create_argon2_user(user_id: '[USER_ID]', email: 'email@example.com', password: 'password') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/create-bcrypt-user.md b/docs/examples/1.4.x/server-ruby/examples/users/create-bcrypt-user.md index 700fe49d12..1d253f687c 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/create-bcrypt-user.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/create-bcrypt-user.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.create_bcrypt_user(user_id: '[USER_ID]', email: 'email@example.com', password: 'password') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/create-m-d5user.md b/docs/examples/1.4.x/server-ruby/examples/users/create-m-d5user.md index a7f9c4f7a2..cc36572cf4 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/create-m-d5user.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/create-m-d5user.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.create_md5_user(user_id: '[USER_ID]', email: 'email@example.com', password: 'password') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/create-p-h-pass-user.md b/docs/examples/1.4.x/server-ruby/examples/users/create-p-h-pass-user.md index d7d8ba1933..30004fc360 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/create-p-h-pass-user.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/create-p-h-pass-user.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.create_ph_pass_user(user_id: '[USER_ID]', email: 'email@example.com', password: 'password') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/create-s-h-a-user.md b/docs/examples/1.4.x/server-ruby/examples/users/create-s-h-a-user.md index 2d37fc3bfa..809c313ca6 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/create-s-h-a-user.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/create-s-h-a-user.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.create_sha_user(user_id: '[USER_ID]', email: 'email@example.com', password: 'password') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/create-scrypt-modified-user.md b/docs/examples/1.4.x/server-ruby/examples/users/create-scrypt-modified-user.md index cec9dbb277..46b17d1338 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/create-scrypt-modified-user.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/create-scrypt-modified-user.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.create_scrypt_modified_user(user_id: '[USER_ID]', email: 'email@example.com', password: 'password', password_salt: '[PASSWORD_SALT]', password_salt_separator: '[PASSWORD_SALT_SEPARATOR]', password_signer_key: '[PASSWORD_SIGNER_KEY]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/create-scrypt-user.md b/docs/examples/1.4.x/server-ruby/examples/users/create-scrypt-user.md index 94a7af53d2..c84f7e7bab 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/create-scrypt-user.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/create-scrypt-user.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.create_scrypt_user(user_id: '[USER_ID]', email: 'email@example.com', password: 'password', password_salt: '[PASSWORD_SALT]', password_cpu: null, password_memory: null, password_parallel: null, password_length: null) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/create.md b/docs/examples/1.4.x/server-ruby/examples/users/create.md index b04fd5025f..a73a489294 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/create.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/create.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.create(user_id: '[USER_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/delete-identity.md b/docs/examples/1.4.x/server-ruby/examples/users/delete-identity.md index b1405ddf2a..dd14abb41c 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/delete-identity.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/delete-identity.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.delete_identity(identity_id: '[IDENTITY_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/delete-session.md b/docs/examples/1.4.x/server-ruby/examples/users/delete-session.md index 3b7918ca1f..da0362bfcb 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/delete-session.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/delete-session.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.delete_session(user_id: '[USER_ID]', session_id: '[SESSION_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/delete-sessions.md b/docs/examples/1.4.x/server-ruby/examples/users/delete-sessions.md index 6772e3fe10..647916fdf0 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/delete-sessions.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/delete-sessions.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.delete_sessions(user_id: '[USER_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/delete.md b/docs/examples/1.4.x/server-ruby/examples/users/delete.md index 62585db404..b128e57c69 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/delete.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/delete.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.delete(user_id: '[USER_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/get-prefs.md b/docs/examples/1.4.x/server-ruby/examples/users/get-prefs.md index a4c4764bef..984d1e60c3 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/get-prefs.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/get-prefs.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.get_prefs(user_id: '[USER_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/get.md b/docs/examples/1.4.x/server-ruby/examples/users/get.md index a731ada955..f0ace233a9 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/get.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/get.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.get(user_id: '[USER_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/list-identities.md b/docs/examples/1.4.x/server-ruby/examples/users/list-identities.md index c087063269..fc18bc9bfe 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/list-identities.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/list-identities.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.list_identities() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/list-logs.md b/docs/examples/1.4.x/server-ruby/examples/users/list-logs.md index fa8586eeff..4bea6d152a 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/list-logs.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/list-logs.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.list_logs(user_id: '[USER_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/list-memberships.md b/docs/examples/1.4.x/server-ruby/examples/users/list-memberships.md index 693e6b9f2c..9465441397 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/list-memberships.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/list-memberships.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.list_memberships(user_id: '[USER_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/list-sessions.md b/docs/examples/1.4.x/server-ruby/examples/users/list-sessions.md index 765789a0da..27f04e1bb0 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/list-sessions.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/list-sessions.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.list_sessions(user_id: '[USER_ID]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/list.md b/docs/examples/1.4.x/server-ruby/examples/users/list.md index 8d5daf21f8..3672217a75 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/list.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/list.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.list() -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/update-email-verification.md b/docs/examples/1.4.x/server-ruby/examples/users/update-email-verification.md index 72ec0195dc..4ef455f9d4 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/update-email-verification.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/update-email-verification.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.update_email_verification(user_id: '[USER_ID]', email_verification: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/update-email.md b/docs/examples/1.4.x/server-ruby/examples/users/update-email.md index a3a7dbde34..93e963e325 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/update-email.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/update-email.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.update_email(user_id: '[USER_ID]', email: 'email@example.com') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/update-labels.md b/docs/examples/1.4.x/server-ruby/examples/users/update-labels.md index f94cd1bcaf..7947ef6737 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/update-labels.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/update-labels.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.update_labels(user_id: '[USER_ID]', labels: []) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/update-name.md b/docs/examples/1.4.x/server-ruby/examples/users/update-name.md index fbf00bb51b..54a3edfb09 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/update-name.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/update-name.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.update_name(user_id: '[USER_ID]', name: '[NAME]') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/update-password.md b/docs/examples/1.4.x/server-ruby/examples/users/update-password.md index 7310a2a0cc..2623267f55 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/update-password.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/update-password.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.update_password(user_id: '[USER_ID]', password: '') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/update-phone-verification.md b/docs/examples/1.4.x/server-ruby/examples/users/update-phone-verification.md index 995a32a4bf..ec96581e21 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/update-phone-verification.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/update-phone-verification.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.update_phone_verification(user_id: '[USER_ID]', phone_verification: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/update-phone.md b/docs/examples/1.4.x/server-ruby/examples/users/update-phone.md index f1e74b4a9e..430f03aaed 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/update-phone.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/update-phone.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.update_phone(user_id: '[USER_ID]', number: '+12065550100') -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/update-prefs.md b/docs/examples/1.4.x/server-ruby/examples/users/update-prefs.md index 9845cdf0d5..eb17d8f500 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/update-prefs.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/update-prefs.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.update_prefs(user_id: '[USER_ID]', prefs: {}) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/examples/1.4.x/server-ruby/examples/users/update-status.md b/docs/examples/1.4.x/server-ruby/examples/users/update-status.md index 8a82948c76..34365cf58e 100644 --- a/docs/examples/1.4.x/server-ruby/examples/users/update-status.md +++ b/docs/examples/1.4.x/server-ruby/examples/users/update-status.md @@ -1,4 +1,4 @@ -require 'Appwrite' +require 'appwrite' include Appwrite @@ -11,4 +11,4 @@ users = Users.new(client) response = users.update_status(user_id: '[USER_ID]', status: false) -puts response.inspect \ No newline at end of file +puts response.inspect diff --git a/docs/references/messaging/create-apns-provider.md b/docs/references/messaging/create-apns-provider.md new file mode 100644 index 0000000000..daa025df0a --- /dev/null +++ b/docs/references/messaging/create-apns-provider.md @@ -0,0 +1 @@ +Create a new Apple Push Notification service provider. \ No newline at end of file diff --git a/docs/references/messaging/create-email.md b/docs/references/messaging/create-email.md new file mode 100644 index 0000000000..e5d038ada4 --- /dev/null +++ b/docs/references/messaging/create-email.md @@ -0,0 +1 @@ +Create a new email message. \ No newline at end of file diff --git a/docs/references/messaging/create-fcm-provider.md b/docs/references/messaging/create-fcm-provider.md new file mode 100644 index 0000000000..d4ce0a6714 --- /dev/null +++ b/docs/references/messaging/create-fcm-provider.md @@ -0,0 +1 @@ +Create a new Firebase Cloud Messaging provider. \ No newline at end of file diff --git a/docs/references/messaging/create-mailgun-provider.md b/docs/references/messaging/create-mailgun-provider.md new file mode 100644 index 0000000000..34c243f807 --- /dev/null +++ b/docs/references/messaging/create-mailgun-provider.md @@ -0,0 +1 @@ +Create a new Mailgun provider. \ No newline at end of file diff --git a/docs/references/messaging/create-msg91-provider.md b/docs/references/messaging/create-msg91-provider.md new file mode 100644 index 0000000000..47040ee38f --- /dev/null +++ b/docs/references/messaging/create-msg91-provider.md @@ -0,0 +1 @@ +Create a new MSG91 provider. \ No newline at end of file diff --git a/docs/references/messaging/create-push.md b/docs/references/messaging/create-push.md new file mode 100644 index 0000000000..80627e7150 --- /dev/null +++ b/docs/references/messaging/create-push.md @@ -0,0 +1 @@ +Create a new push notification. \ No newline at end of file diff --git a/docs/references/messaging/create-sendgrid-provider.md b/docs/references/messaging/create-sendgrid-provider.md new file mode 100644 index 0000000000..8bd88960b8 --- /dev/null +++ b/docs/references/messaging/create-sendgrid-provider.md @@ -0,0 +1 @@ +Create a new Sendgrid provider. \ No newline at end of file diff --git a/docs/references/messaging/create-sms.md b/docs/references/messaging/create-sms.md new file mode 100644 index 0000000000..ec8a84531a --- /dev/null +++ b/docs/references/messaging/create-sms.md @@ -0,0 +1 @@ +Create a new SMS message. \ No newline at end of file diff --git a/docs/references/messaging/create-smtp-provider.md b/docs/references/messaging/create-smtp-provider.md new file mode 100644 index 0000000000..58be8dadc5 --- /dev/null +++ b/docs/references/messaging/create-smtp-provider.md @@ -0,0 +1 @@ +Create a new SMTP provider. \ No newline at end of file diff --git a/docs/references/messaging/create-subscriber.md b/docs/references/messaging/create-subscriber.md new file mode 100644 index 0000000000..1e9763889c --- /dev/null +++ b/docs/references/messaging/create-subscriber.md @@ -0,0 +1 @@ +Create a new subscriber. \ No newline at end of file diff --git a/docs/references/messaging/create-telesign-provider.md b/docs/references/messaging/create-telesign-provider.md new file mode 100644 index 0000000000..efe8da8169 --- /dev/null +++ b/docs/references/messaging/create-telesign-provider.md @@ -0,0 +1 @@ +Create a new Telesign provider. \ No newline at end of file diff --git a/docs/references/messaging/create-textmagic-provider.md b/docs/references/messaging/create-textmagic-provider.md new file mode 100644 index 0000000000..67a1f0b998 --- /dev/null +++ b/docs/references/messaging/create-textmagic-provider.md @@ -0,0 +1 @@ +Create a new Textmagic provider. \ No newline at end of file diff --git a/docs/references/messaging/create-topic.md b/docs/references/messaging/create-topic.md new file mode 100644 index 0000000000..a9660cbf7e --- /dev/null +++ b/docs/references/messaging/create-topic.md @@ -0,0 +1 @@ +Create a new topic. \ No newline at end of file diff --git a/docs/references/messaging/create-twilio-provider.md b/docs/references/messaging/create-twilio-provider.md new file mode 100644 index 0000000000..d359171605 --- /dev/null +++ b/docs/references/messaging/create-twilio-provider.md @@ -0,0 +1 @@ +Create a new Twilio provider. \ No newline at end of file diff --git a/docs/references/messaging/create-vonage-provider.md b/docs/references/messaging/create-vonage-provider.md new file mode 100644 index 0000000000..c85f8eb4fc --- /dev/null +++ b/docs/references/messaging/create-vonage-provider.md @@ -0,0 +1 @@ +Create a new Vonage provider. \ No newline at end of file diff --git a/docs/references/messaging/delete-provider.md b/docs/references/messaging/delete-provider.md new file mode 100644 index 0000000000..8fbacad910 --- /dev/null +++ b/docs/references/messaging/delete-provider.md @@ -0,0 +1 @@ +Delete a provider by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/delete-subscriber.md b/docs/references/messaging/delete-subscriber.md new file mode 100644 index 0000000000..96a03840df --- /dev/null +++ b/docs/references/messaging/delete-subscriber.md @@ -0,0 +1 @@ +Delete a subscriber by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/delete-topic.md b/docs/references/messaging/delete-topic.md new file mode 100644 index 0000000000..f92ae94707 --- /dev/null +++ b/docs/references/messaging/delete-topic.md @@ -0,0 +1 @@ +Delete a topic by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/get-message.md b/docs/references/messaging/get-message.md new file mode 100644 index 0000000000..8399aabc27 --- /dev/null +++ b/docs/references/messaging/get-message.md @@ -0,0 +1 @@ +Get a message by its unique ID. diff --git a/docs/references/messaging/get-provider.md b/docs/references/messaging/get-provider.md new file mode 100644 index 0000000000..25f35cb768 --- /dev/null +++ b/docs/references/messaging/get-provider.md @@ -0,0 +1 @@ +Get a provider by its unique ID. diff --git a/docs/references/messaging/get-subscriber.md b/docs/references/messaging/get-subscriber.md new file mode 100644 index 0000000000..ee790ee591 --- /dev/null +++ b/docs/references/messaging/get-subscriber.md @@ -0,0 +1 @@ +Get a subscriber by its unique ID. diff --git a/docs/references/messaging/get-topic.md b/docs/references/messaging/get-topic.md new file mode 100644 index 0000000000..2f54f23e88 --- /dev/null +++ b/docs/references/messaging/get-topic.md @@ -0,0 +1 @@ +Get a topic by its unique ID. diff --git a/docs/references/messaging/list-message-logs.md b/docs/references/messaging/list-message-logs.md new file mode 100644 index 0000000000..8a050983a7 --- /dev/null +++ b/docs/references/messaging/list-message-logs.md @@ -0,0 +1 @@ +Get the message activity logs listed by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/list-message-targets.md b/docs/references/messaging/list-message-targets.md index cc39250d5f..9bd8a05d6d 100644 --- a/docs/references/messaging/list-message-targets.md +++ b/docs/references/messaging/list-message-targets.md @@ -1 +1 @@ -List the targets associated with a message as set via the targets attribute. \ No newline at end of file +Get a list of the targets associated with a message. \ No newline at end of file diff --git a/docs/references/messaging/list-messages.md b/docs/references/messaging/list-messages.md new file mode 100644 index 0000000000..121f4c9a13 --- /dev/null +++ b/docs/references/messaging/list-messages.md @@ -0,0 +1 @@ +Get a list of all messages from the current Appwrite project. \ No newline at end of file diff --git a/docs/references/messaging/list-provider-logs.md b/docs/references/messaging/list-provider-logs.md new file mode 100644 index 0000000000..279839659f --- /dev/null +++ b/docs/references/messaging/list-provider-logs.md @@ -0,0 +1 @@ +Get the provider activity logs listed by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/list-providers.md b/docs/references/messaging/list-providers.md new file mode 100644 index 0000000000..7ca20ea069 --- /dev/null +++ b/docs/references/messaging/list-providers.md @@ -0,0 +1 @@ +Get a list of all providers from the current Appwrite project. \ No newline at end of file diff --git a/docs/references/messaging/list-subscriber-logs.md b/docs/references/messaging/list-subscriber-logs.md new file mode 100644 index 0000000000..c6c28e3549 --- /dev/null +++ b/docs/references/messaging/list-subscriber-logs.md @@ -0,0 +1 @@ +Get the subscriber activity logs listed by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/list-subscribers.md b/docs/references/messaging/list-subscribers.md new file mode 100644 index 0000000000..221b4fa6fe --- /dev/null +++ b/docs/references/messaging/list-subscribers.md @@ -0,0 +1 @@ +Get a list of all subscribers from the current Appwrite project. \ No newline at end of file diff --git a/docs/references/messaging/list-topic-logs.md b/docs/references/messaging/list-topic-logs.md new file mode 100644 index 0000000000..b807735408 --- /dev/null +++ b/docs/references/messaging/list-topic-logs.md @@ -0,0 +1 @@ +Get the topic activity logs listed by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/list-topics.md b/docs/references/messaging/list-topics.md new file mode 100644 index 0000000000..0f98f29826 --- /dev/null +++ b/docs/references/messaging/list-topics.md @@ -0,0 +1 @@ +Get a list of all topics from the current Appwrite project. \ No newline at end of file diff --git a/docs/references/messaging/update-apns-provider.md b/docs/references/messaging/update-apns-provider.md new file mode 100644 index 0000000000..eef0c3127b --- /dev/null +++ b/docs/references/messaging/update-apns-provider.md @@ -0,0 +1 @@ +Update a Apple Push Notification service provider by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/update-email.md b/docs/references/messaging/update-email.md new file mode 100644 index 0000000000..89f01d7a11 --- /dev/null +++ b/docs/references/messaging/update-email.md @@ -0,0 +1 @@ +Update an email message by its unique ID. diff --git a/docs/references/messaging/update-fcm-provider.md b/docs/references/messaging/update-fcm-provider.md new file mode 100644 index 0000000000..ea898a3cb3 --- /dev/null +++ b/docs/references/messaging/update-fcm-provider.md @@ -0,0 +1 @@ +Update a Firebase Cloud Messaging provider by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/update-mailgun-provider.md b/docs/references/messaging/update-mailgun-provider.md new file mode 100644 index 0000000000..9f1e981b24 --- /dev/null +++ b/docs/references/messaging/update-mailgun-provider.md @@ -0,0 +1 @@ +Update a Mailgun provider by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/update-msg91-provider.md b/docs/references/messaging/update-msg91-provider.md new file mode 100644 index 0000000000..e9a44a48cd --- /dev/null +++ b/docs/references/messaging/update-msg91-provider.md @@ -0,0 +1 @@ +Update a MSG91 provider by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/update-push.md b/docs/references/messaging/update-push.md new file mode 100644 index 0000000000..dd200a9729 --- /dev/null +++ b/docs/references/messaging/update-push.md @@ -0,0 +1 @@ +Update a push notification by its unique ID. diff --git a/docs/references/messaging/update-sendgrid-provider.md b/docs/references/messaging/update-sendgrid-provider.md new file mode 100644 index 0000000000..6ac45ec4a8 --- /dev/null +++ b/docs/references/messaging/update-sendgrid-provider.md @@ -0,0 +1 @@ +Update a Sendgrid provider by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/update-sms.md b/docs/references/messaging/update-sms.md new file mode 100644 index 0000000000..2f9b57a7a7 --- /dev/null +++ b/docs/references/messaging/update-sms.md @@ -0,0 +1 @@ +Update an SMS message by its unique ID. diff --git a/docs/references/messaging/update-smtp-provider.md b/docs/references/messaging/update-smtp-provider.md new file mode 100644 index 0000000000..c140075f0c --- /dev/null +++ b/docs/references/messaging/update-smtp-provider.md @@ -0,0 +1 @@ +Update a SMTP provider by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/update-telesign-provider.md b/docs/references/messaging/update-telesign-provider.md new file mode 100644 index 0000000000..7b06ef8e8f --- /dev/null +++ b/docs/references/messaging/update-telesign-provider.md @@ -0,0 +1 @@ +Update a Telesign provider by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/update-textmagic-provider.md b/docs/references/messaging/update-textmagic-provider.md new file mode 100644 index 0000000000..02aec37c2f --- /dev/null +++ b/docs/references/messaging/update-textmagic-provider.md @@ -0,0 +1 @@ +Update a Textmagic provider by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/update-topic.md b/docs/references/messaging/update-topic.md new file mode 100644 index 0000000000..25c9ea5b36 --- /dev/null +++ b/docs/references/messaging/update-topic.md @@ -0,0 +1 @@ +Update a topic by its unique ID. diff --git a/docs/references/messaging/update-twilio-provider.md b/docs/references/messaging/update-twilio-provider.md new file mode 100644 index 0000000000..fcdb400c73 --- /dev/null +++ b/docs/references/messaging/update-twilio-provider.md @@ -0,0 +1 @@ +Update a Twilio provider by its unique ID. \ No newline at end of file diff --git a/docs/references/messaging/update-vonage-provider.md b/docs/references/messaging/update-vonage-provider.md new file mode 100644 index 0000000000..d3d8329833 --- /dev/null +++ b/docs/references/messaging/update-vonage-provider.md @@ -0,0 +1 @@ +Update a Vonage provider by its unique ID. \ No newline at end of file diff --git a/docs/sdks/android/GETTING_STARTED.md b/docs/sdks/android/GETTING_STARTED.md index 684f98e0f6..de16f2cc57 100644 --- a/docs/sdks/android/GETTING_STARTED.md +++ b/docs/sdks/android/GETTING_STARTED.md @@ -52,8 +52,9 @@ When trying to connect to Appwrite from an emulator or a mobile device, localhos val account = Account(client) val response = account.create( ID.unique(), - "email@example.com", - "password" + "email@example.com", + "password", + "Walter O'Brien" ) ``` @@ -72,8 +73,9 @@ val client = Client(context) val account = Account(client) val user = account.create( ID.unique(), - "email@example.com", - "password" + "email@example.com", + "password", + "Walter O'Brien" ) ``` @@ -82,7 +84,7 @@ The Appwrite Android SDK raises an `AppwriteException` object with `message`, `c ```kotlin try { - var user = account.create(ID.unique(), "email@example.com", "password") + var user = account.create(ID.unique(),"email@example.com","password","Walter O'Brien") Log.d("Appwrite user", user.toMap()) } catch(e : AppwriteException) { e.printStackTrace() @@ -94,4 +96,4 @@ You can use the following resources to learn more and get help - 🚀 [Getting Started Tutorial](https://appwrite.io/docs/getting-started-for-android) - 📜 [Appwrite Docs](https://appwrite.io/docs) - 💬 [Discord Community](https://appwrite.io/discord) -- 🚂 [Appwrite Android Playground](https://github.com/appwrite/playground-for-android) \ No newline at end of file +- 🚂 [Appwrite Android Playground](https://github.com/appwrite/playground-for-android) diff --git a/docs/sdks/apple/GETTING_STARTED.md b/docs/sdks/apple/GETTING_STARTED.md index e62f1ce3fa..6defbc5f00 100644 --- a/docs/sdks/apple/GETTING_STARTED.md +++ b/docs/sdks/apple/GETTING_STARTED.md @@ -75,9 +75,10 @@ let account = Account(client) do { let user = try await account.create( - userId: ID.unique(), - email: "email@example.com", - password: "password" + userId: ID.unique(), + email: "email@example.com", + password: "password", + name: "Walter O'Brien" ) print(String(describing: user.toMap())) } catch { @@ -100,9 +101,10 @@ func main() { do { let user = try await account.create( - userId: ID.unique(), - email: "email@example.com", - password: "password" + userId: ID.unique(), + email: "email@example.com", + password: "password", + name: "Walter O'Brien" ) print(String(describing: account.toMap())) } catch { diff --git a/docs/sdks/dart/EXAMPLES.md b/docs/sdks/dart/EXAMPLES.md index 208cc7f782..fc2c6d0996 100644 --- a/docs/sdks/dart/EXAMPLES.md +++ b/docs/sdks/dart/EXAMPLES.md @@ -18,9 +18,11 @@ Create a new user: Users users = Users(client); User result = await users.create( - userId: '[USER_ID]', - email: 'email@example.com', - password: 'password', + userId: ID.unique(), + email: "email@example.com", + phone: "+123456789", + password: "password", + name: "Walter O'Brien" ); ``` @@ -57,4 +59,4 @@ storage.createFile( }); ``` -All examples and API features are available at the [official Appwrite docs](https://appwrite.io/docs) \ No newline at end of file +All examples and API features are available at the [official Appwrite docs](https://appwrite.io/docs) diff --git a/docs/sdks/dart/GETTING_STARTED.md b/docs/sdks/dart/GETTING_STARTED.md index 559c0894a3..a1dd4b5c4e 100644 --- a/docs/sdks/dart/GETTING_STARTED.md +++ b/docs/sdks/dart/GETTING_STARTED.md @@ -16,7 +16,7 @@ void main() async { Users users = Users(client); try { - final user = await users.create(userId: ID.unique(), email: ‘email@example.com’,password: ‘password’, name: ‘name’); + final user = await users.create(userId: ID.unique(), email: "email@example.com", phone: "+123456789", password: "password", name: "Walter O'Brien"); print(user.toMap()); } on AppwriteException catch(e) { print(e.message); @@ -31,7 +31,7 @@ The Appwrite Dart SDK raises `AppwriteException` object with `message`, `code` a Users users = Users(client); try { - final user = await users.create(userId: ID.unique(), email: ‘email@example.com’,password: ‘password’, name: ‘name’); + final user = await users.create(userId: ID.unique(), email: "email@example.com", phone: "+123456789", password: "password", name: "Walter O'Brien"); print(user.toMap()); } on AppwriteException catch(e) { //show message to user or do other operation based on error as required diff --git a/docs/sdks/deno/GETTING_STARTED.md b/docs/sdks/deno/GETTING_STARTED.md index 6546719a61..22ea80aa84 100644 --- a/docs/sdks/deno/GETTING_STARTED.md +++ b/docs/sdks/deno/GETTING_STARTED.md @@ -21,7 +21,7 @@ Once your SDK object is set, create any of the Appwrite service objects and choo ```typescript let users = new sdk.Users(client); -let user = await users.create(ID.unique(), 'email@example.com', 'password'); +let user = await users.create(ID.unique(), "email@example.com", "+123456789", "password", "Walter O'Brien"); console.log(user); ``` @@ -39,7 +39,7 @@ client .setSelfSigned() // Use only on dev mode with a self-signed SSL cert ; -let user = await users.create(ID.unique(), 'email@example.com', 'password'); +let user = await users.create(ID.unique(), "email@example.com", "+123456789", "password", "Walter O'Brien"); console.log(user); ``` @@ -50,7 +50,7 @@ The Appwrite Deno SDK raises `AppwriteException` object with `message`, `code` a let users = new sdk.Users(client); try { - let user = await users.create(ID.unique(), 'email@example.com', 'password'); + let user = await users.create(ID.unique(), "email@example.com", "+123456789", "password", "Walter O'Brien"); } catch(e) { console.log(e.message); } diff --git a/docs/sdks/dotnet/GETTING_STARTED.md b/docs/sdks/dotnet/GETTING_STARTED.md index 08d7742dd0..ae1f692e0c 100644 --- a/docs/sdks/dotnet/GETTING_STARTED.md +++ b/docs/sdks/dotnet/GETTING_STARTED.md @@ -18,8 +18,9 @@ var users = new Users(client); var user = await users.Create( userId: ID.Unique(), email: "email@example.com", + phone: "+123456789", password: "password", - name: "name"); + name: "Walter O'Brien"); Console.WriteLine(user.ToMap()); ``` @@ -35,8 +36,9 @@ try var user = await users.Create( userId: ID.Unique(), email: "email@example.com", + phone: "+123456789", password: "password", - name: "name"); + name: "Walter O'Brien"); } catch (AppwriteException e) { diff --git a/docs/sdks/flutter-dev/EXAMPLES.md b/docs/sdks/flutter-dev/EXAMPLES.md index 23b631900f..d0cb0c2b2a 100644 --- a/docs/sdks/flutter-dev/EXAMPLES.md +++ b/docs/sdks/flutter-dev/EXAMPLES.md @@ -17,7 +17,7 @@ Create a new user and session: ```dart Account account = Account(client); -final user = await account.create(userId: '[USER_ID]', email: 'me@appwrite.io', password: 'password', name: 'My Name'); +final user = await account.create(userId: ID.unique(), email: "email@example.com", password: "password", name: "Walter O'Brien"); final session = await account.createEmailSession(email: 'me@appwrite.io', password: 'password'); @@ -60,4 +60,4 @@ storage.createFile( }); ``` -All examples and API features are available at the [official Appwrite docs](https://appwrite.io/docs) \ No newline at end of file +All examples and API features are available at the [official Appwrite docs](https://appwrite.io/docs) diff --git a/docs/sdks/flutter/EXAMPLES.md b/docs/sdks/flutter/EXAMPLES.md index 23b631900f..d0cb0c2b2a 100644 --- a/docs/sdks/flutter/EXAMPLES.md +++ b/docs/sdks/flutter/EXAMPLES.md @@ -17,7 +17,7 @@ Create a new user and session: ```dart Account account = Account(client); -final user = await account.create(userId: '[USER_ID]', email: 'me@appwrite.io', password: 'password', name: 'My Name'); +final user = await account.create(userId: ID.unique(), email: "email@example.com", password: "password", name: "Walter O'Brien"); final session = await account.createEmailSession(email: 'me@appwrite.io', password: 'password'); @@ -60,4 +60,4 @@ storage.createFile( }); ``` -All examples and API features are available at the [official Appwrite docs](https://appwrite.io/docs) \ No newline at end of file +All examples and API features are available at the [official Appwrite docs](https://appwrite.io/docs) diff --git a/docs/sdks/flutter/GETTING_STARTED.md b/docs/sdks/flutter/GETTING_STARTED.md index 110ee3eb4a..8d239402b3 100644 --- a/docs/sdks/flutter/GETTING_STARTED.md +++ b/docs/sdks/flutter/GETTING_STARTED.md @@ -105,10 +105,7 @@ When trying to connect to Appwrite from an emulator or a mobile device, localhos Account account = Account(client); final user = await account .create( - userId: ID.unique(), - email: 'me@appwrite.io', - password: 'password', - name: 'My Name' + userId: ID.unique(), email: "email@example.com", password: "password", name: "Walter O'Brien" ); ``` @@ -133,10 +130,7 @@ void main() { final user = await account .create( - userId: ID.unique(), - email: 'me@appwrite.io', - password: 'password', - name: 'My Name' + userId: ID.unique(), email: "email@example.com", password: "password", name: "Walter O'Brien" ); } ``` @@ -148,7 +142,7 @@ The Appwrite Flutter SDK raises `AppwriteException` object with `message`, `type Account account = Account(client); try { - final user = await account.create(userId: ID.unique(), email: ‘email@example.com’,password: ‘password’, name: ‘name’); + final user = await account.create(userId: ID.unique(), email: "email@example.com", password: "password", name: "Walter O'Brien"); print(user.toMap()); } on AppwriteException catch(e) { //show message to user or do other operation based on error as required diff --git a/docs/sdks/kotlin/GETTING_STARTED.md b/docs/sdks/kotlin/GETTING_STARTED.md index 99d5e719af..5b5ee5f050 100644 --- a/docs/sdks/kotlin/GETTING_STARTED.md +++ b/docs/sdks/kotlin/GETTING_STARTED.md @@ -26,7 +26,9 @@ val users = Users(client) val user = users.create( user = ID.unique(), email = "email@example.com", + phone = "+123456789", password = "password", + name = "Walter O'Brien" ) ``` @@ -48,7 +50,9 @@ suspend fun main() { val user = users.create( user = ID.unique(), email = "email@example.com", + phone = "+123456789", password = "password", + name = "Walter O'Brien" ) } ``` @@ -68,7 +72,9 @@ suspend fun main() { val user = users.create( user = ID.unique(), email = "email@example.com", + phone = "+123456789", password = "password", + name = "Walter O'Brien" ) } catch (e: AppwriteException) { e.printStackTrace() diff --git a/docs/sdks/nodejs/GETTING_STARTED.md b/docs/sdks/nodejs/GETTING_STARTED.md index 985648d3fd..e98400f846 100644 --- a/docs/sdks/nodejs/GETTING_STARTED.md +++ b/docs/sdks/nodejs/GETTING_STARTED.md @@ -22,7 +22,7 @@ Once your SDK object is set, create any of the Appwrite service objects and choo ```js let users = new sdk.Users(client); -let promise = users.create(sdk.ID.unique(), 'email@example.com', undefined, 'password', 'Jane Doe'); +let promise = users.create(sdk.ID.unique(), "email@example.com", "+123456789", "password", "Walter O'Brien"); promise.then(function (response) { console.log(response); @@ -45,7 +45,7 @@ client ; let users = new sdk.Users(client); -let promise = users.create(sdk.ID.unique(), 'email@example.com', undefined, 'password', 'Jane Doe'); +let promise = users.create(sdk.ID.unique(), "email@example.com", "+123456789", "password", "Walter O'Brien"); promise.then(function (response) { console.log(response); @@ -61,7 +61,7 @@ The Appwrite Node SDK raises `AppwriteException` object with `message`, `code` a let users = new sdk.Users(client); try { - let res = await users.create(sdk.ID.unique(), 'email@example.com', 'password'); + let res = await users.create(sdk.ID.unique(), "email@example.com", "+123456789", "password", "Walter O'Brien"); } catch(e) { console.log(e.message); } diff --git a/docs/sdks/php/GETTING_STARTED.md b/docs/sdks/php/GETTING_STARTED.md index faa3dcf654..acbd06c8a4 100644 --- a/docs/sdks/php/GETTING_STARTED.md +++ b/docs/sdks/php/GETTING_STARTED.md @@ -20,7 +20,7 @@ Once your SDK object is set, create any of the Appwrite service objects and choo ```php $users = new Users($client); -$user = $users->create(ID::unique(), 'email@example.com', 'password'); +$user = $users->create(ID::unique(), "email@example.com", "+123456789", "password", "Walter O'Brien"); ``` ### Full Example @@ -40,7 +40,7 @@ $client $users = new Users($client); -$user = $users->create(ID::unique(), 'email@example.com', 'password'); +$user = $users->create(ID::unique(), "email@example.com", "+123456789", "password", "Walter O'Brien"); ``` ### Error Handling @@ -49,7 +49,7 @@ The Appwrite PHP SDK raises `AppwriteException` object with `message`, `code` an ```php $users = new Users($client); try { - $user = $users->create(ID::unique(), 'email@example.com', 'password'); + $user = $users->create(ID::unique(), "email@example.com", "+123456789", "password", "Walter O'Brien"); } catch(AppwriteException $error) { echo $error->message; } diff --git a/docs/sdks/python/GETTING_STARTED.md b/docs/sdks/python/GETTING_STARTED.md index 9f693b65c6..2732ef8483 100644 --- a/docs/sdks/python/GETTING_STARTED.md +++ b/docs/sdks/python/GETTING_STARTED.md @@ -23,7 +23,7 @@ Once your SDK object is set, create any of the Appwrite service objects and choo ```python users = Users(client) -result = users.create('[USER_ID]', 'email@example.com', 'password') +result = users.create(ID.unique(), email = "email@example.com", phone = "+123456789", password = "password", name = "Walter O'Brien") ``` ### Full Example @@ -43,7 +43,7 @@ client = Client() users = Users(client) -result = users.create(ID.unique(), 'email@example.com', 'password') +result = users.create(ID.unique(), email = "email@example.com", phone = "+123456789", password = "password", name = "Walter O'Brien") ``` ### Error Handling @@ -52,7 +52,7 @@ The Appwrite Python SDK raises `AppwriteException` object with `message`, `code` ```python users = Users(client) try: - result = users.create(ID.unique(), 'email@example.com', 'password') + result = users.create(ID.unique(), email = "email@example.com", phone = "+123456789", password = "password", name = "Walter O'Brien") except AppwriteException as e: print(e.message) ``` diff --git a/docs/sdks/ruby/GETTING_STARTED.md b/docs/sdks/ruby/GETTING_STARTED.md index da10e1aebc..5d7d7ee370 100644 --- a/docs/sdks/ruby/GETTING_STARTED.md +++ b/docs/sdks/ruby/GETTING_STARTED.md @@ -22,7 +22,7 @@ Once your SDK object is set, create any of the Appwrite service objects and choo ```ruby users = Appwrite::Users.new(client); -user = users.create(userId: Appwrite::ID::unique(), email: 'email@example.com', password: 'password'); +user = users.create(userId: Appwrite::ID::unique(), email: "email@example.com", phone: "+123456789", password: "password", name: "Walter O'Brien"); ``` ### Full Example @@ -40,7 +40,7 @@ client users = Appwrite::Users.new(client); -user = users.create(userId: Appwrite::ID::unique(), email: 'email@example.com', password: 'password'); +user = users.create(userId: Appwrite::ID::unique(), email: "email@example.com", phone: "+123456789", password: "password", name: "Walter O'Brien"); ``` ### Error Handling @@ -50,7 +50,7 @@ The Appwrite Ruby SDK raises `Appwrite::Exception` object with `message`, `code` users = Appwrite::Users.new(client); begin - user = users.create(userId: Appwrite::ID::unique(), email: 'email@example.com', password: 'password'); + user = users.create(userId: Appwrite::ID::unique(), email: "email@example.com", phone: "+123456789", password: "password", name: "Walter O'Brien"); rescue Appwrite::Exception => error puts error.message end diff --git a/docs/sdks/swift/GETTING_STARTED.md b/docs/sdks/swift/GETTING_STARTED.md index e0fb45dd7d..49aa51e9b2 100644 --- a/docs/sdks/swift/GETTING_STARTED.md +++ b/docs/sdks/swift/GETTING_STARTED.md @@ -25,9 +25,11 @@ let users = Users(client) do { let user = try await users.create( - userId: ID.unique(), - email: "email@example.com", - password: "password" + userId: ID.unique(), + email: "email@example.com", + phone: "+123456789", + password: "password", + name: "Walter O'Brien" ) print(String(describing: user.toMap())) } catch { @@ -51,9 +53,11 @@ func main() { do { let user = try await users.create( - userId: ID.unique(), - email: "email@example.com", - password: "password" + userId: ID.unique(), + email: "email@example.com", + phone: "+123456789", + password: "password", + name: "Walter O'Brien" ) print(String(describing: user.toMap())) } catch { diff --git a/docs/sdks/web/GETTING_STARTED.md b/docs/sdks/web/GETTING_STARTED.md index 445a362c05..26aa9470bd 100644 --- a/docs/sdks/web/GETTING_STARTED.md +++ b/docs/sdks/web/GETTING_STARTED.md @@ -25,7 +25,7 @@ Once your SDK object is set, access any of the Appwrite services and choose any const account = new Account(client); // Register User -account.create(ID.unique(), 'me@example.com', 'password', 'Jane Doe') +account.create(ID.unique(), "email@example.com", "password", "Walter O'Brien") .then(function (response) { console.log(response); }, function (error) { @@ -47,7 +47,7 @@ client const account = new Account(client); // Register User -account.create(ID.unique(), 'me@example.com', 'password', 'Jane Doe') +account.create(ID.unique(), "email@example.com", "password", "Walter O'Brien") .then(function (response) { console.log(response); }, function (error) { diff --git a/src/Appwrite/Auth/Validator/Phone.php b/src/Appwrite/Auth/Validator/Phone.php index b8c66edd07..26aa687278 100644 --- a/src/Appwrite/Auth/Validator/Phone.php +++ b/src/Appwrite/Auth/Validator/Phone.php @@ -47,7 +47,7 @@ class Phone extends Validator return true; } - return !!\preg_match('/^\+[1-9]\d{1,14}$/', $value); + return !!\preg_match('/^\+[1-9]\d{6,14}$/', $value); } /** diff --git a/src/Appwrite/Event/Delete.php b/src/Appwrite/Event/Delete.php index 57300feb72..064fbcefa9 100644 --- a/src/Appwrite/Event/Delete.php +++ b/src/Appwrite/Event/Delete.php @@ -10,6 +10,7 @@ class Delete extends Event { protected string $type = ''; protected ?Document $document = null; + protected ?string $resourceType = null; protected ?string $resource = null; protected ?string $datetime = null; protected ?string $hourlyUsageRetentionDatetime = null; @@ -107,6 +108,19 @@ class Delete extends Event return $this; } + /** + * Sets the resource type for the delete event. + * + * @param string $resourceType + * @return self + */ + public function setResourceType(string $resourceType): self + { + $this->resourceType = $resourceType; + + return $this; + } + /** * Returns the set document for the delete event. * @@ -133,6 +147,7 @@ class Delete extends Event 'type' => $this->type, 'document' => $this->document, 'resource' => $this->resource, + 'resourceType' => $this->resourceType, 'datetime' => $this->datetime, 'hourlyUsageRetentionDatetime' => $this->hourlyUsageRetentionDatetime ]); diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index fc12c5b5b3..9f71ef5ebc 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -27,6 +27,9 @@ class Event public const USAGE_QUEUE_NAME = 'v1-usage'; public const USAGE_CLASS_NAME = 'UsageV1'; + public const USAGE_DUMP_QUEUE_NAME = 'v1-usage-dump'; + public const USAGE_DUMP_CLASS_NAME = 'UsageDumpV1'; + public const WEBHOOK_QUEUE_NAME = 'v1-webhooks'; public const WEBHOOK_CLASS_NAME = 'WebhooksV1'; diff --git a/src/Appwrite/Event/Messaging.php b/src/Appwrite/Event/Messaging.php index 9201799355..f8ea9e472b 100644 --- a/src/Appwrite/Event/Messaging.php +++ b/src/Appwrite/Event/Messaging.php @@ -8,13 +8,13 @@ use Utopia\Queue\Client; class Messaging extends Event { + protected string $type = ''; protected ?string $messageId = null; protected ?Document $message = null; protected ?array $recipients = null; protected ?string $scheduledAt = null; protected ?string $providerType = null; - public function __construct(protected Connection $connection) { parent::__construct($connection); @@ -24,6 +24,29 @@ class Messaging extends Event ->setClass(Event::MESSAGING_CLASS_NAME); } + /** + * Sets type for the build event. + * + * @param string $type Can be `MESSAGE_TYPE_INTERNAL` or `MESSAGE_TYPE_EXTERNAL`. + * @return self + */ + public function setType(string $type): self + { + $this->type = $type; + + return $this; + } + + /** + * Returns set type for the function event. + * + * @return string + */ + public function getType(): string + { + return $this->type; + } + /** * Sets recipient for the messaging event. * @@ -162,6 +185,7 @@ class Messaging extends Event $client = new Client($this->queue, $this->connection); return $client->enqueue([ + 'type' => $this->type, 'project' => $this->project, 'user' => $this->user, 'messageId' => $this->messageId, diff --git a/src/Appwrite/Event/Usage.php b/src/Appwrite/Event/Usage.php index 398c3319f2..ded276e166 100644 --- a/src/Appwrite/Event/Usage.php +++ b/src/Appwrite/Event/Usage.php @@ -2,6 +2,7 @@ namespace Appwrite\Event; +use Utopia\CLI\Console; use Utopia\Queue\Client; use Utopia\Queue\Connection; use Utopia\Database\Document; diff --git a/src/Appwrite/Event/UsageDump.php b/src/Appwrite/Event/UsageDump.php new file mode 100644 index 0000000000..8f87908849 --- /dev/null +++ b/src/Appwrite/Event/UsageDump.php @@ -0,0 +1,47 @@ +setQueue(Event::USAGE_DUMP_QUEUE_NAME) + ->setClass(Event::USAGE_DUMP_CLASS_NAME); + } + + /** + * Add Stats. + * + * @param array $stats + * @return self + */ + public function setStats(array $stats): self + { + $this->stats = $stats; + + return $this; + } + + /** + * Sends metrics to the usage worker. + * + * @return string|bool + */ + public function trigger(): string|bool + { + $client = new Client($this->queue, $this->connection); + + return $client->enqueue([ + 'stats' => $this->stats, + ]); + } +} diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index b508dbcc51..c8b6cdad0d 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -92,8 +92,9 @@ class Exception extends \Exception public const USER_OAUTH2_BAD_REQUEST = 'user_oauth2_bad_request'; public const USER_OAUTH2_UNAUTHORIZED = 'user_oauth2_unauthorized'; public const USER_OAUTH2_PROVIDER_ERROR = 'user_oauth2_provider_error'; - public const USER_EMAIL_ALREADY_VERIFIED = 'user_email_alread_verified'; - public const USER_PHONE_ALREADY_VERIFIED = 'user_phone_already_verified'; + public const USER_EMAIL_ALREADY_VERIFIED = 'user_email_already_verified'; + public const USER_PHONE_ALREADY_VERIFIED = 'user_phone_already_verified'; + public const USER_DELETION_PROHIBITED = 'user_deletion_prohibited'; public const USER_TARGET_NOT_FOUND = 'user_target_not_found'; public const USER_TARGET_ALREADY_EXISTS = 'user_target_already_exists'; @@ -130,18 +131,19 @@ class Exception extends \Exception public const STORAGE_INVALID_CONTENT_RANGE = 'storage_invalid_content_range'; public const STORAGE_INVALID_RANGE = 'storage_invalid_range'; public const STORAGE_INVALID_APPWRITE_ID = 'storage_invalid_appwrite_id'; + public const STORAGE_FILE_NOT_PUBLIC = 'storage_file_not_public'; /** VCS */ - public const INSTALLATION_NOT_FOUND = 'installation_not_found'; - public const PROVIDER_REPOSITORY_NOT_FOUND = 'provider_repository_not_found'; - public const REPOSITORY_NOT_FOUND = 'repository_not_found'; - public const PROVIDER_CONTRIBUTION_CONFLICT = 'provider_contribution_conflict'; - public const GENERAL_PROVIDER_FAILURE = 'general_provider_failure'; + public const INSTALLATION_NOT_FOUND = 'installation_not_found'; + public const PROVIDER_REPOSITORY_NOT_FOUND = 'provider_repository_not_found'; + public const REPOSITORY_NOT_FOUND = 'repository_not_found'; + public const PROVIDER_CONTRIBUTION_CONFLICT = 'provider_contribution_conflict'; + public const GENERAL_PROVIDER_FAILURE = 'general_provider_failure'; /** Functions */ public const FUNCTION_NOT_FOUND = 'function_not_found'; public const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported'; - public const FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing'; + public const FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing'; /** Deployments */ public const DEPLOYMENT_NOT_FOUND = 'deployment_not_found'; @@ -214,10 +216,10 @@ class Exception extends \Exception public const ROUTER_DOMAIN_NOT_CONFIGURED = 'router_domain_not_configured'; /** Proxy */ - public const RULE_RESOURCE_NOT_FOUND = 'rule_resource_not_found'; - public const RULE_NOT_FOUND = 'rule_not_found'; - public const RULE_ALREADY_EXISTS = 'rule_already_exists'; - public const RULE_VERIFICATION_FAILED = 'rule_verification_failed'; + public const RULE_RESOURCE_NOT_FOUND = 'rule_resource_not_found'; + public const RULE_NOT_FOUND = 'rule_not_found'; + public const RULE_ALREADY_EXISTS = 'rule_already_exists'; + public const RULE_VERIFICATION_FAILED = 'rule_verification_failed'; /** Keys */ public const KEY_NOT_FOUND = 'key_not_found'; @@ -234,53 +236,52 @@ class Exception extends \Exception public const GRAPHQL_TOO_MANY_QUERIES = 'graphql_too_many_queries'; /** Migrations */ - public const MIGRATION_NOT_FOUND = 'migration_not_found'; - public const MIGRATION_ALREADY_EXISTS = 'migration_already_exists'; - public const MIGRATION_IN_PROGRESS = 'migration_in_progress'; - public const MIGRATION_PROVIDER_ERROR = 'migration_provider_error'; + public const MIGRATION_NOT_FOUND = 'migration_not_found'; + public const MIGRATION_ALREADY_EXISTS = 'migration_already_exists'; + public const MIGRATION_IN_PROGRESS = 'migration_in_progress'; + public const MIGRATION_PROVIDER_ERROR = 'migration_provider_error'; /** Realtime */ - public const REALTIME_MESSAGE_FORMAT_INVALID = 'realtime_message_format_invalid'; - public const REALTIME_TOO_MANY_MESSAGES = 'realtime_too_many_messages'; - public const REALTIME_POLICY_VIOLATION = 'realtime_policy_violation'; + public const REALTIME_MESSAGE_FORMAT_INVALID = 'realtime_message_format_invalid'; + public const REALTIME_TOO_MANY_MESSAGES = 'realtime_too_many_messages'; + public const REALTIME_POLICY_VIOLATION = 'realtime_policy_violation'; /** Health */ - public const HEALTH_QUEUE_SIZE_EXCEEDED = 'health_queue_size_exceeded'; - public const HEALTH_CERTIFICATE_EXPIRED = 'health_certificate_expired'; - public const HEALTH_INVALID_HOST = 'health_invalid_host'; + public const HEALTH_QUEUE_SIZE_EXCEEDED = 'health_queue_size_exceeded'; + public const HEALTH_CERTIFICATE_EXPIRED = 'health_certificate_expired'; + public const HEALTH_INVALID_HOST = 'health_invalid_host'; /** Provider */ - public const PROVIDER_NOT_FOUND = 'provider_not_found'; - public const PROVIDER_ALREADY_EXISTS = 'provider_already_exists'; - public const PROVIDER_INCORRECT_TYPE = 'provider_incorrect_type'; - - public const PROVIDER_MISSING_CREDENTIALS = 'provider_missing_credentials'; + public const PROVIDER_NOT_FOUND = 'provider_not_found'; + public const PROVIDER_ALREADY_EXISTS = 'provider_already_exists'; + public const PROVIDER_INCORRECT_TYPE = 'provider_incorrect_type'; + public const PROVIDER_MISSING_CREDENTIALS = 'provider_missing_credentials'; /** Topic */ - public const TOPIC_NOT_FOUND = 'topic_not_found'; - public const TOPIC_ALREADY_EXISTS = 'topic_already_exists'; + public const TOPIC_NOT_FOUND = 'topic_not_found'; + public const TOPIC_ALREADY_EXISTS = 'topic_already_exists'; /** Subscriber */ - public const SUBSCRIBER_NOT_FOUND = 'subscriber_not_found'; - public const SUBSCRIBER_ALREADY_EXISTS = 'subscriber_already_exists'; + public const SUBSCRIBER_NOT_FOUND = 'subscriber_not_found'; + public const SUBSCRIBER_ALREADY_EXISTS = 'subscriber_already_exists'; /** Message */ - public const MESSAGE_NOT_FOUND = 'message_not_found'; - public const MESSAGE_MISSING_TARGET = 'message_missing_target'; - public const MESSAGE_ALREADY_SENT = 'message_already_sent'; - public const MESSAGE_ALREADY_PROCESSING = 'message_already_processing'; - public const MESSAGE_ALREADY_FAILED = 'message_already_failed'; - public const MESSAGE_ALREADY_SCHEDULED = 'message_already_scheduled'; - public const MESSAGE_TARGET_NOT_EMAIL = 'message_target_not_email'; - public const MESSAGE_TARGET_NOT_SMS = 'message_target_not_sms'; - public const MESSAGE_TARGET_NOT_PUSH = 'message_target_not_push'; - public const MESSAGE_MISSING_SCHEDULE = 'message_missing_schedule'; + public const MESSAGE_NOT_FOUND = 'message_not_found'; + public const MESSAGE_MISSING_TARGET = 'message_missing_target'; + public const MESSAGE_ALREADY_SENT = 'message_already_sent'; + public const MESSAGE_ALREADY_PROCESSING = 'message_already_processing'; + public const MESSAGE_ALREADY_FAILED = 'message_already_failed'; + public const MESSAGE_ALREADY_SCHEDULED = 'message_already_scheduled'; + public const MESSAGE_TARGET_NOT_EMAIL = 'message_target_not_email'; + public const MESSAGE_TARGET_NOT_SMS = 'message_target_not_sms'; + public const MESSAGE_TARGET_NOT_PUSH = 'message_target_not_push'; + public const MESSAGE_MISSING_SCHEDULE = 'message_missing_schedule'; /** Targets */ public const TARGET_PROVIDER_INVALID_TYPE = 'target_provider_invalid_type'; /** Schedules */ - public const SCHEDULE_NOT_FOUND = 'schedule_not_found'; + public const SCHEDULE_NOT_FOUND = 'schedule_not_found'; protected string $type = ''; diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php index 671120a817..e50c7c1ddc 100644 --- a/src/Appwrite/Platform/Services/Tasks.php +++ b/src/Appwrite/Platform/Services/Tasks.php @@ -42,7 +42,6 @@ class Tasks extends Service ->addAction(Install::getName(), new Install()) ->addAction(Maintenance::getName(), new Maintenance()) ->addAction(Migrate::getName(), new Migrate()) - ->addAction(Migrate::getName(), new Migrate()) ->addAction(PatchRecreateRepositoriesDocuments::getName(), new PatchRecreateRepositoriesDocuments()) ->addAction(QueueCount::getName(), new QueueCount()) ->addAction(QueueRetry::getName(), new QueueRetry()) diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index ce0904bcd8..84700e82e3 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -14,7 +14,7 @@ use Appwrite\Platform\Workers\Builds; use Appwrite\Platform\Workers\Deletes; use Appwrite\Platform\Workers\Hamster; use Appwrite\Platform\Workers\Usage; -use Appwrite\Platform\Workers\UsageHook; +use Appwrite\Platform\Workers\UsageDump; use Appwrite\Platform\Workers\Migrations; class Workers extends Service @@ -33,7 +33,7 @@ class Workers extends Service ->addAction(Messaging::getName(), new Messaging()) ->addAction(Webhooks::getName(), new Webhooks()) ->addAction(Hamster::getName(), new Hamster()) - ->addAction(UsageHook::getName(), new UsageHook()) + ->addAction(UsageDump::getName(), new UsageDump()) ->addAction(Usage::getName(), new Usage()) ->addAction(Migrations::getName(), new Migrations()) diff --git a/src/Appwrite/Platform/Tasks/ScheduleBase.php b/src/Appwrite/Platform/Tasks/ScheduleBase.php index f0aa559a7f..fbb2320a34 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleBase.php +++ b/src/Appwrite/Platform/Tasks/ScheduleBase.php @@ -64,8 +64,13 @@ abstract class ScheduleBase extends Action $getSchedule = function (Document $schedule) use ($dbForConsole, $getProjectDB): array { $project = $dbForConsole->getDocument('projects', $schedule->getAttribute('projectId')); + $collectionId = match ($schedule->getAttribute('resourceType')) { + 'function' => 'functions', + 'message' => 'messages' + }; + $resource = $getProjectDB($project)->getDocument( - $schedule->getAttribute('resourceCollection'), + $collectionId, $schedule->getAttribute('resourceId') ); @@ -108,7 +113,12 @@ abstract class ScheduleBase extends Action try { $this->schedules[$document['resourceId']] = $getSchedule($document); } catch (\Throwable $th) { - Console::error("Failed to load schedule for project {$document['projectId']} {$document['resourceCollection']} {$document['resourceId']}"); + $collectionId = match ($document->getAttribute('resourceType')) { + 'function' => 'functions', + 'message' => 'messages' + }; + + Console::error("Failed to load schedule for project {$document['projectId']} {$collectionId} {$document['resourceId']}"); Console::error($th->getMessage()); } } diff --git a/src/Appwrite/Platform/Tasks/ScheduleMessages.php b/src/Appwrite/Platform/Tasks/ScheduleMessages.php index cc641b434a..bc9b6d37d2 100644 --- a/src/Appwrite/Platform/Tasks/ScheduleMessages.php +++ b/src/Appwrite/Platform/Tasks/ScheduleMessages.php @@ -50,6 +50,7 @@ class ScheduleMessages extends ScheduleBase $queueForMessaging = new Messaging($connection); $queueForMessaging + ->setType(MESSAGE_SEND_TYPE_EXTERNAL) ->setMessageId($schedule['resourceId']) ->setProject($schedule['project']) ->trigger(); diff --git a/src/Appwrite/Platform/Workers/Builds.php b/src/Appwrite/Platform/Workers/Builds.php index d23cfbff94..5b2e5be761 100644 --- a/src/Appwrite/Platform/Workers/Builds.php +++ b/src/Appwrite/Platform/Workers/Builds.php @@ -25,6 +25,7 @@ use Utopia\Database\Helpers\ID; use Utopia\Logger\Log; use Utopia\Platform\Action; use Utopia\Queue\Message; +use Utopia\Storage\Device; use Utopia\Storage\Device\Local; use Utopia\VCS\Adapter\Git\GitHub; @@ -49,9 +50,9 @@ class Builds extends Action ->inject('queueForUsage') ->inject('cache') ->inject('dbForProject') - ->inject('getFunctionsDevice') + ->inject('deviceForFunctions') ->inject('log') - ->callback(fn($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, callable $getFunctionsDevice, Log $log) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $getFunctionsDevice, $log)); + ->callback(fn($message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log) => $this->action($message, $dbForConsole, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log)); } /** @@ -62,12 +63,12 @@ class Builds extends Action * @param Usage $queueForUsage * @param Cache $cache * @param Database $dbForProject - * @param callable $getFunctionsDevice + * @param Device $deviceForFunctions * @param Log $log * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, Database $dbForProject, callable $getFunctionsDevice, Log $log): void + public function action(Message $message, Database $dbForConsole, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log): void { $payload = $message->getPayload() ?? []; @@ -89,7 +90,7 @@ class Builds extends Action case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); $github = new GitHub($cache); - $this->buildDeployment($getFunctionsDevice, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForConsole, $dbForProject, $github, $project, $resource, $deployment, $template, $log); + $this->buildDeployment($deviceForFunctions, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForConsole, $dbForProject, $github, $project, $resource, $deployment, $template, $log); break; default: @@ -98,7 +99,7 @@ class Builds extends Action } /** - * @param callable $getFunctionsDevice + * @param Device $deviceForFunctions * @param Func $queueForFunctions * @param Event $queueForEvents * @param Usage $queueForUsage @@ -114,7 +115,7 @@ class Builds extends Action * @throws \Utopia\Database\Exception * @throws Exception */ - protected function buildDeployment(callable $getFunctionsDevice, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log): void + protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForConsole, Database $dbForProject, GitHub $github, Document $project, Document $function, Document $deployment, Document $template, Log $log): void { $executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST')); @@ -156,7 +157,6 @@ class Builds extends Action $durationStart = \microtime(true); $buildId = $deployment->getAttribute('buildId', ''); $isNewBuild = empty($buildId); - $deviceFunctions = $getFunctionsDevice($project->getId()); if ($isNewBuild) { $buildId = ID::unique(); @@ -170,7 +170,7 @@ class Builds extends Action 'path' => '', 'runtime' => $function->getAttribute('runtime'), 'source' => $deployment->getAttribute('path', ''), - 'sourceType' => strtolower($deviceFunctions->getType()), + 'sourceType' => strtolower($deviceForFunctions->getType()), 'logs' => '', 'endTime' => null, 'duration' => 0, @@ -188,7 +188,7 @@ class Builds extends Action $installationId = $deployment->getAttribute('installationId', ''); $providerRepositoryId = $deployment->getAttribute('providerRepositoryId', ''); $providerCommitHash = $deployment->getAttribute('providerCommitHash', ''); - $isVcsEnabled = $providerRepositoryId ? true : false; + $isVcsEnabled = !empty($providerRepositoryId); $owner = ''; $repositoryName = ''; @@ -311,10 +311,8 @@ class Builds extends Action Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr); - $deviceFunctions = $getFunctionsDevice($project->getId()); - - $path = $deviceFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); - $result = $localDevice->transfer($tmpPathFile, $path, $deviceFunctions); + $path = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION)); + $result = $localDevice->transfer($tmpPathFile, $path, $deviceForFunctions); if (!$result) { throw new \Exception("Unable to move file"); diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 542f7f41db..40e0de86cb 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -44,22 +44,22 @@ class Deletes extends Action ->inject('message') ->inject('dbForConsole') ->inject('getProjectDB') - ->inject('getFilesDevice') - ->inject('getFunctionsDevice') - ->inject('getBuildsDevice') - ->inject('getCacheDevice') + ->inject('deviceForFiles') + ->inject('deviceForFunctions') + ->inject('deviceForBuilds') + ->inject('deviceForCache') ->inject('abuseRetention') ->inject('executionRetention') ->inject('auditRetention') ->inject('log') - ->callback(fn ($message, $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log) => $this->action($message, $dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $abuseRetention, $executionRetention, $auditRetention, $log)); + ->callback(fn ($message, $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log) => $this->action($message, $dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $abuseRetention, $executionRetention, $auditRetention, $log)); } /** * @throws Exception * @throws Throwable */ - public function action(Message $message, Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log): void + public function action(Message $message, Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, string $abuseRetention, string $executionRetention, string $auditRetention, Log $log): void { $payload = $message->getPayload() ?? []; @@ -71,6 +71,7 @@ class Deletes extends Action $datetime = $payload['datetime'] ?? null; $hourlyUsageRetentionDatetime = $payload['hourlyUsageRetentionDatetime'] ?? null; $resource = $payload['resource'] ?? null; + $resourceType = $payload['resourceType'] ?? null; $document = new Document($payload['document'] ?? []); $project = new Document($payload['project'] ?? []); @@ -80,20 +81,14 @@ class Deletes extends Action switch (\strval($type)) { case DELETE_TYPE_DOCUMENT: switch ($document->getCollection()) { - case DELETE_TYPE_DATABASES: - $this->deleteDatabase($getProjectDB, $document, $project); - break; - case DELETE_TYPE_COLLECTIONS: - $this->deleteCollection($getProjectDB, $document, $project); - break; case DELETE_TYPE_PROJECTS: - $this->deleteProject($dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $document); + $this->deleteProject($dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $document); break; case DELETE_TYPE_FUNCTIONS: - $this->deleteFunction($dbForConsole, $getProjectDB, $getFunctionsDevice, $getBuildsDevice, $document, $project); + $this->deleteFunction($dbForConsole, $getProjectDB, $deviceForFunctions, $deviceForBuilds, $document, $project); break; case DELETE_TYPE_DEPLOYMENTS: - $this->deleteDeployment($getProjectDB, $getFunctionsDevice, $getBuildsDevice, $document, $project); + $this->deleteDeployment($getProjectDB, $deviceForFunctions, $deviceForBuilds, $document, $project); break; case DELETE_TYPE_USERS: $this->deleteUser($getProjectDB, $document, $project); @@ -101,11 +96,11 @@ class Deletes extends Action case DELETE_TYPE_TEAMS: $this->deleteMemberships($getProjectDB, $document, $project); if ($project->getId() === 'console') { - $this->deleteProjectsByTeam($dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $document); + $this->deleteProjectsByTeam($dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $document); } break; case DELETE_TYPE_BUCKETS: - $this->deleteBucket($getProjectDB, $getFilesDevice, $document, $project); + $this->deleteBucket($getProjectDB, $deviceForFiles, $document, $project); break; case DELETE_TYPE_INSTALLATIONS: $this->deleteInstallation($dbForConsole, $getProjectDB, $document, $project); @@ -114,10 +109,6 @@ class Deletes extends Action $this->deleteRule($dbForConsole, $document); break; default: - if (\str_starts_with($document->getCollection(), 'database_')) { - $this->deleteCollection($getProjectDB, $document, $project); - break; - } Console::error('No lazy delete operation available for document of type: ' . $document->getCollection()); break; } @@ -147,7 +138,7 @@ class Deletes extends Action $this->deleteUsageStats($project, $getProjectDB, $hourlyUsageRetentionDatetime); break; case DELETE_TYPE_CACHE_BY_RESOURCE: - $this->deleteCacheByResource($project, $getProjectDB, $resource); + $this->deleteCacheByResource($project, $getProjectDB, $resource, $resourceType); break; case DELETE_TYPE_CACHE_BY_TIMESTAMP: $this->deleteCacheByDate($project, $getProjectDB, $datetime); @@ -203,8 +194,13 @@ class Deletes extends Action return; } + $collectionId = match ($document->getAttribute('resourceType')) { + 'function' => 'functions', + 'message' => 'messages' + }; + $resource = $getProjectDB($project)->getDocument( - $document->getAttribute('resourceCollection'), + $collectionId, $document->getAttribute('resourceId') ); @@ -327,32 +323,37 @@ class Deletes extends Action * @param string $resource * @return void * @throws Authorization + * @param string|null $resourceType + * @throws Exception */ - private function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource): void + private function deleteCacheByResource(Document $project, callable $getProjectDB, string $resource, string $resourceType = null): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); - $document = $dbForProject->findOne('cache', [Query::equal('resource', [$resource])]); - if ($document) { - $cache = new Cache( - new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) - ); + $cache = new Cache( + new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId) + ); - $this->deleteById( - $document, - $dbForProject, - function ($document) use ($cache, $projectId) { - $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); - - if ($cache->purge($document->getId())) { - Console::success('Deleting cache file: ' . $path); - } else { - Console::error('Failed to delete cache file: ' . $path); - } - } - ); + $query[] = Query::equal('resource', [$resource]); + if (!empty($resourceType)) { + $query[] = Query::equal('resourceType', [$resourceType]); } + + $this->deleteByGroup( + 'cache', + $query, + $dbForProject, + function (Document $document) use ($cache, $projectId) { + $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId(); + + if ($cache->purge($document->getId())) { + Console::success('Deleting cache file: ' . $path); + } else { + Console::error('Failed to delete cache file: ' . $path); + } + } + ); } /** @@ -392,72 +393,6 @@ class Deletes extends Action ); } - /** - * @param callable $getProjectDB - * @param Document $document - * @param Document $project - * @return void - * @throws Exception - */ - private function deleteDatabase(callable $getProjectDB, Document $document, Document $project): void - { - $databaseId = $document->getId(); - $dbForProject = $getProjectDB($project); - - $this->deleteByGroup('database_' . $document->getInternalId(), [], $dbForProject, function ($document) use ($getProjectDB, $project) { - $this->deleteCollection($getProjectDB, $document, $project); - }); - - $dbForProject->deleteCollection('database_' . $document->getInternalId()); - $this->deleteAuditLogsByResource($getProjectDB, 'database/' . $databaseId, $project); - } - - /** - * @param callable $getProjectDB - * @param Document $document teams document - * @param Document $project - * @return void - * @throws Exception - */ - private function deleteCollection(callable $getProjectDB, Document $document, Document $project): void - { - $collectionId = $document->getId(); - $collectionInternalId = $document->getInternalId(); - $databaseId = $document->getAttribute('databaseId'); - $databaseInternalId = $document->getAttribute('databaseInternalId'); - - $dbForProject = $getProjectDB($project); - - $relationships = \array_filter( - $document->getAttribute('attributes'), - fn ($attribute) => $attribute['type'] === Database::VAR_RELATIONSHIP - ); - - foreach ($relationships as $relationship) { - if (!$relationship['twoWay']) { - continue; - } - $relatedCollection = $dbForProject->getDocument('database_' . $databaseInternalId, $relationship['relatedCollection']); - $dbForProject->deleteDocument('attributes', $databaseInternalId . '_' . $relatedCollection->getInternalId() . '_' . $relationship['twoWayKey']); - $dbForProject->purgeCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId()); - $dbForProject->purgeCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId()); - } - - $dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId()); - - $this->deleteByGroup('attributes', [ - Query::equal('databaseInternalId', [$databaseInternalId]), - Query::equal('collectionInternalId', [$collectionInternalId]) - ], $dbForProject); - - $this->deleteByGroup('indexes', [ - Query::equal('databaseInternalId', [$databaseInternalId]), - Query::equal('collectionInternalId', [$collectionInternalId]) - ], $dbForProject); - - $this->deleteAuditLogsByResource($getProjectDB, 'database/' . $databaseId . '/collection/' . $collectionId, $project); - } - /** * @param Database $dbForConsole * @param callable $getProjectDB @@ -511,14 +446,14 @@ class Deletes extends Action * @throws Restricted * @throws Structure */ - private function deleteProjectsByTeam(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void + private function deleteProjectsByTeam(Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, Document $document): void { $projects = $dbForConsole->find('projects', [ Query::equal('teamInternalId', [$document->getInternalId()]) ]); foreach ($projects as $project) { - $this->deleteProject($dbForConsole, $getProjectDB, $getFilesDevice, $getFunctionsDevice, $getBuildsDevice, $getCacheDevice, $project); + $this->deleteProject($dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $project); $dbForConsole->deleteDocument('projects', $project->getId()); } } @@ -526,17 +461,17 @@ class Deletes extends Action /** * @param Database $dbForConsole * @param callable $getProjectDB - * @param callable $getFilesDevice - * @param callable $getFunctionsDevice - * @param callable $getBuildsDevice - * @param callable $getCacheDevice + * @param Device $deviceForFiles + * @param Device $deviceForFunctions + * @param Device $deviceForBuilds + * @param Device $deviceForCache * @param Document $document * @return void * @throws Exception * @throws Authorization * @throws \Utopia\Database\Exception */ - private function deleteProject(Database $dbForConsole, callable $getProjectDB, callable $getFilesDevice, callable $getFunctionsDevice, callable $getBuildsDevice, callable $getCacheDevice, Document $document): void + private function deleteProject(Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, Document $document): void { $projectId = $document->getId(); $projectInternalId = $document->getInternalId(); @@ -602,15 +537,10 @@ class Deletes extends Action } // Delete all storage directories - $uploads = $getFilesDevice($projectId); - $functions = $getFunctionsDevice($projectId); - $builds = $getBuildsDevice($projectId); - $cache = $getCacheDevice($projectId); - - $uploads->delete($uploads->getRoot(), true); - $functions->delete($functions->getRoot(), true); - $builds->delete($builds->getRoot(), true); - $cache->delete($cache->getRoot(), true); + $deviceForFiles->delete($deviceForFiles->getRoot(), true); + $deviceForFunctions->delete($deviceForFunctions->getRoot(), true); + $deviceForBuilds->delete($deviceForBuilds->getRoot(), true); + $deviceForCache->delete($deviceForCache->getRoot(), true); } /** @@ -772,14 +702,14 @@ class Deletes extends Action /** * @param callable $getProjectDB - * @param callable $getFunctionsDevice - * @param callable $getBuildsDevice + * @param Device $deviceForFunctions + * @param Device $deviceForBuilds * @param Document $document function document * @param Document $project * @return void * @throws Exception */ - private function deleteFunction(Database $dbForConsole, callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void + private function deleteFunction(Database $dbForConsole, callable $getProjectDB, Device $deviceForFunctions, Device $deviceForBuilds, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); @@ -811,25 +741,25 @@ class Deletes extends Action * Delete Deployments */ Console::info("Deleting deployments for function " . $functionId); - $functionsStorage = $getFunctionsDevice($projectId); + $deploymentInternalIds = []; $this->deleteByGroup('deployments', [ Query::equal('resourceInternalId', [$functionInternalId]) - ], $dbForProject, function (Document $document) use ($functionsStorage, &$deploymentInternalIds) { + ], $dbForProject, function (Document $document) use ($deviceForFunctions, &$deploymentInternalIds) { $deploymentInternalIds[] = $document->getInternalId(); - $this->deleteDeploymentFiles($functionsStorage, $document); + $this->deleteDeploymentFiles($deviceForFunctions, $document); }); /** * Delete builds */ Console::info("Deleting builds for function " . $functionId); - $buildsStorage = $getBuildsDevice($projectId); + foreach ($deploymentInternalIds as $deploymentInternalId) { $this->deleteByGroup('builds', [ Query::equal('deploymentInternalId', [$deploymentInternalId]) - ], $dbForProject, function (Document $document) use ($buildsStorage) { - $this->deleteBuildFiles($buildsStorage, $document); + ], $dbForProject, function (Document $document) use ($deviceForBuilds) { + $this->deleteBuildFiles($deviceForBuilds, $document); }); } @@ -929,14 +859,14 @@ class Deletes extends Action /** * @param callable $getProjectDB - * @param callable $getFunctionsDevice - * @param callable $getBuildsDevice + * @param Device $deviceForFunctions + * @param Device $deviceForBuilds * @param Document $document * @param Document $project * @return void * @throws Exception */ - private function deleteDeployment(callable $getProjectDB, callable $getFunctionsDevice, callable $getBuildsDevice, Document $document, Document $project): void + private function deleteDeployment(callable $getProjectDB, Device $deviceForFunctions, Device $deviceForBuilds, Document $document, Document $project): void { $projectId = $project->getId(); $dbForProject = $getProjectDB($project); @@ -946,18 +876,17 @@ class Deletes extends Action /** * Delete deployment files */ - $functionsStorage = $getFunctionsDevice($projectId); - $this->deleteDeploymentFiles($functionsStorage, $document); + $this->deleteDeploymentFiles($deviceForFunctions, $document); /** * Delete builds */ Console::info("Deleting builds for deployment " . $deploymentId); - $buildsStorage = $getBuildsDevice($projectId); + $this->deleteByGroup('builds', [ Query::equal('deploymentInternalId', [$deploymentInternalId]) - ], $dbForProject, function (Document $document) use ($buildsStorage) { - $this->deleteBuildFiles($buildsStorage, $document); + ], $dbForProject, function (Document $document) use ($deviceForBuilds) { + $this->deleteBuildFiles($deviceForBuilds, $document); }); /** @@ -1101,21 +1030,18 @@ class Deletes extends Action /** * @param callable $getProjectDB - * @param callable $getFilesDevice + * @param Device $deviceForFiles * @param Document $document * @param Document $project * @return void */ - private function deleteBucket(callable $getProjectDB, callable $getFilesDevice, Document $document, Document $project): void + private function deleteBucket(callable $getProjectDB, Device $deviceForFiles, Document $document, Document $project): void { - $projectId = $project->getId(); $dbForProject = $getProjectDB($project); $dbForProject->deleteCollection('bucket_' . $document->getInternalId()); - $device = $getFilesDevice($projectId); - - $device->deletePath($document->getId()); + $deviceForFiles->deletePath($document->getId()); } /** diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index 083eae4e0a..62e58db11a 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -2,11 +2,14 @@ namespace Appwrite\Platform\Workers; +use Appwrite\Auth\Auth; use Appwrite\Event\Usage; use Appwrite\Extend\Exception; use Appwrite\Messaging\Status as MessageStatus; use Utopia\App; use Utopia\CLI\Console; +use Utopia\Config\Config; +use Utopia\Database\Validator\Authorization; use Utopia\DSN\DSN; use Utopia\Database\Database; use Utopia\Database\DateTime; @@ -29,10 +32,13 @@ use Utopia\Messaging\Adapter\SMS\Textmagic; use Utopia\Messaging\Adapter\SMS\Twilio; use Utopia\Messaging\Adapter\SMS\Vonage; use Utopia\Messaging\Messages\Email; +use Utopia\Messaging\Messages\Email\Attachment; use Utopia\Messaging\Messages\Push; use Utopia\Messaging\Messages\SMS; use Utopia\Platform\Action; use Utopia\Queue\Message; +use Utopia\Storage\Device; +use Utopia\Storage\Storage; use function Swoole\Coroutine\batch; @@ -44,7 +50,7 @@ class Messaging extends Action } /** - * @throws Exception + * @throws \Exception */ public function __construct() { @@ -53,49 +59,62 @@ class Messaging extends Action ->inject('message') ->inject('log') ->inject('dbForProject') + ->inject('deviceForFiles') + ->inject('deviceForLocalFiles') ->inject('queueForUsage') - ->callback(fn(Message $message, Log $log, Database $dbForProject, Usage $queueForUsage) => $this->action($message, $log, $dbForProject, $queueForUsage)); + ->callback(fn(Message $message, Log $log, Database $dbForProject, Device $deviceForFiles, Device $deviceForLocalFiles, Usage $queueForUsage) => $this->action($message, $log, $dbForProject, $deviceForFiles, $deviceForLocalFiles, $queueForUsage)); } /** * @param Message $message * @param Log $log * @param Database $dbForProject + * @param Device $deviceForFiles + * @param Device $deviceForLocalFiles * @param Usage $queueForUsage * @return void - * @throws Exception + * @throws \Exception */ - public function action(Message $message, Log $log, Database $dbForProject, Usage $queueForUsage): void - { + public function action( + Message $message, + Log $log, + Database $dbForProject, + Device $deviceForFiles, + Device $deviceForLocalFiles, + Usage $queueForUsage + ): void { $payload = $message->getPayload() ?? []; if (empty($payload)) { throw new Exception('Missing payload'); } + $type = $payload['type'] ?? ''; + $project = new Document($payload['project'] ?? []); - if ( - !\is_null($payload['message']) - && !\is_null($payload['recipients']) - && $payload['providerType'] === MESSAGE_TYPE_SMS - ) { - // Message was triggered internally - $this->processInternalSMSMessage( - new Document($payload['message']), - new Document($payload['project'] ?? []), - $payload['recipients'], - $queueForUsage, - $log, - ); - } else { - $message = $dbForProject->getDocument('messages', $payload['messageId']); + switch ($type) { + case MESSAGE_SEND_TYPE_INTERNAL: + $message = new Document($payload['message'] ?? []); + $recipients = $payload['recipients'] ?? []; - $this->processMessage($dbForProject, $message); + $this->sendInternalSMSMessage($message, $project, $recipients, $queueForUsage, $log); + break; + case MESSAGE_SEND_TYPE_EXTERNAL: + $message = $dbForProject->getDocument('messages', $payload['messageId']); + + $this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $deviceForLocalFiles,); + break; + default: + throw new Exception('Unknown message type: ' . $type); } } - private function processMessage(Database $dbForProject, Document $message): void - { + private function sendExternalMessage( + Database $dbForProject, + Document $message, + Device $deviceForFiles, + Device $deviceForLocalFiles, + ): void { $topicIds = $message->getAttribute('topics', []); $targetIds = $message->getAttribute('targets', []); $userIds = $message->getAttribute('users', []); @@ -199,8 +218,8 @@ class Messaging extends Action /** * @var array $results */ - $results = batch(\array_map(function ($providerId) use ($identifiers, $providers, $fallback, $message, $dbForProject) { - return function () use ($providerId, $identifiers, $providers, $fallback, $message, $dbForProject) { + $results = batch(\array_map(function ($providerId) use ($identifiers, $providers, $fallback, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) { + return function () use ($providerId, $identifiers, $providers, $fallback, $message, $dbForProject, $deviceForFiles, $deviceForLocalFiles) { if (\array_key_exists($providerId, $providers)) { $provider = $providers[$providerId]; } else { @@ -216,9 +235,9 @@ class Messaging extends Action $identifiers = $identifiers[$providerId]; $adapter = match ($provider->getAttribute('type')) { - MESSAGE_TYPE_SMS => $this->sms($provider), - MESSAGE_TYPE_PUSH => $this->push($provider), - MESSAGE_TYPE_EMAIL => $this->email($provider), + MESSAGE_TYPE_SMS => $this->getSmsAdapter($provider), + MESSAGE_TYPE_PUSH => $this->getPushAdapter($provider), + MESSAGE_TYPE_EMAIL => $this->getEmailAdapter($provider), default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE) }; @@ -226,17 +245,17 @@ class Messaging extends Action $batches = \array_chunk($identifiers, $maxBatchSize); $batchIndex = 0; - return batch(\array_map(function ($batch) use ($message, $provider, $adapter, &$batchIndex, $dbForProject) { - return function () use ($batch, $message, $provider, $adapter, &$batchIndex, $dbForProject) { + return batch(\array_map(function ($batch) use ($message, $provider, $adapter, &$batchIndex, $dbForProject, $deviceForFiles, $deviceForLocalFiles) { + return function () use ($batch, $message, $provider, $adapter, &$batchIndex, $dbForProject, $deviceForFiles, $deviceForLocalFiles) { $deliveredTotal = 0; $deliveryErrors = []; $messageData = clone $message; $messageData->setAttribute('to', $batch); $data = match ($provider->getAttribute('type')) { - MESSAGE_TYPE_SMS => $this->buildSMSMessage($messageData, $provider), + MESSAGE_TYPE_SMS => $this->buildSmsMessage($messageData, $provider), MESSAGE_TYPE_PUSH => $this->buildPushMessage($messageData), - MESSAGE_TYPE_EMAIL => $this->buildEmailMessage($dbForProject, $messageData, $provider), + MESSAGE_TYPE_EMAIL => $this->buildEmailMessage($dbForProject, $messageData, $provider, $deviceForFiles, $deviceForLocalFiles), default => throw new Exception(Exception::PROVIDER_INCORRECT_TYPE) }; @@ -310,9 +329,40 @@ class Messaging extends Action $message->setAttribute('deliveredAt', DateTime::now()); $dbForProject->updateDocument('messages', $message->getId(), $message); + + // Delete any attachments that were downloaded to the local cache + if ($provider->getAttribute('type') === MESSAGE_TYPE_EMAIL) { + if ($deviceForFiles->getType() === Storage::DEVICE_LOCAL) { + return; + } + + $data = $message->getAttribute('data'); + $attachments = $data['attachments'] ?? []; + + foreach ($attachments as $attachment) { + $bucketId = $attachment['bucketId']; + $fileId = $attachment['fileId']; + + $bucket = $dbForProject->getDocument('buckets', $bucketId); + if ($bucket->isEmpty()) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } + + $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + if ($file->isEmpty()) { + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); + } + + $path = $file->getAttribute('path', ''); + + if ($deviceForLocalFiles->exists($path)) { + $deviceForLocalFiles->delete($path); + } + } + } } - private function processInternalSMSMessage(Document $message, Document $project, array $recipients, Usage $queueForUsage, Log $log): void + private function sendInternalSMSMessage(Document $message, Document $project, array $recipients, Usage $queueForUsage, Log $log): void { if (empty(App::getEnv('_APP_SMS_PROVIDER')) || empty(App::getEnv('_APP_SMS_FROM'))) { throw new \Exception('Skipped SMS processing. Missing "_APP_SMS_PROVIDER" or "_APP_SMS_FROM" environment variables.'); @@ -375,7 +425,7 @@ class Messaging extends Action ] ]); - $adapter = $this->sms($provider); + $adapter = $this->getSmsAdapter($provider); $maxBatchSize = $adapter->getMaxMessagesPerRequest(); $batches = \array_chunk($recipients, $maxBatchSize); @@ -385,7 +435,7 @@ class Messaging extends Action return function () use ($batch, $message, $provider, $adapter, $batchIndex, $project, $queueForUsage) { $message->setAttribute('to', $batch); - $data = $this->buildSMSMessage($message, $provider); + $data = $this->buildSmsMessage($message, $provider); try { $adapter->send($data); @@ -401,11 +451,7 @@ class Messaging extends Action }, $batches)); } - public function shutdown(): void - { - } - - private function sms(Document $provider): ?SMSAdapter + private function getSmsAdapter(Document $provider): ?SMSAdapter { $credentials = $provider->getAttribute('credentials'); @@ -420,7 +466,7 @@ class Messaging extends Action }; } - private function push(Document $provider): ?PushAdapter + private function getPushAdapter(Document $provider): ?PushAdapter { $credentials = $provider->getAttribute('credentials'); @@ -437,7 +483,7 @@ class Messaging extends Action }; } - private function email(Document $provider): ?EmailAdapter + private function getEmailAdapter(Document $provider): ?EmailAdapter { $credentials = $provider->getAttribute('credentials', []); $options = $provider->getAttribute('options', []); @@ -463,8 +509,13 @@ class Messaging extends Action }; } - private function buildEmailMessage(Database $dbForProject, Document $message, Document $provider): Email - { + private function buildEmailMessage( + Database $dbForProject, + Document $message, + Document $provider, + Device $deviceForFiles, + Device $deviceForLocalFiles, + ): Email { $fromName = $provider['options']['fromName'] ?? null; $fromEmail = $provider['options']['fromEmail'] ?? null; $replyToEmail = $provider['options']['replyToEmail'] ?? null; @@ -474,8 +525,9 @@ class Messaging extends Action $bccTargets = $data['bcc'] ?? []; $cc = []; $bcc = []; + $attachments = $data['attachments'] ?? []; - if (\count($ccTargets) > 0) { + if (!empty($ccTargets)) { $ccTargets = $dbForProject->find('targets', [ Query::equal('$id', $ccTargets), Query::limit(\count($ccTargets)), @@ -485,7 +537,7 @@ class Messaging extends Action } } - if (\count($bccTargets) > 0) { + if (!empty($bccTargets)) { $bccTargets = $dbForProject->find('targets', [ Query::equal('$id', $bccTargets), Query::limit(\count($bccTargets)), @@ -495,21 +547,77 @@ class Messaging extends Action } } + if (!empty($attachments)) { + foreach ($attachments as &$attachment) { + $bucketId = $attachment['bucketId']; + $fileId = $attachment['fileId']; + + $bucket = $dbForProject->getDocument('buckets', $bucketId); + if ($bucket->isEmpty()) { + throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); + } + + $file = $dbForProject->getDocument('bucket_' . $bucket->getInternalId(), $fileId); + if ($file->isEmpty()) { + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND); + } + + $mimes = Config::getParam('storage-mimes'); + $path = $file->getAttribute('path', ''); + + if (!$deviceForFiles->exists($path)) { + throw new Exception(Exception::STORAGE_FILE_NOT_FOUND, 'File not found in ' . $path); + } + + $contentType = 'text/plain'; + + if (\in_array($file->getAttribute('mimeType'), $mimes)) { + $contentType = $file->getAttribute('mimeType'); + } + + if ($deviceForFiles->getType() !== Storage::DEVICE_LOCAL) { + $deviceForFiles->transfer($path, $path, $deviceForLocalFiles); + } + + $attachment = new Attachment( + $file->getAttribute('name'), + $path, + $contentType + ); + } + } + $to = $message['to']; $subject = $data['subject']; $content = $data['content']; $html = $data['html'] ?? false; - return new Email($to, $subject, $content, $fromName, $fromEmail, $replyToName, $replyToEmail, $cc, $bcc, null, $html); + return new Email( + $to, + $subject, + $content, + $fromName, + $fromEmail, + $replyToName, + $replyToEmail, + $cc, + $bcc, + $attachments, + $html + ); } - private function buildSMSMessage(Document $message, Document $provider): SMS + private function buildSmsMessage(Document $message, Document $provider): SMS { $to = $message['to']; $content = $message['data']['content']; $from = $provider['options']['from']; - return new SMS($to, $content, $from); + return new SMS( + $to, + $content, + $from + ); } private function buildPushMessage(Document $message): Push @@ -519,12 +627,25 @@ class Messaging extends Action $body = $message['data']['body']; $data = $message['data']['data'] ?? null; $action = $message['data']['action'] ?? null; + $image = $message['data']['image'] ?? null; $sound = $message['data']['sound'] ?? null; $icon = $message['data']['icon'] ?? null; $color = $message['data']['color'] ?? null; $tag = $message['data']['tag'] ?? null; $badge = $message['data']['badge'] ?? null; - return new Push($to, $title, $body, $data, $action, $sound, $icon, $color, $tag, $badge); + return new Push( + $to, + $title, + $body, + $data, + $action, + $sound, + $image, + $icon, + $color, + $tag, + $badge + ); } } diff --git a/src/Appwrite/Platform/Workers/Usage.php b/src/Appwrite/Platform/Workers/Usage.php index 2271dd5909..e2f3252500 100644 --- a/src/Appwrite/Platform/Workers/Usage.php +++ b/src/Appwrite/Platform/Workers/Usage.php @@ -3,8 +3,9 @@ namespace Appwrite\Platform\Workers; use Exception; +use Appwrite\Event\UsageDump; +use Utopia\App; use Utopia\CLI\Console; -use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Platform\Action; @@ -12,14 +13,12 @@ use Utopia\Queue\Message; class Usage extends Action { - protected static array $stats = []; - protected array $periods = [ - '1h' => 'Y-m-d H:00', - '1d' => 'Y-m-d 00:00', - 'inf' => '0000-00-00 00:00' - ]; + private array $stats = []; + private int $lastTriggeredTime = 0; + private int $keys = 0; + private const INFINITY_PERIOD = '_inf_'; + private const KEYS_THRESHOLD = 10000; - protected const INFINITY_PERIOD = '_inf_'; public static function getName(): string { return 'usage'; @@ -35,26 +34,31 @@ class Usage extends Action ->desc('Usage worker') ->inject('message') ->inject('getProjectDB') - ->callback(function (Message $message, callable $getProjectDB) { - $this->action($message, $getProjectDB); + ->inject('queueForUsageDump') + ->callback(function (Message $message, callable $getProjectDB, UsageDump $queueForUsageDump) { + $this->action($message, $getProjectDB, $queueForUsageDump); }); + + $this->lastTriggeredTime = time(); } /** * @param Message $message * @param callable $getProjectDB + * @param UsageDump $queueForUsageDump * @return void * @throws \Utopia\Database\Exception * @throws Exception */ - public function action(Message $message, callable $getProjectDB): void + public function action(Message $message, callable $getProjectDB, UsageDump $queueForUsageDump): void { $payload = $message->getPayload() ?? []; if (empty($payload)) { throw new Exception('Missing payload'); } + //Todo Figure out way to preserve keys when the container is being recreated @shimonewman - $payload = $message->getPayload() ?? []; + $aggregationInterval = (int) App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '20'); $project = new Document($payload['project'] ?? []); $projectId = $project->getInternalId(); foreach ($payload['reduce'] ?? [] as $document) { @@ -70,13 +74,31 @@ class Usage extends Action ); } - self::$stats[$projectId]['project'] = $project; + $this->stats[$projectId]['project'] = $project; foreach ($payload['metrics'] ?? [] as $metric) { - if (!isset(self::$stats[$projectId]['keys'][$metric['key']])) { - self::$stats[$projectId]['keys'][$metric['key']] = $metric['value']; + $this->keys++; + if (!isset($this->stats[$projectId]['keys'][$metric['key']])) { + $this->stats[$projectId]['keys'][$metric['key']] = $metric['value']; continue; } - self::$stats[$projectId]['keys'][$metric['key']] += $metric['value']; + + $this->stats[$projectId]['keys'][$metric['key']] += $metric['value']; + } + + // If keys crossed threshold or X time passed since the last send and there are some keys in the array ($this->stats) + if ( + $this->keys >= self::KEYS_THRESHOLD || + (time() - $this->lastTriggeredTime > $aggregationInterval && $this->keys > 0) + ) { + Console::warning('[' . DateTime::now() . '] Aggregated ' . $this->keys . ' keys'); + + $queueForUsageDump + ->setStats($this->stats) + ->trigger(); + + $this->stats = []; + $this->keys = 0; + $this->lastTriggeredTime = time(); } } @@ -91,7 +113,6 @@ class Usage extends Action */ private function reduce(Document $project, Document $document, array &$metrics, callable $getProjectDB): void { - $dbForProject = $getProjectDB($project); try { diff --git a/src/Appwrite/Platform/Workers/UsageDump.php b/src/Appwrite/Platform/Workers/UsageDump.php new file mode 100644 index 0000000000..bc9deda756 --- /dev/null +++ b/src/Appwrite/Platform/Workers/UsageDump.php @@ -0,0 +1,110 @@ + 'Y-m-d H:00', + '1d' => 'Y-m-d 00:00', + 'inf' => '0000-00-00 00:00' + ]; + + public static function getName(): string + { + return 'usage-dump'; + } + + /** + * @throws \Exception + */ + public function __construct() + { + $this + ->inject('message') + ->inject('getProjectDB') + ->callback(function (Message $message, callable $getProjectDB) { + $this->action($message, $getProjectDB); + }); + } + + /** + * @param Message $message + * @param callable $getProjectDB + * @return void + * @throws Exception + * @throws \Utopia\Database\Exception + */ + public function action(Message $message, callable $getProjectDB): void + { + $payload = $message->getPayload() ?? []; + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + // TODO: rename both usage workers @shimonewman + foreach ($payload['stats'] ?? [] as $stats) { + $project = new Document($stats['project'] ?? []); + $numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0; + + if ($numberOfKeys === 0) { + continue; + } + + console::log('[' . DateTime::now() . '] ProjectId [' . $project->getInternalId() . '] Database [' . $project['database'] . '] ' . $numberOfKeys . ' keys'); + + try { + $dbForProject = $getProjectDB($project); + foreach ($stats['keys'] ?? [] as $key => $value) { + if ($value == 0) { + continue; + } + + foreach ($this->periods as $period => $format) { + $time = 'inf' === $period ? null : date($format, time()); + $id = \md5("{$time}_{$period}_{$key}"); + + try { + $dbForProject->createDocument('stats', new Document([ + '$id' => $id, + 'period' => $period, + 'time' => $time, + 'metric' => $key, + 'value' => $value, + 'region' => App::getEnv('_APP_REGION', 'default'), + ])); + } catch (Duplicate $th) { + if ($value < 0) { + $dbForProject->decreaseDocumentAttribute( + 'stats', + $id, + 'value', + abs($value) + ); + } else { + $dbForProject->increaseDocumentAttribute( + 'stats', + $id, + 'value', + $value + ); + } + } + } + } + } catch (\Exception $e) { + console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); + } + } + } +} diff --git a/src/Appwrite/Platform/Workers/UsageHook.php b/src/Appwrite/Platform/Workers/UsageHook.php deleted file mode 100644 index 81729812e3..0000000000 --- a/src/Appwrite/Platform/Workers/UsageHook.php +++ /dev/null @@ -1,103 +0,0 @@ -setType(Action::TYPE_WORKER_START) - ->inject('register') - ->inject('getProjectDB') - ->callback(function ($register, callable $getProjectDB) { - $this->action($register, $getProjectDB); - }) - ; - } - - /** - * @param $register - * @param $getProjectDB - * @return void - */ - public function action($register, $getProjectDB): void - { - - $interval = (int) App::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '60000'); - Timer::tick($interval, function () use ($register, $getProjectDB) { - - $offset = count(self::$stats); - $projects = array_slice(self::$stats, 0, $offset, true); - array_splice(self::$stats, 0, $offset); - foreach ($projects as $data) { - $numberOfKeys = !empty($data['keys']) ? count($data['keys']) : 0; - $projectInternalId = $data['project']->getInternalId(); - $database = $data['project']['database'] ?? ''; - - console::warning('Ticker started ' . DateTime::now()); - - if ($numberOfKeys === 0) { - continue; - } - - try { - $dbForProject = $getProjectDB($data['project']); - foreach ($data['keys'] ?? [] as $key => $value) { - if ($value == 0) { - continue; - } - - foreach ($this->periods as $period => $format) { - $time = 'inf' === $period ? null : date($format, time()); - $id = \md5("{$time}_{$period}_{$key}"); - - try { - $dbForProject->createDocument('stats', new Document([ - '$id' => $id, - 'period' => $period, - 'time' => $time, - 'metric' => $key, - 'value' => $value, - 'region' => App::getEnv('_APP_REGION', 'default'), - ])); - } catch (Duplicate $th) { - if ($value < 0) { - $dbForProject->decreaseDocumentAttribute( - 'stats', - $id, - 'value', - abs($value) - ); - } else { - $dbForProject->increaseDocumentAttribute( - 'stats', - $id, - 'value', - $value - ); - } - } - } - } - } catch (\Throwable $e) { - console::error(DateTime::now() . ' ' . $projectInternalId . ' ' . $e->getMessage()); - } - } - }); - } -} diff --git a/src/Appwrite/Specification/Format.php b/src/Appwrite/Specification/Format.php index 9ddcbccdf6..c58d69fcb9 100644 --- a/src/Appwrite/Specification/Format.php +++ b/src/Appwrite/Specification/Format.php @@ -208,10 +208,10 @@ abstract class Format return 'MessagingUsageRange'; } break; - case 'createSMS': + case 'createSms': case 'createPush': case 'createEmail': - case 'updateSMS': + case 'updateSms': case 'updatePush': case 'updateEmail': switch ($param) { @@ -255,9 +255,9 @@ abstract class Format case 'deleteSmsTemplate': switch ($param) { case 'type': - return 'SMSTemplateType'; + return 'SmsTemplateType'; case 'locale': - return 'SMSTemplateLocale'; + return 'SmsTemplateLocale'; } break; case 'createPlatform': @@ -288,7 +288,7 @@ abstract class Format case 'updateServiceStatus': switch ($param) { case 'service': - return 'APIService'; + return 'ApiService'; } break; } diff --git a/src/Appwrite/Specification/Format/OpenAPI3.php b/src/Appwrite/Specification/Format/OpenAPI3.php index 6672631ff3..4bbfce17c8 100644 --- a/src/Appwrite/Specification/Format/OpenAPI3.php +++ b/src/Appwrite/Specification/Format/OpenAPI3.php @@ -302,7 +302,7 @@ class OpenAPI3 extends Format case 'Utopia\Database\Validator\UID': case 'Utopia\Validator\Text': $node['schema']['type'] = $validator->getType(); - $node['schema']['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']'; + $node['schema']['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; case 'Utopia\Validator\Boolean': $node['schema']['type'] = $validator->getType(); @@ -313,7 +313,7 @@ class OpenAPI3 extends Format $node['schema']['x-upload-id'] = true; } $node['schema']['type'] = $validator->getType(); - $node['schema']['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']'; + $node['schema']['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; case 'Utopia\Database\Validator\DatetimeValidator': $node['schema']['type'] = $validator->getType(); @@ -451,6 +451,10 @@ class OpenAPI3 extends Format $node['format'] = 'int32'; } break; + case 'Appwrite\Utopia\Database\Validator\CompoundUID': + $node['schema']['type'] = $validator->getType(); + $node['schema']['x-example'] = '[ID1:ID2]'; + break; default: $node['schema']['type'] = 'string'; break; diff --git a/src/Appwrite/Specification/Format/Swagger2.php b/src/Appwrite/Specification/Format/Swagger2.php index 20aeb96222..6d0b94cba8 100644 --- a/src/Appwrite/Specification/Format/Swagger2.php +++ b/src/Appwrite/Specification/Format/Swagger2.php @@ -298,7 +298,7 @@ class Swagger2 extends Format case 'Utopia\Validator\Text': case 'Utopia\Database\Validator\UID': $node['type'] = $validator->getType(); - $node['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']'; + $node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; case 'Utopia\Validator\Boolean': $node['type'] = $validator->getType(); @@ -309,7 +309,7 @@ class Swagger2 extends Format $node['x-upload-id'] = true; } $node['type'] = $validator->getType(); - $node['x-example'] = '[' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . ']'; + $node['x-example'] = '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>'; break; case 'Utopia\Database\Validator\DatetimeValidator': $node['type'] = $validator->getType(); @@ -446,6 +446,10 @@ class Swagger2 extends Format $node['format'] = 'int32'; } break; + case 'Appwrite\Utopia\Database\Validator\CompoundUID': + $node['type'] = $validator->getType(); + $node['x-example'] = '[ID1:ID2]'; + break; default: $node['type'] = 'string'; break; diff --git a/src/Appwrite/Utopia/Database/Validator/CompoundUID.php b/src/Appwrite/Utopia/Database/Validator/CompoundUID.php new file mode 100644 index 0000000000..3f23500952 --- /dev/null +++ b/src/Appwrite/Utopia/Database/Validator/CompoundUID.php @@ -0,0 +1,58 @@ +isValid($id)) { + return false; + } + } + + return true; + } + + public function getType(): string + { + return self::TYPE_STRING; + } + + public static function parse(string $key): array + { + $parts = \explode(':', $key); + $result = []; + + foreach ($parts as $part) { + $result[] = $part; + } + + return $result; + } +} diff --git a/tests/e2e/Services/Account/AccountConsoleClientTest.php b/tests/e2e/Services/Account/AccountConsoleClientTest.php index 4aa2462f89..97a3374536 100644 --- a/tests/e2e/Services/Account/AccountConsoleClientTest.php +++ b/tests/e2e/Services/Account/AccountConsoleClientTest.php @@ -5,10 +5,87 @@ namespace Tests\E2E\Services\Account; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\ProjectConsole; use Tests\E2E\Scopes\SideClient; +use Tests\E2E\Client; +use Utopia\Database\Helpers\ID; class AccountConsoleClientTest extends Scope { use AccountBase; use ProjectConsole; use SideClient; + + public function testDeleteAccount(): void + { + $email = uniqid() . 'user@localhost.test'; + $password = 'password'; + $name = 'User Name'; + + $response = $this->client->call(Client::METHOD_POST, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'userId' => ID::unique(), + 'email' => $email, + 'password' => $password, + 'name' => $name, + ]); + + $this->assertEquals($response['headers']['status-code'], 201); + + $response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'email' => $email, + 'password' => $password, + ]); + + $this->assertEquals($response['headers']['status-code'], 201); + + $session = $response['cookies']['a_session_' . $this->getProject()['$id']]; + + // create team + $team = $this->client->call(Client::METHOD_POST, '/teams', [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ], [ + 'teamId' => 'unique()', + 'name' => 'myteam' + ]); + $this->assertEquals($team['headers']['status-code'], 201); + + $teamId = $team['body']['$id']; + + $response = $this->client->call(Client::METHOD_DELETE, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ])); + + $this->assertEquals($response['headers']['status-code'], 400); + + // DELETE TEAM + $response = $this->client->call(Client::METHOD_DELETE, '/teams/' . $teamId, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ])); + $this->assertEquals($response['headers']['status-code'], 204); + sleep(2); + + $response = $this->client->call(Client::METHOD_DELETE, '/account', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ])); + + $this->assertEquals($response['headers']['status-code'], 204); + } } diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php index 1169686ada..ba525f556e 100644 --- a/tests/e2e/Services/Account/AccountCustomClientTest.php +++ b/tests/e2e/Services/Account/AccountCustomClientTest.php @@ -1918,7 +1918,7 @@ class AccountCustomClientTest extends Scope $this->assertEquals($response['body']['users'][0]['email'], $email); } - + #[Retry(count: 2)] public function testCreatePhone(): array { $number = '+123456789'; @@ -1941,22 +1941,8 @@ class AccountCustomClientTest extends Scope $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire'])); $userId = $response['body']['userId']; - $messageId = $response['body']['$id']; - /** - * Test for FAILURE - */ - $response = $this->client->call(Client::METHOD_POST, '/account/tokens/phone', array_merge([ - 'origin' => 'http://localhost', - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ]), [ - 'userId' => ID::unique() - ]); - - $this->assertEquals(400, $response['headers']['status-code']); - - \sleep(5); + \sleep(7); $smsRequest = $this->getLastRequest(); @@ -1972,6 +1958,19 @@ class AccountCustomClientTest extends Scope $data['id'] = $userId; $data['number'] = $number; + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_POST, '/account/tokens/phone', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ]), [ + 'userId' => ID::unique() + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + return $data; } diff --git a/tests/e2e/Services/GraphQL/Base.php b/tests/e2e/Services/GraphQL/Base.php index d4f290c3db..01153cdd04 100644 --- a/tests/e2e/Services/GraphQL/Base.php +++ b/tests/e2e/Services/GraphQL/Base.php @@ -1872,8 +1872,8 @@ trait Base } }'; case self::$CREATE_FCM_PROVIDER: - return 'mutation createFCMProvider($providerId: String!, $name: String!, $serviceAccountJSON: Json) { - messagingCreateFCMProvider(providerId: $providerId, name: $name, serviceAccountJSON: $serviceAccountJSON) { + return 'mutation createFcmProvider($providerId: String!, $name: String!, $serviceAccountJSON: Json) { + messagingCreateFcmProvider(providerId: $providerId, name: $name, serviceAccountJSON: $serviceAccountJSON) { _id name provider @@ -1882,8 +1882,8 @@ trait Base } }'; case self::$CREATE_APNS_PROVIDER: - return 'mutation createAPNSProvider($providerId: String!, $name: String!, $authKey: String!, $authKeyId: String!, $teamId: String!, $bundleId: String!) { - messagingCreateAPNSProvider(providerId: $providerId, name: $name, authKey: $authKey, authKeyId: $authKeyId, teamId: $teamId, bundleId: $bundleId) { + return 'mutation createApnsProvider($providerId: String!, $name: String!, $authKey: String!, $authKeyId: String!, $teamId: String!, $bundleId: String!) { + messagingCreateApnsProvider(providerId: $providerId, name: $name, authKey: $authKey, authKeyId: $authKeyId, teamId: $teamId, bundleId: $bundleId) { _id name provider @@ -1996,8 +1996,8 @@ trait Base } }'; case self::$UPDATE_FCM_PROVIDER: - return 'mutation updateFCMProvider($providerId: String!, $name: String!, $serviceAccountJSON: Json) { - messagingUpdateFCMProvider(providerId: $providerId, name: $name, serviceAccountJSON: $serviceAccountJSON) { + return 'mutation updateFcmProvider($providerId: String!, $name: String!, $serviceAccountJSON: Json) { + messagingUpdateFcmProvider(providerId: $providerId, name: $name, serviceAccountJSON: $serviceAccountJSON) { _id name provider @@ -2006,8 +2006,8 @@ trait Base } }'; case self::$UPDATE_APNS_PROVIDER: - return 'mutation updateAPNSProvider($providerId: String!, $name: String!, $authKey: String!, $authKeyId: String!, $teamId: String!, $bundleId: String!) { - messagingUpdateAPNSProvider(providerId: $providerId, name: $name, authKey: $authKey, authKeyId: $authKeyId, teamId: $teamId, bundleId: $bundleId) { + return 'mutation updateApnsProvider($providerId: String!, $name: String!, $authKey: String!, $authKeyId: String!, $teamId: String!, $bundleId: String!) { + messagingUpdateApnsProvider(providerId: $providerId, name: $name, authKey: $authKey, authKeyId: $authKeyId, teamId: $teamId, bundleId: $bundleId) { _id name provider diff --git a/tests/e2e/Services/GraphQL/MessagingTest.php b/tests/e2e/Services/GraphQL/MessagingTest.php index 18fcf71f0c..f5e92b9113 100644 --- a/tests/e2e/Services/GraphQL/MessagingTest.php +++ b/tests/e2e/Services/GraphQL/MessagingTest.php @@ -70,7 +70,7 @@ class MessagingTest extends Scope 'apiSecret' => 'my-apisecret', 'from' => '+123456789', ], - 'FCM' => [ + 'Fcm' => [ 'providerId' => ID::unique(), 'name' => 'FCM1', 'serviceAccountJSON' => [ @@ -80,7 +80,7 @@ class MessagingTest extends Scope "private_key" => "test-private-key", ] ], - 'APNS' => [ + 'Apns' => [ 'providerId' => ID::unique(), 'name' => 'APNS1', 'authKey' => 'my-authkey', @@ -159,7 +159,7 @@ class MessagingTest extends Scope 'apiKey' => 'my-apikey', 'apiSecret' => 'my-apisecret', ], - 'FCM' => [ + 'Fcm' => [ 'providerId' => $providers[7]['_id'], 'name' => 'FCM2', 'serviceAccountJSON' => [ @@ -169,7 +169,7 @@ class MessagingTest extends Scope 'private_key' => "test-private-key", ] ], - 'APNS' => [ + 'Apns' => [ 'providerId' => $providers[8]['_id'], 'name' => 'APNS2', 'authKey' => 'my-authkey', @@ -180,15 +180,18 @@ class MessagingTest extends Scope ]; foreach (\array_keys($providersParams) as $index => $key) { $query = $this->getQuery('update_' . \strtolower($key) . '_provider'); + $graphQLPayload = [ 'query' => $query, 'variables' => $providersParams[$key], ]; + $response = $this->client->call(Client::METHOD_POST, '/graphql', [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], 'x-appwrite-key' => $this->getProject()['apiKey'], ], $graphQLPayload); + $providers[$index] = $response['body']['data']['messagingUpdate' . $key . 'Provider']; $this->assertEquals(200, $response['headers']['status-code']); $this->assertEquals($providersParams[$key]['name'], $response['body']['data']['messagingUpdate' . $key . 'Provider']['name']); @@ -991,7 +994,7 @@ class MessagingTest extends Scope $this->assertEquals(200, $provider['headers']['status-code']); - $providerId = $provider['body']['data']['messagingCreateFCMProvider']['_id']; + $providerId = $provider['body']['data']['messagingCreateFcmProvider']['_id']; $query = $this->getQuery(self::$CREATE_TOPIC); $graphQLPayload = [ diff --git a/tests/e2e/Services/Messaging/MessagingBase.php b/tests/e2e/Services/Messaging/MessagingBase.php index 956ac5f68a..25bf985692 100644 --- a/tests/e2e/Services/Messaging/MessagingBase.php +++ b/tests/e2e/Services/Messaging/MessagingBase.php @@ -444,6 +444,17 @@ trait MessagingBase $this->assertEquals($target['body']['userId'], $response['body']['target']['userId']); $this->assertEquals($target['body']['providerType'], $response['body']['target']['providerType']); + // Test duplicate subscribers not allowed + $failure = $this->client->call(Client::METHOD_POST, '/messaging/topics/' . $topics['public']['$id'] . '/subscribers', \array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'subscriberId' => ID::unique(), + 'targetId' => $target['body']['$id'], + ]); + + $this->assertEquals(409, $failure['headers']['status-code']); + $topic = $this->client->call(Client::METHOD_GET, '/messaging/topics/' . $topics['public']['$id'], [ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], diff --git a/tests/unit/Auth/Validator/PhoneTest.php b/tests/unit/Auth/Validator/PhoneTest.php index 7bfa8db477..d5a4e7f826 100644 --- a/tests/unit/Auth/Validator/PhoneTest.php +++ b/tests/unit/Auth/Validator/PhoneTest.php @@ -20,13 +20,20 @@ class PhoneTest extends TestCase $this->assertEquals($this->object->isValid(null), false); $this->assertEquals($this->object->isValid(''), false); $this->assertEquals($this->object->isValid('+1'), false); + $this->assertEquals($this->object->isValid('+14'), false); + $this->assertEquals($this->object->isValid('+141'), false); + $this->assertEquals($this->object->isValid('+1415'), false); + $this->assertEquals($this->object->isValid('+14155'), false); + $this->assertEquals($this->object->isValid('+141555'), false); $this->assertEquals($this->object->isValid('8989829304'), false); $this->assertEquals($this->object->isValid('786-307-3615'), false); $this->assertEquals($this->object->isValid('+16308A520397'), false); $this->assertEquals($this->object->isValid('+0415553452342'), false); $this->assertEquals($this->object->isValid('+14 155 5524564'), false); + $this->assertEquals($this->object->isValid('+1415555245634543'), false); $this->assertEquals($this->object->isValid(+14155552456), false); + $this->assertEquals($this->object->isValid('+1415555'), true); $this->assertEquals($this->object->isValid('+14155552'), true); $this->assertEquals($this->object->isValid('+141555526'), true); $this->assertEquals($this->object->isValid('+16308520394'), true); diff --git a/tests/unit/Utopia/Database/Validator/CompoundUIDTest.php b/tests/unit/Utopia/Database/Validator/CompoundUIDTest.php new file mode 100644 index 0000000000..b443cf590b --- /dev/null +++ b/tests/unit/Utopia/Database/Validator/CompoundUIDTest.php @@ -0,0 +1,37 @@ +object = new CompoundUID(); + } + + public function tearDown(): void + { + } + + public function testValues(): void + { + $this->assertEquals($this->object->isValid('123:456'), true); + $this->assertEquals($this->object->isValid('123'), false); + $this->assertEquals($this->object->isValid('123:_456'), false); + $this->assertEquals($this->object->isValid('dasda asdasd'), false); + $this->assertEquals($this->object->isValid('dasda:asdasd'), true); + $this->assertEquals($this->object->isValid('_asdas:dasdas'), false); + $this->assertEquals($this->object->isValid('as$$5da:sdasdas'), false); + $this->assertEquals($this->object->isValid(false), false); + $this->assertEquals($this->object->isValid(null), false); + $this->assertEquals($this->object->isValid('socialAccountForYoutubeAndRestSubscribers:12345'), false); + $this->assertEquals($this->object->isValid('socialAccountForYoutubeAndRSubscriber:12345'), false); + $this->assertEquals($this->object->isValid('socialAccount:ForYoutubeSubscribe'), true); + $this->assertEquals($this->object->isValid('socialAccountForYoutubeSubscribe:socialAccountForYoutubeSubscribe'), true); + } +}